Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ef15479

Browse files
author
Nikita Glukhov
committed
Add IS JSON predicate transformation
1 parent fbbaaf9 commit ef15479

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
@@ -2911,6 +2911,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
29112911
APP_JUMB(opts->absent_on_null);
29122912
}
29132913
break;
2914+
case T_JsonIsPredicateOpts:
2915+
{
2916+
JsonIsPredicateOpts *opts = (JsonIsPredicateOpts *) node;
2917+
2918+
APP_JUMB(opts->unique_keys);
2919+
APP_JUMB(opts->value_type);
2920+
}
2921+
break;
29142922
case T_List:
29152923
foreach(temp, (List *) node)
29162924
{

src/backend/nodes/copyfuncs.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,6 +2357,36 @@ _copyJsonArrayQueryCtor(const JsonArrayQueryCtor *from)
23572357
return newnode;
23582358
}
23592359

2360+
/*
2361+
* _copyJsonIsPredicate
2362+
*/
2363+
static JsonIsPredicate *
2364+
_copyJsonIsPredicate(const JsonIsPredicate *from)
2365+
{
2366+
JsonIsPredicate *newnode = makeNode(JsonIsPredicate);
2367+
2368+
COPY_NODE_FIELD(expr);
2369+
COPY_SCALAR_FIELD(format);
2370+
COPY_SCALAR_FIELD(vtype);
2371+
COPY_SCALAR_FIELD(unique_keys);
2372+
2373+
return newnode;
2374+
}
2375+
2376+
/*
2377+
* _copyJsonIsPredicateOpts
2378+
*/
2379+
static JsonIsPredicateOpts *
2380+
_copyJsonIsPredicateOpts(const JsonIsPredicateOpts *from)
2381+
{
2382+
JsonIsPredicateOpts *newnode = makeNode(JsonIsPredicateOpts);
2383+
2384+
COPY_SCALAR_FIELD(value_type);
2385+
COPY_SCALAR_FIELD(unique_keys);
2386+
2387+
return newnode;
2388+
}
2389+
23602390
/* ****************************************************************
23612391
* pathnodes.h copy functions
23622392
*
@@ -5285,6 +5315,12 @@ copyObjectImpl(const void *from)
52855315
case T_JsonArrayAgg:
52865316
retval = _copyJsonArrayAgg(from);
52875317
break;
5318+
case T_JsonIsPredicate:
5319+
retval = _copyJsonIsPredicate(from);
5320+
break;
5321+
case T_JsonIsPredicateOpts:
5322+
retval = _copyJsonIsPredicateOpts(from);
5323+
break;
52885324

52895325
/*
52905326
* RELATION NODES

src/backend/nodes/equalfuncs.c

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

848+
static bool
849+
_equalJsonIsPredicateOpts(const JsonIsPredicateOpts *a,
850+
const JsonIsPredicateOpts *b)
851+
{
852+
COMPARE_SCALAR_FIELD(value_type);
853+
COMPARE_SCALAR_FIELD(unique_keys);
854+
855+
return true;
856+
}
857+
848858
/*
849859
* Stuff from pathnodes.h
850860
*/
@@ -3212,6 +3222,9 @@ equal(const void *a, const void *b)
32123222
case T_JsonCtorOpts:
32133223
retval = _equalJsonCtorOpts(a, b);
32143224
break;
3225+
case T_JsonIsPredicateOpts:
3226+
retval = _equalJsonIsPredicateOpts(a, b);
3227+
break;
32153228

32163229
/*
32173230
* RELATION NODES

src/backend/nodes/nodeFuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3847,6 +3847,8 @@ raw_expression_tree_walker(Node *node,
38473847
return true;
38483848
}
38493849
break;
3850+
case T_JsonIsPredicate:
3851+
return walker(((JsonIsPredicate *) node)->expr, context);
38503852
default:
38513853
elog(ERROR, "unrecognized node type: %d",
38523854
(int) nodeTag(node));

src/backend/nodes/outfuncs.c

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

1714+
static void
1715+
_outJsonIsPredicateOpts(StringInfo str, const JsonIsPredicateOpts *node)
1716+
{
1717+
WRITE_NODE_TYPE("JSONISOPTS");
1718+
1719+
WRITE_ENUM_FIELD(value_type, JsonValueType);
1720+
WRITE_BOOL_FIELD(unique_keys);
1721+
}
1722+
17141723
/*****************************************************************************
17151724
*
17161725
* Stuff from pathnodes.h.
@@ -4311,6 +4320,9 @@ outNode(StringInfo str, const void *obj)
43114320
case T_JsonCtorOpts:
43124321
_outJsonCtorOpts(str, obj);
43134322
break;
4323+
case T_JsonIsPredicateOpts:
4324+
_outJsonIsPredicateOpts(str, obj);
4325+
break;
43144326

43154327
default:
43164328

src/backend/nodes/readfuncs.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,20 @@ _readJsonCtorOpts(void)
13831383
READ_DONE();
13841384
}
13851385

1386+
/*
1387+
* _readJsonIsPredicateOpts
1388+
*/
1389+
static JsonIsPredicateOpts *
1390+
_readJsonIsPredicateOpts()
1391+
{
1392+
READ_LOCALS(JsonIsPredicateOpts);
1393+
1394+
READ_ENUM_FIELD(value_type, JsonValueType);
1395+
READ_BOOL_FIELD(unique_keys);
1396+
1397+
READ_DONE();
1398+
}
1399+
13861400
/*
13871401
* Stuff from parsenodes.h.
13881402
*/
@@ -2850,6 +2864,8 @@ parseNodeString(void)
28502864
return_value = _readJsonValueExpr();
28512865
else if (MATCH("JSONCTOROPTS", 12))
28522866
return_value = _readJsonCtorOpts();
2867+
else if (MATCH("JSONISOPTS", 10))
2868+
return_value = _readJsonIsPredicateOpts();
28532869
else
28542870
{
28552871
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
@@ -129,6 +129,7 @@ static Node *transformJsonArrayQueryCtor(ParseState *pstate,
129129
JsonArrayQueryCtor *ctor);
130130
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
131131
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
132+
static Node *transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *p);
132133
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
133134
List *largs, List *rargs, int location);
134135
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -397,6 +398,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
397398
result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
398399
break;
399400

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

42634268
return coerceJsonFuncExpr(pstate, (Node *) fexpr, &returning, true);
42644269
}
4270+
4271+
static const char *
4272+
JsonValueTypeStrings[] =
4273+
{
4274+
"any",
4275+
"object",
4276+
"array",
4277+
"scalar",
4278+
};
4279+
4280+
static Const *
4281+
makeJsonValueTypeConst(JsonValueType type)
4282+
{
4283+
return makeConst(TEXTOID, -1, InvalidOid, -1,
4284+
PointerGetDatum(cstring_to_text(
4285+
JsonValueTypeStrings[(int) type])),
4286+
false, false);
4287+
}
4288+
4289+
/*
4290+
* Transform IS JSON predicate into
4291+
* json[b]_is_valid(json, value_type [, check_key_uniqueness]) call.
4292+
*/
4293+
static Node *
4294+
transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred)
4295+
{
4296+
Node *expr = transformExprRecurse(pstate, pred->expr);
4297+
Oid exprtype = exprType(expr);
4298+
FuncExpr *fexpr;
4299+
JsonIsPredicateOpts *opts;
4300+
4301+
/* prepare input document */
4302+
if (exprtype == BYTEAOID)
4303+
{
4304+
expr = makeJsonByteaToTextConversion(expr, &pred->format,
4305+
exprLocation(expr));
4306+
exprtype = TEXTOID;
4307+
}
4308+
else
4309+
{
4310+
char typcategory;
4311+
bool typispreferred;
4312+
4313+
get_type_category_preferred(exprtype, &typcategory, &typispreferred);
4314+
4315+
if (exprtype == UNKNOWNOID || typcategory == TYPCATEGORY_STRING)
4316+
{
4317+
expr = coerce_to_target_type(pstate, (Node *) expr, exprtype,
4318+
TEXTOID, -1,
4319+
COERCION_IMPLICIT,
4320+
COERCE_IMPLICIT_CAST, -1);
4321+
exprtype = TEXTOID;
4322+
}
4323+
4324+
if (pred->format.encoding != JS_ENC_DEFAULT)
4325+
ereport(ERROR,
4326+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4327+
parser_errposition(pstate, pred->format.location),
4328+
errmsg("cannot use JSON FORMAT ENCODING clause for non-bytea input types")));
4329+
}
4330+
4331+
expr = (Node *) makeJsonValueExpr((Expr *) expr, pred->format);
4332+
4333+
/* make resulting expression */
4334+
if (exprtype == TEXTOID || exprtype == JSONOID)
4335+
{
4336+
fexpr = makeFuncExpr(F_JSON_IS_VALID, BOOLOID,
4337+
list_make3(expr,
4338+
makeJsonValueTypeConst(pred->vtype),
4339+
makeBoolConst(pred->unique_keys, false)),
4340+
InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
4341+
4342+
fexpr->location = pred->location;
4343+
}
4344+
else if (exprtype == JSONBOID)
4345+
{
4346+
/* XXX the following expressions also can be used here:
4347+
* jsonb_type(jsonb) = 'type' (for object and array checks)
4348+
* CASE jsonb_type(jsonb) WHEN ... END (for scalars checks)
4349+
*/
4350+
fexpr = makeFuncExpr(F_JSONB_IS_VALID, BOOLOID,
4351+
list_make2(expr,
4352+
makeJsonValueTypeConst(pred->vtype)),
4353+
InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
4354+
4355+
fexpr->location = pred->location;
4356+
}
4357+
else
4358+
{
4359+
ereport(ERROR,
4360+
(errcode(ERRCODE_DATATYPE_MISMATCH),
4361+
errmsg("cannot use type %s in IS JSON predicate",
4362+
format_type_be(exprtype))));
4363+
return NULL;
4364+
}
4365+
4366+
opts = makeNode(JsonIsPredicateOpts);
4367+
opts->unique_keys = pred->unique_keys;
4368+
opts->value_type = pred->vtype;
4369+
4370+
fexpr->funcformat2 = FUNCFMT_IS_JSON;
4371+
fexpr->funcformatopts = (Node *) opts;
4372+
4373+
return (Node *) fexpr;
4374+
}

0 commit comments

Comments
 (0)