Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sqlite_utils/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,11 @@ def quote_default_value(self, value: str) -> str:
if str(value).upper() in ("CURRENT_TIME", "CURRENT_DATE", "CURRENT_TIMESTAMP"):
return value

if str(value).upper() in ("TRUE", "FALSE", "NULL"):
# Keyword literals must stay unquoted; quoting them would turn the
# default into a string ('TRUE' instead of 1, 'NULL' instead of null).
return value

if str(value).endswith(")"):
# Expr
return "({})".format(value)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_default_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
# Strings
("TEXT DEFAULT 'CURRENT_TIMESTAMP'", "'CURRENT_TIMESTAMP'", "'CURRENT_TIMESTAMP'"),
('TEXT DEFAULT "CURRENT_TIMESTAMP"', '"CURRENT_TIMESTAMP"', '"CURRENT_TIMESTAMP"'),
# Boolean and null keyword literals must stay unquoted
("INTEGER DEFAULT TRUE", "TRUE", "TRUE"),
("INTEGER DEFAULT FALSE", "FALSE", "FALSE"),
("INTEGER DEFAULT true", "true", "true"),
("TEXT DEFAULT NULL", "NULL", "NULL"),
]


Expand Down
34 changes: 34 additions & 0 deletions tests/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,40 @@ def test_transform_rename_pk(fresh_db):
)


def test_transform_preserves_keyword_literal_defaults(fresh_db):
# transform() used to requote keyword-literal defaults (DEFAULT TRUE became
# DEFAULT 'TRUE'), so a default insert stored the text 'TRUE' instead of the
# integer 1 -- silent value corruption on every rebuilt table.
fresh_db.execute(
"CREATE TABLE t ("
" id INTEGER PRIMARY KEY,"
" is_active INTEGER DEFAULT TRUE,"
" flag INTEGER DEFAULT FALSE,"
" note TEXT DEFAULT NULL"
")"
)
table = fresh_db["t"]
table.insert({"id": 1})
before = fresh_db.execute("SELECT is_active, flag, note FROM t").fetchone()
assert before == (1, 0, None)

# Rebuild the table via an unrelated change.
table.transform(rename={"note": "note2"})

# The keyword literals stay unquoted in the schema ...
assert "DEFAULT TRUE" in table.schema
assert "DEFAULT FALSE" in table.schema
assert "DEFAULT NULL" in table.schema
assert "'TRUE'" not in table.schema

# ... and a fresh default insert still yields 1 / 0 / NULL, not strings.
table.insert({"id": 2})
after = fresh_db.execute(
"SELECT is_active, flag, note2 FROM t WHERE id = 2"
).fetchone()
assert after == (1, 0, None)


def test_transform_not_null(fresh_db):
dogs = fresh_db["dogs"]
dogs.insert({"id": 1, "name": "Cleo", "age": "5"}, pk="id")
Expand Down
Loading