Preserve column before/after comments through .transform()

Refs #762

The before comment comes before the column definition - the after
comment is anything after it but before its trailing comma.
This commit is contained in:
Simon Willison 2026-08-11 22:37:13 -07:00
commit b37b8cf8c8
6 changed files with 171 additions and 18 deletions

View file

@ -4,7 +4,13 @@ import hypothesis.strategies as st
import pytest
from hypothesis import given
from sqlite_utils.create_table_parser import Check, ParseError, parse_checks
from sqlite_utils.create_table_parser import (
Check,
ColumnComments,
ParseError,
parse_checks,
parse_column_comments,
)
def test_parse_column_and_table_checks():
@ -46,6 +52,26 @@ def test_comments_are_trivia_not_constraints():
]
def test_parse_comments_owned_by_columns():
sql = """
CREATE TABLE t (
-- Before id
id /* Between name and type */ INTEGER /* After id */,
/* Between column definitions */
value TEXT CHECK(value != '') /* After value */,
/* Before a table constraint, not a column */
CHECK(value != 'forbidden')
)
"""
assert parse_column_comments(sql) == {
"id": ColumnComments(before="-- Before id", after="/* After id */"),
"value": ColumnComments(
before="/* Between column definitions */",
after="/* After value */",
),
}
@pytest.mark.parametrize(
"expression,expected",
[

View file

@ -1102,6 +1102,54 @@ def test_transform_preserves_check_ending_in_line_comment(fresh_db):
inventory.insert({"quantity": -1})
def test_transform_preserves_comments_owned_by_columns(fresh_db):
fresh_db.execute("""
CREATE TABLE people (
-- Primary identifier
id INTEGER PRIMARY KEY /* IDs are stable */,
/* Displayed to users */
name TEXT /* May contain spaces */,
-- Age in years
age INTEGER -- May be NULL
)
""")
people = fresh_db["people"]
people.insert({"id": 1, "name": "Cleo", "age": 5})
people.transform(
rename={"name": "display_name"},
types={"age": float},
column_order=("age", "id", "name"),
)
assert people.get(1) == {"age": 5.0, "id": 1, "display_name": "Cleo"}
schema = people.schema
assert schema.index("-- Age in years") < schema.index('"age" REAL')
assert schema.index('"age" REAL') < schema.index("-- May be NULL")
assert schema.index("-- Primary identifier") < schema.index('"id" INTEGER')
assert schema.index('"id" INTEGER') < schema.index("/* IDs are stable */")
assert schema.index("/* Displayed to users */") < schema.index(
'"display_name" TEXT'
)
assert schema.index('"display_name" TEXT') < schema.index(
"/* May contain spaces */"
)
def test_transform_drops_comments_owned_by_dropped_column(fresh_db):
fresh_db.execute("""
CREATE TABLE t (
/* Keep this explanation */
id INTEGER,
/* Drop this explanation */
obsolete TEXT /* Drop this too */
)
""")
fresh_db["t"].transform(drop={"obsolete"})
schema = fresh_db["t"].schema
assert "Keep this explanation" in schema
assert "Drop this explanation" not in schema
assert "Drop this too" not in schema
def test_transform_renames_columns_inside_check_constraints(fresh_db):
fresh_db.execute("""
CREATE TABLE inventory (