Skip to content

Commit 564dc52

Browse files
author
Nikita Glukhov
committed
Add IS JSON predicate transformation
1 parent b064ff1 commit 564dc52

File tree

17 files changed

+783
-4
lines changed

17 files changed

+783
-4
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,6 +2833,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28332833
APP_JUMB(opts->absent_on_null);
28342834
}
28352835
break;
2836+
case T_JsonIsPredicateOpts:
2837+
{
2838+
JsonIsPredicateOpts *opts = (JsonIsPredicateOpts *) node;
2839+
2840+
APP_JUMB(opts->unique_keys);
2841+
APP_JUMB(opts->value_type);
2842+
}
2843+
break;
28362844
case T_List:
28372845
foreach(temp, (List *) node)
28382846
{

src/backend/nodes/copyfuncs.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,6 +2342,36 @@ _copyJsonArrayQueryCtor(const JsonArrayQueryCtor *from)
23422342
return newnode;
23432343
}
23442344

2345+
/*
2346+
* _copyJsonIsPredicate
2347+
*/
2348+
static JsonIsPredicate *
2349+
_copyJsonIsPredicate(const JsonIsPredicate *from)
2350+
{
2351+
JsonIsPredicate *newnode = makeNode(JsonIsPredicate);
2352+
2353+
COPY_NODE_FIELD(expr);
2354+
COPY_SCALAR_FIELD(format);
2355+
COPY_SCALAR_FIELD(vtype);
2356+
COPY_SCALAR_FIELD(unique_keys);
2357+
2358+
return newnode;
2359+
}
2360+
2361+
/*
2362+
* _copyJsonIsPredicateOpts
2363+
*/
2364+
static JsonIsPredicateOpts *
2365+
_copyJsonIsPredicateOpts(const JsonIsPredicateOpts *from)
2366+
{
2367+
JsonIsPredicateOpts *newnode = makeNode(JsonIsPredicateOpts);
2368+
2369+
COPY_SCALAR_FIELD(value_type);
2370+
COPY_SCALAR_FIELD(unique_keys);
2371+
2372+
return newnode;
2373+
}
2374+
23452375
/* ****************************************************************
23462376
* relation.h copy functions
23472377
*
@@ -5256,6 +5286,12 @@ copyObjectImpl(const void *from)
52565286
case T_JsonArrayAgg:
52575287
retval = _copyJsonArrayAgg(from);
52585288
break;
5289+
case T_JsonIsPredicate:
5290+
retval = _copyJsonIsPredicate(from);
5291+
break;
5292+
case T_JsonIsPredicateOpts:
5293+
retval = _copyJsonIsPredicateOpts(from);
5294+
break;
52595295

52605296
/*
52615297
* RELATION NODES

src/backend/nodes/equalfuncs.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,16 @@ _equalJsonCtorOpts(const JsonCtorOpts *a, const JsonCtorOpts *b)
843843
return true;
844844
}
845845

846+
static bool
847+
_equalJsonIsPredicateOpts(const JsonIsPredicateOpts *a,
848+
const JsonIsPredicateOpts *b)
849+
{
850+
COMPARE_SCALAR_FIELD(value_type);
851+
COMPARE_SCALAR_FIELD(unique_keys);
852+
853+
return true;
854+
}
855+
846856
/*
847857
* Stuff from relation.h
848858
*/
@@ -3197,6 +3207,9 @@ equal(const void *a, const void *b)
31973207
case T_JsonCtorOpts:
31983208
retval = _equalJsonCtorOpts(a, b);
31993209
break;
3210+
case T_JsonIsPredicateOpts:
3211+
retval = _equalJsonIsPredicateOpts(a, b);
3212+
break;
32003213

32013214
/*
32023215
* RELATION NODES

src/backend/nodes/nodeFuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3807,6 +3807,8 @@ raw_expression_tree_walker(Node *node,
38073807
return true;
38083808
}
38093809
break;
3810+
case T_JsonIsPredicate:
3811+
return walker(((JsonIsPredicate *) node)->expr, context);
38103812
default:
38113813
elog(ERROR, "unrecognized node type: %d",
38123814
(int) nodeTag(node));

src/backend/nodes/outfuncs.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,6 +1792,15 @@ _outJsonCtorOpts(StringInfo str, const JsonCtorOpts *node)
17921792
WRITE_BOOL_FIELD(absent_on_null);
17931793
}
17941794

1795+
static void
1796+
_outJsonIsPredicateOpts(StringInfo str, const JsonIsPredicateOpts *node)
1797+
{
1798+
WRITE_NODE_TYPE("JSONISOPTS");
1799+
1800+
WRITE_ENUM_FIELD(value_type, JsonValueType);
1801+
WRITE_BOOL_FIELD(unique_keys);
1802+
}
1803+
17951804
/*****************************************************************************
17961805
*
17971806
* Stuff from relation.h.
@@ -4353,6 +4362,9 @@ outNode(StringInfo str, const void *obj)
43534362
case T_JsonCtorOpts:
43544363
_outJsonCtorOpts(str, obj);
43554364
break;
4365+
case T_JsonIsPredicateOpts:
4366+
_outJsonIsPredicateOpts(str, obj);
4367+
break;
43564368

43574369
default:
43584370

src/backend/nodes/readfuncs.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,20 @@ _readJsonCtorOpts(void)
13691369
READ_DONE();
13701370
}
13711371

1372+
/*
1373+
* _readJsonIsPredicateOpts
1374+
*/
1375+
static JsonIsPredicateOpts *
1376+
_readJsonIsPredicateOpts()
1377+
{
1378+
READ_LOCALS(JsonIsPredicateOpts);
1379+
1380+
READ_ENUM_FIELD(value_type, JsonValueType);
1381+
READ_BOOL_FIELD(unique_keys);
1382+
1383+
READ_DONE();
1384+
}
1385+
13721386
/*
13731387
* Stuff from parsenodes.h.
13741388
*/
@@ -2786,6 +2800,8 @@ parseNodeString(void)
27862800
return_value = _readJsonValueExpr();
27872801
else if (MATCH("JSONCTOROPTS", 12))
27882802
return_value = _readJsonCtorOpts();
2803+
else if (MATCH("JSONISOPTS", 10))
2804+
return_value = _readJsonIsPredicateOpts();
27892805
else
27902806
{
27912807
elog(ERROR, "badly formatted node string \"%.32s\"...", token);

src/backend/parser/parse_expr.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ static Node *transformJsonArrayQueryCtor(ParseState *pstate,
130130
JsonArrayQueryCtor *ctor);
131131
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
132132
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
133+
static Node *transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *p);
133134
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
134135
List *largs, List *rargs, int location);
135136
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -398,6 +399,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
398399
result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
399400
break;
400401

402+
case T_JsonIsPredicate:
403+
result = transformJsonIsPredicate(pstate, (JsonIsPredicate *) expr);
404+
break;
405+
401406
default:
402407
/* should not reach here */
403408
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -4174,3 +4179,108 @@ transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor)
41744179

41754180
return coerceJsonFuncExpr(pstate, (Node *) fexpr, &returning, true);
41764181
}
4182+
4183+
static const char *
4184+
JsonValueTypeStrings[] =
4185+
{
4186+
"any",
4187+
"object",
4188+
"array",
4189+
"scalar",
4190+
};
4191+
4192+
static Const *
4193+
makeJsonValueTypeConst(JsonValueType type)
4194+
{
4195+
return makeConst(TEXTOID, -1, InvalidOid, -1,
4196+
PointerGetDatum(cstring_to_text(
4197+
JsonValueTypeStrings[(int) type])),
4198+
false, false);
4199+
}
4200+
4201+
/*
4202+
* Transform IS JSON predicate into
4203+
* json[b]_is_valid(json, value_type [, check_key_uniqueness]) call.
4204+
*/
4205+
static Node *
4206+
transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred)
4207+
{
4208+
Node *expr = transformExprRecurse(pstate, pred->expr);
4209+
Oid exprtype = exprType(expr);
4210+
FuncExpr *fexpr;
4211+
JsonIsPredicateOpts *opts;
4212+
4213+
/* prepare input document */
4214+
if (exprtype == BYTEAOID)
4215+
{
4216+
expr = makeJsonByteaToTextConversion(expr, &pred->format,
4217+
exprLocation(expr));
4218+
exprtype = TEXTOID;
4219+
}
4220+
else
4221+
{
4222+
char typcategory;
4223+
bool typispreferred;
4224+
4225+
get_type_category_preferred(exprtype, &typcategory, &typispreferred);
4226+
4227+
if (exprtype == UNKNOWNOID || typcategory == TYPCATEGORY_STRING)
4228+
{
4229+
expr = coerce_to_target_type(pstate, (Node *) expr, exprtype,
4230+
TEXTOID, -1,
4231+
COERCION_IMPLICIT,
4232+
COERCE_IMPLICIT_CAST, -1);
4233+
exprtype = TEXTOID;
4234+
}
4235+
4236+
if (pred->format.encoding != JS_ENC_DEFAULT)
4237+
ereport(ERROR,
4238+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4239+
parser_errposition(pstate, pred->format.location),
4240+
errmsg("cannot use JSON FORMAT ENCODING clause for non-bytea input types")));
4241+
}
4242+
4243+
expr = (Node *) makeJsonValueExpr((Expr *) expr, pred->format);
4244+
4245+
/* make resulting expression */
4246+
if (exprtype == TEXTOID || exprtype == JSONOID)
4247+
{
4248+
fexpr = makeFuncExpr(F_JSON_IS_VALID, BOOLOID,
4249+
list_make3(expr,
4250+
makeJsonValueTypeConst(pred->vtype),
4251+
makeBoolConst(pred->unique_keys, false)),
4252+
InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
4253+
4254+
fexpr->location = pred->location;
4255+
}
4256+
else if (exprtype == JSONBOID)
4257+
{
4258+
/* XXX the following expressions also can be used here:
4259+
* jsonb_type(jsonb) = 'type' (for object and array checks)
4260+
* CASE jsonb_type(jsonb) WHEN ... END (for scalars checks)
4261+
*/
4262+
fexpr = makeFuncExpr(F_JSONB_IS_VALID, BOOLOID,
4263+
list_make2(expr,
4264+
makeJsonValueTypeConst(pred->vtype)),
4265+
InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
4266+
4267+
fexpr->location = pred->location;
4268+
}
4269+
else
4270+
{
4271+
ereport(ERROR,
4272+
(errcode(ERRCODE_DATATYPE_MISMATCH),
4273+
errmsg("cannot use type %s in IS JSON predicate",
4274+
format_type_be(exprtype))));
4275+
return NULL;
4276+
}
4277+
4278+
opts = makeNode(JsonIsPredicateOpts);
4279+
opts->unique_keys = pred->unique_keys;
4280+
opts->value_type = pred->vtype;
4281+
4282+
fexpr->funcformat2 = FUNCFMT_IS_JSON;
4283+
fexpr->funcformatopts = (Node *) opts;
4284+
4285+
return (Node *) fexpr;
4286+
}

0 commit comments

Comments
 (0)