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

Commit 38267c6

Browse files
author
Nikita Glukhov
committed
Add IS JSON predicate transformation
1 parent e24d1a5 commit 38267c6

File tree

17 files changed

+780
-4
lines changed

17 files changed

+780
-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
@@ -2857,6 +2857,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
28572857
APP_JUMB(opts->absent_on_null);
28582858
}
28592859
break;
2860+
case T_JsonIsPredicateOpts:
2861+
{
2862+
JsonIsPredicateOpts *opts = (JsonIsPredicateOpts *) node;
2863+
2864+
APP_JUMB(opts->unique_keys);
2865+
APP_JUMB(opts->value_type);
2866+
}
2867+
break;
28602868
case T_List:
28612869
foreach(temp, (List *) node)
28622870
{

src/backend/nodes/copyfuncs.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,6 +2265,36 @@ _copyJsonArrayQueryCtor(const JsonArrayQueryCtor *from)
22652265
return newnode;
22662266
}
22672267

2268+
/*
2269+
* _copyJsonIsPredicate
2270+
*/
2271+
static JsonIsPredicate *
2272+
_copyJsonIsPredicate(const JsonIsPredicate *from)
2273+
{
2274+
JsonIsPredicate *newnode = makeNode(JsonIsPredicate);
2275+
2276+
COPY_NODE_FIELD(expr);
2277+
COPY_SCALAR_FIELD(format);
2278+
COPY_SCALAR_FIELD(vtype);
2279+
COPY_SCALAR_FIELD(unique_keys);
2280+
2281+
return newnode;
2282+
}
2283+
2284+
/*
2285+
* _copyJsonIsPredicateOpts
2286+
*/
2287+
static JsonIsPredicateOpts *
2288+
_copyJsonIsPredicateOpts(const JsonIsPredicateOpts *from)
2289+
{
2290+
JsonIsPredicateOpts *newnode = makeNode(JsonIsPredicateOpts);
2291+
2292+
COPY_SCALAR_FIELD(value_type);
2293+
COPY_SCALAR_FIELD(unique_keys);
2294+
2295+
return newnode;
2296+
}
2297+
22682298
/* ****************************************************************
22692299
* relation.h copy functions
22702300
*
@@ -5141,6 +5171,12 @@ copyObjectImpl(const void *from)
51415171
case T_JsonArrayAgg:
51425172
retval = _copyJsonArrayAgg(from);
51435173
break;
5174+
case T_JsonIsPredicate:
5175+
retval = _copyJsonIsPredicate(from);
5176+
break;
5177+
case T_JsonIsPredicateOpts:
5178+
retval = _copyJsonIsPredicateOpts(from);
5179+
break;
51445180

51455181
/*
51465182
* RELATION NODES

src/backend/nodes/equalfuncs.c

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

847+
static bool
848+
_equalJsonIsPredicateOpts(const JsonIsPredicateOpts *a,
849+
const JsonIsPredicateOpts *b)
850+
{
851+
COMPARE_SCALAR_FIELD(value_type);
852+
COMPARE_SCALAR_FIELD(unique_keys);
853+
854+
return true;
855+
}
856+
847857
/*
848858
* Stuff from relation.h
849859
*/
@@ -3173,6 +3183,9 @@ equal(const void *a, const void *b)
31733183
case T_JsonCtorOpts:
31743184
retval = _equalJsonCtorOpts(a, b);
31753185
break;
3186+
case T_JsonIsPredicateOpts:
3187+
retval = _equalJsonIsPredicateOpts(a, b);
3188+
break;
31763189

31773190
/*
31783191
* RELATION NODES

src/backend/nodes/nodeFuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3782,6 +3782,8 @@ raw_expression_tree_walker(Node *node,
37823782
return true;
37833783
}
37843784
break;
3785+
case T_JsonIsPredicate:
3786+
return walker(((JsonIsPredicate *) node)->expr, context);
37853787
default:
37863788
elog(ERROR, "unrecognized node type: %d",
37873789
(int) nodeTag(node));

src/backend/nodes/outfuncs.c

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

1726+
static void
1727+
_outJsonIsPredicateOpts(StringInfo str, const JsonIsPredicateOpts *node)
1728+
{
1729+
WRITE_NODE_TYPE("JSONISOPTS");
1730+
1731+
WRITE_ENUM_FIELD(value_type, JsonValueType);
1732+
WRITE_BOOL_FIELD(unique_keys);
1733+
}
1734+
17261735
/*****************************************************************************
17271736
*
17281737
* Stuff from relation.h.
@@ -4268,6 +4277,9 @@ outNode(StringInfo str, const void *obj)
42684277
case T_JsonCtorOpts:
42694278
_outJsonCtorOpts(str, obj);
42704279
break;
4280+
case T_JsonIsPredicateOpts:
4281+
_outJsonIsPredicateOpts(str, obj);
4282+
break;
42714283

42724284
default:
42734285

src/backend/nodes/readfuncs.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,20 @@ _readJsonCtorOpts(void)
13581358
READ_DONE();
13591359
}
13601360

1361+
/*
1362+
* _readJsonIsPredicateOpts
1363+
*/
1364+
static JsonIsPredicateOpts *
1365+
_readJsonIsPredicateOpts()
1366+
{
1367+
READ_LOCALS(JsonIsPredicateOpts);
1368+
1369+
READ_ENUM_FIELD(value_type, JsonValueType);
1370+
READ_BOOL_FIELD(unique_keys);
1371+
1372+
READ_DONE();
1373+
}
1374+
13611375
/*
13621376
* Stuff from parsenodes.h.
13631377
*/
@@ -2704,6 +2718,8 @@ parseNodeString(void)
27042718
return_value = _readJsonValueExpr();
27052719
else if (MATCH("JSONCTOROPTS", 12))
27062720
return_value = _readJsonCtorOpts();
2721+
else if (MATCH("JSONISOPTS", 10))
2722+
return_value = _readJsonIsPredicateOpts();
27072723
else
27082724
{
27092725
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));
@@ -4155,3 +4160,108 @@ transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor)
41554160

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

0 commit comments

Comments
 (0)