--- /dev/null
+.. change::
+ :tags: bug, postgresql
+ :tickets: 12778
+
+ Fixed regression in PostgreSQL dialect where JSONB subscription syntax
+ would generate incorrect SQL for JSONB-returning functions, causing syntax
+ errors. The dialect now properly wraps function calls and expressions in
+ parentheses when using the ``[]`` subscription syntax, generating
+ ``(function_call)[index]`` instead of ``function_call[index]`` to comply
+ with PostgreSQL syntax requirements.
>>> stmt = select(function_expr["def"])
>>> print(stmt)
- {printsql}SELECT json_object(:json_object_1)[:json_object_2] AS anon_1
+ {printsql}SELECT (json_object(:json_object_1))[:json_object_2] AS anon_1
Built-in Functions Have Pre-Configured Return Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# expressions against getitem. This may need to be made
# more portable if in the future we support other DBs
# besides postgresql.
- if against is operators.getitem and isinstance(
- self.type, sqltypes.ARRAY
- ):
+ if against in (operators.getitem, operators.json_getitem_op):
return Grouping(self)
else:
return super().self_group(against=against)
"UPDATE data SET x -> %(x_1)s=(data.x -> %(x_2)s)",
)
+ def test_jsonb_functions_use_parentheses_with_subscripting(self):
+ """test #12778 - JSONB functions are parenthesized with [] syntax"""
+ data = table("data", column("id", Integer), column("x", JSONB))
+
+ # Test that JSONB functions are properly parenthesized with [] syntax
+ # This ensures correct PostgreSQL syntax: (function_call)[index]
+ # instead of the invalid: function_call[index]
+
+ stmt = select(func.jsonb_array_elements(data.c.x, type_=JSONB)["key"])
+ self.assert_compile(
+ stmt,
+ "SELECT "
+ "(jsonb_array_elements(data.x))[%(jsonb_array_elements_1)s] "
+ "AS anon_1 FROM data",
+ )
+
+ # Test with nested function calls
+ stmt = select(
+ func.jsonb_array_elements(data.c.x["items"], type_=JSONB)["key"]
+ )
+ self.assert_compile(
+ stmt,
+ "SELECT (jsonb_array_elements(data.x[%(x_1)s]))"
+ "[%(jsonb_array_elements_1)s] AS anon_1 FROM data",
+ )
+
def test_range_custom_object_hook(self):
# See issue #8884
from datetime import date