diff --git a/datasette/filters.py b/datasette/filters.py index 95cc5f3748..82e564317d 100644 --- a/datasette/filters.py +++ b/datasette/filters.py @@ -209,7 +209,16 @@ def where_clause(self, table, column, value, param_counter): kwargs = {"c": column} converted = None else: - kwargs = {"c": column, "p": f"p{param_counter}", "t": table} + kwargs = { + "c": column, + "p": f"p{param_counter}", + "t": table, + # Properly quoted identifiers for templates that reference the + # table/column directly (e.g. json_each()). Bracket quoting + # cannot escape a "]" in a name, so use escape_sqlite() instead. + "c_escaped": escape_sqlite(column), + "t_escaped": escape_sqlite(table) if table is not None else "", + } return self.sql_template.format(**kwargs), converted def human_clause(self, column, value): @@ -322,13 +331,13 @@ class Filters: TemplatedFilter( "arraycontains", "array contains", - """:{p} in (select value from json_each([{t}].[{c}]))""", + """:{p} in (select value from json_each({t_escaped}.{c_escaped}))""", '{c} contains "{v}"', ), TemplatedFilter( "arraynotcontains", "array does not contain", - """:{p} not in (select value from json_each([{t}].[{c}]))""", + """:{p} not in (select value from json_each({t_escaped}.{c_escaped}))""", '{c} does not contain "{v}"', ), ] diff --git a/tests/test_filters.py b/tests/test_filters.py index eda9e9a17c..dc963a6c64 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -65,12 +65,24 @@ # JSON arraycontains, arraynotcontains ( (("Availability+Info__arraycontains", "yes"),), - [":p0 in (select value from json_each([table].[Availability+Info]))"], + [':p0 in (select value from json_each("table"."Availability+Info"))'], ["yes"], ), ( (("Availability+Info__arraynotcontains", "yes"),), - [":p0 not in (select value from json_each([table].[Availability+Info]))"], + [':p0 not in (select value from json_each("table"."Availability+Info"))'], + ["yes"], + ), + # A column name containing "]" must be escaped with escape_sqlite() - + # bracket quoting would produce invalid SQL, see refs #2431 + ( + (("ta]gs__arraycontains", "yes"),), + [':p0 in (select value from json_each("table"."ta]gs"))'], + ["yes"], + ), + ( + (("ta]gs__arraynotcontains", "yes"),), + [':p0 not in (select value from json_each("table"."ta]gs"))'], ["yes"], ), ],