Skip to content

Commit

Permalink
Fixed #35778 -- Use native JSON_OBJECT on postgres 16+ with server-si…
Browse files Browse the repository at this point in the history
…de binding.
  • Loading branch information
john-parton committed Oct 16, 2024
1 parent 438fc42 commit 5075141
Showing 1 changed file with 27 additions and 18 deletions.
45 changes: 27 additions & 18 deletions django/db/models/functions/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ def as_native(self, compiler, connection, *, returning, **extra_context):
class ArgJoiner:
def join(self, args):
pairs = zip(args[::2], args[1::2], strict=True)
return ", ".join([" VALUE ".join(pair) for pair in pairs])
# 'key' is wrapped in parentheses in case it's been cast using
# the postgres :: shortcut syntax.
return ", ".join(f"({key}) VALUE {value}" for key, value in pairs)

return self.as_sql(
compiler,
Expand All @@ -175,24 +177,31 @@ def join(self, args):
)

def as_postgresql(self, compiler, connection, **extra_context):
if (
not connection.features.is_postgresql_16
or connection.features.uses_server_side_binding
):
copy = self.copy()
copy.set_source_expressions(
[
Cast(expression, TextField()) if index % 2 == 0 else expression
for index, expression in enumerate(copy.get_source_expressions())
]
)
return super(JSONObject, copy).as_sql(
compiler,
connection,
function="JSONB_BUILD_OBJECT",
**extra_context,
# It is required to cast keys to text while using JSONB_BUILD_OBJECT
# and also while using JSON_OBJECT in PostgreSQL 16+ with server-side
# binding.
# It is not technically required to cast keys to text while using
# JSON_OBJECT in PostgreSQL 16+ without server-side binding, but it is
# done for consistency.
copy = self.copy()
copy.set_source_expressions(
[
Cast(expression, TextField()) if index % 2 == 0 else expression
for index, expression in enumerate(copy.get_source_expressions())
]
)

if connection.features.is_postgresql_16:
return copy.as_native(
compiler, connection, returning="JSONB", **extra_context
)
return self.as_native(compiler, connection, returning="JSONB", **extra_context)

return super(JSONObject, copy).as_sql(
compiler,
connection,
function="JSONB_BUILD_OBJECT",
**extra_context,
)

def as_oracle(self, compiler, connection, **extra_context):
return self.as_native(compiler, connection, returning="CLOB", **extra_context)
Expand Down

0 comments on commit 5075141

Please sign in to comment.