Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)