From d7c6df6b463cdf00e413dbf552d4a46f4eb36c84 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 14:38:23 +0000 Subject: [PATCH 1/2] Fix _decode_default_value to unescape doubled single quotes in string defaults SQLite stores string defaults with single quotes doubled (e.g. DEFAULT 'O''Brien' is stored as the literal "'O''Brien'" in sqlite_master). The previous code stripped the outer quotes with value[1:-1] but never converted '' back to ', so default_values returned the raw escaped form instead of the true string value. --- sqlite_utils/db.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index e97b7d9cb..515e5e22f 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -5212,8 +5212,8 @@ def resolve_extracts( def _decode_default_value(value: str) -> object: if value.startswith("'") and value.endswith("'"): - # It's a string - return value[1:-1] + # It's a string; unescape doubled single quotes + return value[1:-1].replace("''", "'") if value.isdigit(): # It's an integer return int(value) From e7f567d764d9dbf45c815138033c4312b242b581 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sat, 25 Jul 2026 21:45:18 -0700 Subject: [PATCH 2/2] Test for doubled single quotes in string defaults Refs #811 Co-Authored-By: Claude Opus 5 (1M context) --- tests/test_introspect.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_introspect.py b/tests/test_introspect.py index 8b6765d86..4708b29d5 100644 --- a/tests/test_introspect.py +++ b/tests/test_introspect.py @@ -311,6 +311,7 @@ def test_table_strict(fresh_db, create_table, expected_strict): 1, 1.3, "foo", + "O'Brien", True, b"binary", ), @@ -323,6 +324,16 @@ def test_table_default_values(fresh_db, value): assert default_values == {"value": value} +def test_table_default_values_escaped_quotes(fresh_db): + # SQLite stores string defaults with single quotes doubled, so + # introspection needs to unescape them again + fresh_db.execute( + "create table t (id integer primary key, name text default 'O''Brien')" + ) + assert "default 'O''Brien'" in fresh_db["t"].schema + assert fresh_db["t"].default_values == {"name": "O'Brien"} + + def test_pks_use_primary_key_declaration_order(fresh_db): # PRIMARY KEY (a, b) declared against columns stored in order (b, a) - # pks must follow the declaration order, which is what SQLite uses to