Skip to content

Commit aef3088

Browse files
author
Nikita Glukhov
committed
Enable RETURNING clause for JSON_EXISTS
1 parent 33c1fd3 commit aef3088

File tree

7 files changed

+146
-16
lines changed

7 files changed

+146
-16
lines changed

doc/src/sgml/func.sgml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17727,6 +17727,7 @@ INSERT INTO my_films VALUES (
1772717727
<refsynopsisdiv>
1772817728
<synopsis>JSON_EXISTS (
1772917729
<replaceable class="parameter">json_api_common_syntax</replaceable>
17730+
[ RETURNING <replaceable class="parameter">data_type</replaceable> ]
1773017731
[ { TRUE | FALSE | UNKNOWN | ERROR } ON ERROR ]
1773117732
)
1773217733
</synopsis>
@@ -17757,6 +17758,19 @@ INSERT INTO my_films VALUES (
1775717758
</listitem>
1775817759
</varlistentry>
1775917760

17761+
<varlistentry>
17762+
<term>
17763+
<literal>RETURNING <replaceable class="parameter">data_type</replaceable></literal>
17764+
</term>
17765+
<listitem>
17766+
<para>
17767+
The output clause that specifies the data type of the returned value.
17768+
The specified data type should have a cast from a <literal>boolean</literal>
17769+
type, which is returned by default.
17770+
</para>
17771+
</listitem>
17772+
</varlistentry>
17773+
1776017774
<varlistentry>
1776117775
<term>
1776217776
<literal>{ TRUE | FALSE | UNKNOWN | ERROR } ON ERROR</literal>

src/backend/executor/execExprInterp.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,7 +4519,8 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
45194519

45204520
if (estate) /* coerce using specified expression */
45214521
return ExecEvalExpr(estate, econtext, isNull);
4522-
else
4522+
4523+
if (op->d.jsonexpr.jsexpr->op != IS_JSON_EXISTS)
45234524
{
45244525
JsonCoercion *coercion = op->d.jsonexpr.jsexpr->result_coercion;
45254526
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
@@ -4855,11 +4856,21 @@ ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
48554856

48564857
case IS_JSON_EXISTS:
48574858
{
4858-
bool res = JsonPathExists(item, path,
4859-
op->d.jsonexpr.args, error);
4859+
bool exists = JsonPathExists(item, path,
4860+
op->d.jsonexpr.args,
4861+
error);
48604862

48614863
*resnull = error && *error;
4862-
return BoolGetDatum(res);
4864+
res = BoolGetDatum(exists);
4865+
4866+
if (!op->d.jsonexpr.result_expr)
4867+
return res;
4868+
4869+
/* coerce using result expression */
4870+
estate = op->d.jsonexpr.result_expr;
4871+
op->d.jsonexpr.res_expr->value = res;
4872+
op->d.jsonexpr.res_expr->isnull = *resnull;
4873+
break;
48634874
}
48644875

48654876
default:
@@ -4909,7 +4920,7 @@ ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr,
49094920
if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
49104921
return false;
49114922

4912-
if (jsexpr->op == IS_JSON_EXISTS)
4923+
if (jsexpr->op == IS_JSON_EXISTS && !jsexpr->result_coercion)
49134924
return false;
49144925

49154926
if (!coercions)
@@ -4980,9 +4991,8 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
49804991
op->d.jsonexpr.default_on_error,
49814992
op->resnull);
49824993

4983-
if (jexpr->op != IS_JSON_EXISTS &&
4984-
/* result is already coerced in DEFAULT behavior case */
4985-
jexpr->on_error->btype != JSON_BEHAVIOR_DEFAULT)
4994+
/* result is already coerced in DEFAULT behavior case */
4995+
if (jexpr->on_error->btype != JSON_BEHAVIOR_DEFAULT)
49864996
res = ExecEvalJsonExprCoercion(op, econtext, res,
49874997
op->resnull,
49884998
NULL, NULL);

src/backend/parser/gram.y

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15061,13 +15061,15 @@ json_output_clause_opt:
1506115061
json_exists_predicate:
1506215062
JSON_EXISTS '('
1506315063
json_api_common_syntax
15064+
json_returning_clause_opt
1506415065
json_exists_error_clause_opt
1506515066
')'
1506615067
{
1506715068
JsonFuncExpr *p = makeNode(JsonFuncExpr);
1506815069
p->op = IS_JSON_EXISTS;
1506915070
p->common = (JsonCommon *) $3;
15070-
p->on_error = $4;
15071+
p->output = (JsonOutput *) $4;
15072+
p->on_error = $5;
1507115073
p->location = @1;
1507215074
$$ = (Node *) p;
1507315075
}

src/backend/parser/parse_expr.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4828,11 +4828,45 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
48284828
case IS_JSON_EXISTS:
48294829
func_name = "JSON_EXISTS";
48304830

4831-
jsexpr->returning = makeNode(JsonReturning);
4832-
jsexpr->returning->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
4833-
jsexpr->returning->typid = BOOLOID;
4834-
jsexpr->returning->typmod = -1;
4831+
jsexpr->returning = transformJsonOutput(pstate, func->output, false);
48354832

4833+
jsexpr->returning->format->format = JS_FORMAT_DEFAULT;
4834+
jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
4835+
4836+
if (!OidIsValid(jsexpr->returning->typid))
4837+
{
4838+
jsexpr->returning->typid = BOOLOID;
4839+
jsexpr->returning->typmod = -1;
4840+
}
4841+
else if (jsexpr->returning->typid != BOOLOID)
4842+
{
4843+
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
4844+
int location = exprLocation((Node *) jsexpr);
4845+
4846+
placeholder->typeId = BOOLOID;
4847+
placeholder->typeMod = -1;
4848+
placeholder->collation = InvalidOid;
4849+
4850+
jsexpr->result_coercion = makeNode(JsonCoercion);
4851+
jsexpr->result_coercion->expr =
4852+
coerce_to_target_type(pstate, (Node *) placeholder, BOOLOID,
4853+
jsexpr->returning->typid,
4854+
jsexpr->returning->typmod,
4855+
COERCION_EXPLICIT,
4856+
COERCE_INTERNAL_CAST,
4857+
location);
4858+
4859+
if (!jsexpr->result_coercion->expr)
4860+
ereport(ERROR,
4861+
(errcode(ERRCODE_CANNOT_COERCE),
4862+
errmsg("cannot cast type %s to %s",
4863+
format_type_be(BOOLOID),
4864+
format_type_be(jsexpr->returning->typid)),
4865+
parser_coercion_errposition(pstate, location, (Node *) jsexpr)));
4866+
4867+
if (jsexpr->result_coercion->expr == (Node *) placeholder)
4868+
jsexpr->result_coercion->expr = NULL;
4869+
}
48364870
break;
48374871
}
48384872

src/backend/utils/adt/ruleutils.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9306,9 +9306,10 @@ get_rule_expr(Node *node, deparse_context *context,
93069306
}
93079307
}
93089308

9309-
if (jexpr->op != IS_JSON_EXISTS)
9309+
if (jexpr->op != IS_JSON_EXISTS ||
9310+
jexpr->returning->typid != BOOLOID)
93109311
get_json_returning(jexpr->returning, context->buf,
9311-
jexpr->op != IS_JSON_VALUE);
9312+
jexpr->op == IS_JSON_QUERY);
93129313

93139314
get_json_expr_options(jexpr, context,
93149315
jexpr->op == IS_JSON_EXISTS ?

src/test/regress/expected/jsonb_sqljson.out

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,57 @@ SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
140140
t
141141
(1 row)
142142

143+
-- extension: RETURNING clause
144+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING bool);
145+
json_exists
146+
-------------
147+
t
148+
(1 row)
149+
150+
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING bool);
151+
json_exists
152+
-------------
153+
f
154+
(1 row)
155+
156+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING int);
157+
json_exists
158+
-------------
159+
1
160+
(1 row)
161+
162+
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING int);
163+
json_exists
164+
-------------
165+
0
166+
(1 row)
167+
168+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING text);
169+
json_exists
170+
-------------
171+
true
172+
(1 row)
173+
174+
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING text);
175+
json_exists
176+
-------------
177+
false
178+
(1 row)
179+
180+
SELECT JSON_EXISTS(jsonb '1', 'strict $[1]' RETURNING text FALSE ON ERROR);
181+
json_exists
182+
-------------
183+
false
184+
(1 row)
185+
186+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING jsonb);
187+
ERROR: cannot cast type boolean to jsonb
188+
LINE 1: SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING jsonb);
189+
^
190+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING float4);
191+
ERROR: cannot cast type boolean to real
192+
LINE 1: SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING float4);
193+
^
143194
-- JSON_VALUE
144195
SELECT JSON_VALUE(NULL::jsonb, '$');
145196
json_value
@@ -866,6 +917,8 @@ CREATE TABLE test_jsonb_constraints (
866917
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
867918
CONSTRAINT test_jsonb_constraint5
868919
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
920+
CONSTRAINT test_jsonb_constraint6
921+
CHECK (JSON_EXISTS(js::jsonb, 'strict $.a' RETURNING int TRUE ON ERROR) < 2)
869922
);
870923
\d test_jsonb_constraints
871924
Table "public.test_jsonb_constraints"
@@ -880,6 +933,7 @@ Check constraints:
880933
"test_jsonb_constraint3" CHECK (JSON_VALUE(js::jsonb, '$."a"' RETURNING integer DEFAULT ('12'::text || i)::integer ON EMPTY ERROR ON ERROR) > i)
881934
"test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
882935
"test_jsonb_constraint5" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
936+
"test_jsonb_constraint6" CHECK (JSON_EXISTS(js::jsonb, 'strict $."a"' RETURNING integer TRUE ON ERROR) < 2)
883937

884938
SELECT check_clause
885939
FROM information_schema.check_constraints
@@ -891,7 +945,8 @@ WHERE constraint_name LIKE 'test_jsonb_constraint%';
891945
((JSON_VALUE((js)::jsonb, '$."a"' RETURNING integer DEFAULT (('12'::text || i))::integer ON EMPTY ERROR ON ERROR) > i))
892946
((JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < '[10]'::jsonb))
893947
((JSON_QUERY((js)::jsonb, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C")))
894-
(5 rows)
948+
((JSON_EXISTS((js)::jsonb, 'strict $."a"' RETURNING integer TRUE ON ERROR) < 2))
949+
(6 rows)
895950

896951
SELECT pg_get_expr(adbin, adrelid) FROM pg_attrdef WHERE adrelid = 'test_jsonb_constraints'::regclass;
897952
pg_get_expr

src/test/regress/sql/jsonb_sqljson.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING
3232
SELECT JSON_EXISTS(jsonb '1', '$ > 2');
3333
SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
3434

35+
-- extension: RETURNING clause
36+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING bool);
37+
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING bool);
38+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING int);
39+
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING int);
40+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING text);
41+
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING text);
42+
SELECT JSON_EXISTS(jsonb '1', 'strict $[1]' RETURNING text FALSE ON ERROR);
43+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING jsonb);
44+
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING float4);
45+
46+
3547
-- JSON_VALUE
3648

3749
SELECT JSON_VALUE(NULL::jsonb, '$');
@@ -258,6 +270,8 @@ CREATE TABLE test_jsonb_constraints (
258270
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
259271
CONSTRAINT test_jsonb_constraint5
260272
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
273+
CONSTRAINT test_jsonb_constraint6
274+
CHECK (JSON_EXISTS(js::jsonb, 'strict $.a' RETURNING int TRUE ON ERROR) < 2)
261275
);
262276

263277
\d test_jsonb_constraints

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy