escape_sqlite() favors double quotes, closes #2795

This commit is contained in:
Simon Willison 2026-06-23 14:04:20 -07:00
commit 22ccd8a087
2 changed files with 24 additions and 11 deletions

View file

@ -410,12 +410,7 @@ def escape_css_string(s):
def escape_sqlite(s):
if _boring_keyword_re.match(s) and (s.lower() not in reserved_words):
return s
elif "]" in s:
# SQLite does not support escaping ] inside [bracket] quoting, so fall
# back to double-quote quoting (doubling any embedded ") - #2677
return '"{}"'.format(s.replace('"', '""'))
else:
return f"[{s}]"
return '"{}"'.format(s.replace('"', '""'))
def make_dockerfile(

View file

@ -221,11 +221,10 @@ def test_detect_fts(open_quote, close_quote):
"identifier,expected",
(
("plain", "plain"),
("select", "[select]"),
("has space", "[has space]"),
("has'quote", "[has'quote]"),
# Identifiers containing ] must fall back to double-quote quoting
# (SQLite does not support escaping ] inside [brackets]) - #2677
("select", '"select"'),
("has space", '"has space"'),
("has'quote", '"has\'quote"'),
('has"dquote', '"has""dquote"'),
("has]bracket", '"has]bracket"'),
('has"dquote]', '"has""dquote]"'),
),
@ -234,6 +233,25 @@ def test_escape_sqlite(identifier, expected):
assert utils.escape_sqlite(identifier) == expected
def test_escape_sqlite_double_quotes_work_in_query():
conn = utils.sqlite3.connect(":memory:")
table = 'table with "double quotes"'
column = "select"
escaped_table = utils.escape_sqlite(table)
escaped_column = utils.escape_sqlite(column)
conn.execute(f"CREATE TABLE {escaped_table} ({escaped_column} TEXT)")
create_sql = conn.execute(
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?", (table,)
).fetchone()[0]
assert create_sql == 'CREATE TABLE "table with ""double quotes""" ("select" TEXT)'
conn.execute(
f"INSERT INTO {escaped_table} ({escaped_column}) VALUES (?)", ("hello",)
)
results = conn.execute(f"SELECT {escaped_column} FROM {escaped_table}").fetchall()
conn.close()
assert results == [("hello",)]
def test_escape_sqlite_prevents_injection():
# https://github.com/simonw/datasette/issues/2677
conn = utils.sqlite3.connect(":memory:")