Skip to content

Commit 33a3776

Browse files
committed
IS JSON predicate
This patch intrdocuces the SQL standard IS JSON predicate. It operates on text and bytea values representing JSON as well as on the json and jsonb types. Each test has an IS and IS NOT variant. The tests are: IS JSON [VALUE] IS JSON ARRAY IS JSON OBJECT IS JSON SCALAR IS JSON WITH | WITHOUT UNIQUE KEYS These are mostly self-explanatory, but note that IS JSON WITHOUT UNIQUE KEYS is true whenever IS JSON is true, and IS JSON WITH UNIQUE KEYS is true whenever IS JSON is true except it IS JSON OBJECT is true and there are duplicate keys (which is never the case when applied to jsonb values). Nikita Glukhov Reviewers have included (in no particular order) Andres Freund, Alexander Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu, Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby. Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru
1 parent 6198420 commit 33a3776

25 files changed

+856
-17
lines changed

src/backend/executor/execExpr.c

+13
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,19 @@ ExecInitExprRec(Expr *node, ExprState *state,
25132513
}
25142514
break;
25152515

2516+
case T_JsonIsPredicate:
2517+
{
2518+
JsonIsPredicate *pred = (JsonIsPredicate *) node;
2519+
2520+
ExecInitExprRec((Expr *) pred->expr, state, resv, resnull);
2521+
2522+
scratch.opcode = EEOP_IS_JSON;
2523+
scratch.d.is_json.pred = pred;
2524+
2525+
ExprEvalPushStep(state, &scratch);
2526+
break;
2527+
}
2528+
25162529
default:
25172530
elog(ERROR, "unrecognized node type: %d",
25182531
(int) nodeTag(node));

src/backend/executor/execExprInterp.c

+95
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include "utils/expandedrecord.h"
7474
#include "utils/json.h"
7575
#include "utils/jsonb.h"
76+
#include "utils/jsonfuncs.h"
7677
#include "utils/lsyscache.h"
7778
#include "utils/memutils.h"
7879
#include "utils/timestamp.h"
@@ -480,6 +481,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
480481
&&CASE_EEOP_WINDOW_FUNC,
481482
&&CASE_EEOP_SUBPLAN,
482483
&&CASE_EEOP_JSON_CONSTRUCTOR,
484+
&&CASE_EEOP_IS_JSON,
483485
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
484486
&&CASE_EEOP_AGG_DESERIALIZE,
485487
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
@@ -1799,6 +1801,14 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
17991801
EEO_NEXT();
18001802
}
18011803

1804+
EEO_CASE(EEOP_IS_JSON)
1805+
{
1806+
/* too complex for an inline implementation */
1807+
ExecEvalJsonIsPredicate(state, op);
1808+
1809+
EEO_NEXT();
1810+
}
1811+
18021812
EEO_CASE(EEOP_LAST)
18031813
{
18041814
/* unreachable */
@@ -3909,6 +3919,91 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
39093919
}
39103920
}
39113921

3922+
void
3923+
ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op)
3924+
{
3925+
JsonIsPredicate *pred = op->d.is_json.pred;
3926+
Datum js = *op->resvalue;
3927+
Oid exprtype;
3928+
bool res;
3929+
3930+
if (*op->resnull)
3931+
{
3932+
*op->resvalue = BoolGetDatum(false);
3933+
return;
3934+
}
3935+
3936+
exprtype = exprType(pred->expr);
3937+
3938+
if (exprtype == TEXTOID || exprtype == JSONOID)
3939+
{
3940+
text *json = DatumGetTextP(js);
3941+
3942+
if (pred->value_type == JS_TYPE_ANY)
3943+
res = true;
3944+
else
3945+
{
3946+
switch (json_get_first_token(json, false))
3947+
{
3948+
case JSON_TOKEN_OBJECT_START:
3949+
res = pred->value_type == JS_TYPE_OBJECT;
3950+
break;
3951+
case JSON_TOKEN_ARRAY_START:
3952+
res = pred->value_type == JS_TYPE_ARRAY;
3953+
break;
3954+
case JSON_TOKEN_STRING:
3955+
case JSON_TOKEN_NUMBER:
3956+
case JSON_TOKEN_TRUE:
3957+
case JSON_TOKEN_FALSE:
3958+
case JSON_TOKEN_NULL:
3959+
res = pred->value_type == JS_TYPE_SCALAR;
3960+
break;
3961+
default:
3962+
res = false;
3963+
break;
3964+
}
3965+
}
3966+
3967+
/*
3968+
* Do full parsing pass only for uniqueness check or for
3969+
* JSON text validation.
3970+
*/
3971+
if (res && (pred->unique_keys || exprtype == TEXTOID))
3972+
res = json_validate(json, pred->unique_keys);
3973+
}
3974+
else if (exprtype == JSONBOID)
3975+
{
3976+
if (pred->value_type == JS_TYPE_ANY)
3977+
res = true;
3978+
else
3979+
{
3980+
Jsonb *jb = DatumGetJsonbP(js);
3981+
3982+
switch (pred->value_type)
3983+
{
3984+
case JS_TYPE_OBJECT:
3985+
res = JB_ROOT_IS_OBJECT(jb);
3986+
break;
3987+
case JS_TYPE_ARRAY:
3988+
res = JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb);
3989+
break;
3990+
case JS_TYPE_SCALAR:
3991+
res = JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb);
3992+
break;
3993+
default:
3994+
res = false;
3995+
break;
3996+
}
3997+
}
3998+
3999+
/* Key uniqueness check is redundant for jsonb */
4000+
}
4001+
else
4002+
res = false;
4003+
4004+
*op->resvalue = BoolGetDatum(res);
4005+
}
4006+
39124007
/*
39134008
* ExecEvalGroupingFunc
39144009
*

src/backend/jit/llvm/llvmjit_expr.c

+6
Original file line numberDiff line numberDiff line change
@@ -2354,6 +2354,12 @@ llvm_compile_expr(ExprState *state)
23542354
LLVMBuildBr(b, opblocks[opno + 1]);
23552355
break;
23562356

2357+
case EEOP_IS_JSON:
2358+
build_EvalXFunc(b, mod, "ExecEvalJsonIsPredicate",
2359+
v_state, op);
2360+
LLVMBuildBr(b, opblocks[opno + 1]);
2361+
break;
2362+
23572363
case EEOP_LAST:
23582364
Assert(false);
23592365
break;

src/backend/jit/llvm/llvmjit_types.c

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ void *referenced_functions[] =
132132
ExecEvalWholeRowVar,
133133
ExecEvalXmlExpr,
134134
ExecEvalJsonConstructor,
135+
ExecEvalJsonIsPredicate,
135136
MakeExpandedObjectReadOnlyInternal,
136137
slot_getmissingattrs,
137138
slot_getsomeattrs_int,

src/backend/nodes/copyfuncs.c

+20
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,23 @@ _copyJsonArrayQueryConstructor(const JsonArrayQueryConstructor *from)
24912491
return newnode;
24922492
}
24932493

2494+
/*
2495+
* _copyJsonIsPredicate
2496+
*/
2497+
static JsonIsPredicate *
2498+
_copyJsonIsPredicate(const JsonIsPredicate *from)
2499+
{
2500+
JsonIsPredicate *newnode = makeNode(JsonIsPredicate);
2501+
2502+
COPY_NODE_FIELD(expr);
2503+
COPY_SCALAR_FIELD(format);
2504+
COPY_SCALAR_FIELD(value_type);
2505+
COPY_SCALAR_FIELD(unique_keys);
2506+
COPY_LOCATION_FIELD(location);
2507+
2508+
return newnode;
2509+
}
2510+
24942511
/* ****************************************************************
24952512
* pathnodes.h copy functions
24962513
*
@@ -5625,6 +5642,9 @@ copyObjectImpl(const void *from)
56255642
case T_JsonArrayAgg:
56265643
retval = _copyJsonArrayAgg(from);
56275644
break;
5645+
case T_JsonIsPredicate:
5646+
retval = _copyJsonIsPredicate(from);
5647+
break;
56285648

56295649
/*
56305650
* RELATION NODES

src/backend/nodes/equalfuncs.c

+15
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,18 @@ _equalJsonArrayQueryConstructor(const JsonArrayQueryConstructor *a,
976976
return true;
977977
}
978978

979+
static bool
980+
_equalJsonIsPredicate(const JsonIsPredicate *a,
981+
const JsonIsPredicate *b)
982+
{
983+
COMPARE_NODE_FIELD(expr);
984+
COMPARE_SCALAR_FIELD(value_type);
985+
COMPARE_SCALAR_FIELD(unique_keys);
986+
COMPARE_LOCATION_FIELD(location);
987+
988+
return true;
989+
}
990+
979991
/*
980992
* Stuff from pathnodes.h
981993
*/
@@ -3546,6 +3558,9 @@ equal(const void *a, const void *b)
35463558
case T_JsonConstructorExpr:
35473559
retval = _equalJsonConstructorExpr(a, b);
35483560
break;
3561+
case T_JsonIsPredicate:
3562+
retval = _equalJsonIsPredicate(a, b);
3563+
break;
35493564

35503565
/*
35513566
* RELATION NODES

src/backend/nodes/makefuncs.c

+19
Original file line numberDiff line numberDiff line change
@@ -887,3 +887,22 @@ makeJsonKeyValue(Node *key, Node *value)
887887

888888
return (Node *) n;
889889
}
890+
891+
/*
892+
* makeJsonIsPredicate -
893+
* creates a JsonIsPredicate node
894+
*/
895+
Node *
896+
makeJsonIsPredicate(Node *expr, JsonFormat *format, JsonValueType value_type,
897+
bool unique_keys, int location)
898+
{
899+
JsonIsPredicate *n = makeNode(JsonIsPredicate);
900+
901+
n->expr = expr;
902+
n->format = format;
903+
n->value_type = value_type;
904+
n->unique_keys = unique_keys;
905+
n->location = location;
906+
907+
return (Node *) n;
908+
}

src/backend/nodes/nodeFuncs.c

+26
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ exprType(const Node *expr)
260260
case T_JsonConstructorExpr:
261261
type = ((const JsonConstructorExpr *) expr)->returning->typid;
262262
break;
263+
case T_JsonIsPredicate:
264+
type = BOOLOID;
265+
break;
263266
default:
264267
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
265268
type = InvalidOid; /* keep compiler quiet */
@@ -985,6 +988,9 @@ exprCollation(const Node *expr)
985988
coll = InvalidOid;
986989
}
987990
break;
991+
case T_JsonIsPredicate:
992+
coll = InvalidOid; /* result is always an boolean type */
993+
break;
988994
default:
989995
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
990996
coll = InvalidOid; /* keep compiler quiet */
@@ -1211,6 +1217,9 @@ exprSetCollation(Node *expr, Oid collation)
12111217
Assert(!OidIsValid(collation)); /* result is always a json[b] type */
12121218
}
12131219
break;
1220+
case T_JsonIsPredicate:
1221+
Assert(!OidIsValid(collation)); /* result is always boolean */
1222+
break;
12141223
default:
12151224
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
12161225
break;
@@ -1663,6 +1672,9 @@ exprLocation(const Node *expr)
16631672
case T_JsonConstructorExpr:
16641673
loc = ((const JsonConstructorExpr *) expr)->location;
16651674
break;
1675+
case T_JsonIsPredicate:
1676+
loc = ((const JsonIsPredicate *) expr)->location;
1677+
break;
16661678
default:
16671679
/* for any other node type it's just unknown... */
16681680
loc = -1;
@@ -2429,6 +2441,8 @@ expression_tree_walker(Node *node,
24292441
return true;
24302442
}
24312443
break;
2444+
case T_JsonIsPredicate:
2445+
return walker(((JsonIsPredicate *) node)->expr, context);
24322446
default:
24332447
elog(ERROR, "unrecognized node type: %d",
24342448
(int) nodeTag(node));
@@ -3438,6 +3452,16 @@ expression_tree_mutator(Node *node,
34383452
MUTATE(newnode->coercion, jve->coercion, Expr *);
34393453
MUTATE(newnode->returning, jve->returning, JsonReturning *);
34403454

3455+
return (Node *) newnode;
3456+
}
3457+
case T_JsonIsPredicate:
3458+
{
3459+
JsonIsPredicate *pred = (JsonIsPredicate *) node;
3460+
JsonIsPredicate *newnode;
3461+
3462+
FLATCOPY(newnode, pred, JsonIsPredicate);
3463+
MUTATE(newnode->expr, pred->expr, Node *);
3464+
34413465
return (Node *) newnode;
34423466
}
34433467
default:
@@ -4290,6 +4314,8 @@ raw_expression_tree_walker(Node *node,
42904314
return true;
42914315
}
42924316
break;
4317+
case T_JsonIsPredicate:
4318+
return walker(((JsonIsPredicate *) node)->expr, context);
42934319
default:
42944320
elog(ERROR, "unrecognized node type: %d",
42954321
(int) nodeTag(node));

src/backend/nodes/outfuncs.c

+14
Original file line numberDiff line numberDiff line change
@@ -1797,6 +1797,17 @@ _outJsonConstructorExpr(StringInfo str, const JsonConstructorExpr *node)
17971797
WRITE_LOCATION_FIELD(location);
17981798
}
17991799

1800+
static void
1801+
_outJsonIsPredicate(StringInfo str, const JsonIsPredicate *node)
1802+
{
1803+
WRITE_NODE_TYPE("JSONISPREDICATE");
1804+
1805+
WRITE_NODE_FIELD(expr);
1806+
WRITE_ENUM_FIELD(value_type, JsonValueType);
1807+
WRITE_BOOL_FIELD(unique_keys);
1808+
WRITE_LOCATION_FIELD(location);
1809+
}
1810+
18001811
/*****************************************************************************
18011812
*
18021813
* Stuff from pathnodes.h.
@@ -4630,6 +4641,9 @@ outNode(StringInfo str, const void *obj)
46304641
case T_JsonConstructorExpr:
46314642
_outJsonConstructorExpr(str, obj);
46324643
break;
4644+
case T_JsonIsPredicate:
4645+
_outJsonIsPredicate(str, obj);
4646+
break;
46334647

46344648
default:
46354649

src/backend/nodes/readfuncs.c

+18
Original file line numberDiff line numberDiff line change
@@ -1492,6 +1492,22 @@ _readJsonConstructorExpr(void)
14921492
READ_DONE();
14931493
}
14941494

1495+
/*
1496+
* _readJsonIsPredicate
1497+
*/
1498+
static JsonIsPredicate *
1499+
_readJsonIsPredicate()
1500+
{
1501+
READ_LOCALS(JsonIsPredicate);
1502+
1503+
READ_NODE_FIELD(expr);
1504+
READ_ENUM_FIELD(value_type, JsonValueType);
1505+
READ_BOOL_FIELD(unique_keys);
1506+
READ_LOCATION_FIELD(location);
1507+
1508+
READ_DONE();
1509+
}
1510+
14951511
/*
14961512
* Stuff from pathnodes.h.
14971513
*
@@ -3090,6 +3106,8 @@ parseNodeString(void)
30903106
return_value = _readJsonValueExpr();
30913107
else if (MATCH("JSONCTOREXPR", 12))
30923108
return_value = _readJsonConstructorExpr();
3109+
else if (MATCH("JSONISPREDICATE", 15))
3110+
return_value = _readJsonIsPredicate();
30933111
else
30943112
{
30953113
elog(ERROR, "badly formatted node string \"%.32s\"...", token);

0 commit comments

Comments
 (0)