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

Commit a7332c0

Browse files
author
Nikita Glukhov
committed
Add json support for JSON_EXISTS, JSON_VALUE, JSON_QUERY
1 parent d1dc4b5 commit a7332c0

File tree

7 files changed

+1388
-81
lines changed

7 files changed

+1388
-81
lines changed

src/backend/executor/execExprInterp.c

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4163,17 +4163,21 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
41634163
*/
41644164
static Datum
41654165
ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
4166-
ExprState *default_estate, bool *is_null)
4166+
ExprState *default_estate, bool is_jsonb, bool *is_null)
41674167
{
41684168
*is_null = false;
41694169

41704170
switch (behavior->btype)
41714171
{
41724172
case JSON_BEHAVIOR_EMPTY_ARRAY:
4173-
return JsonbPGetDatum(JsonbMakeEmptyArray());
4173+
return is_jsonb
4174+
? JsonbPGetDatum(JsonbMakeEmptyArray())
4175+
: PointerGetDatum(cstring_to_text("[]"));
41744176

41754177
case JSON_BEHAVIOR_EMPTY_OBJECT:
4176-
return JsonbPGetDatum(JsonbMakeEmptyObject());
4178+
return is_jsonb
4179+
? JsonbPGetDatum(JsonbMakeEmptyObject())
4180+
: PointerGetDatum(cstring_to_text("{}"));
41774181

41784182
case JSON_BEHAVIOR_TRUE:
41794183
return BoolGetDatum(true);
@@ -4200,17 +4204,20 @@ ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
42004204
*/
42014205
static Datum
42024206
ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
4203-
Datum res, bool *isNull)
4207+
Datum res, bool *isNull, bool isJsonb)
42044208
{
42054209
JsonExpr *jexpr = op->d.jsonexpr.jsexpr;
42064210
JsonCoercion *coercion = jexpr->result_coercion;
4207-
Jsonb *jb = *isNull ? NULL : DatumGetJsonbP(res);
4211+
Jsonb *jb = *isNull || !isJsonb ? NULL : DatumGetJsonbP(res);
4212+
Json *js = *isNull || isJsonb ? NULL : DatumGetJsonP(res);
42084213

42094214
if ((coercion && coercion->via_io) ||
4210-
(jexpr->omit_quotes && !*isNull && JB_ROOT_IS_SCALAR(jb)))
4215+
(jexpr->omit_quotes && !*isNull &&
4216+
(isJsonb ? JB_ROOT_IS_SCALAR(jb) : JsonContainerIsScalar(&js->root))))
42114217
{
42124218
/* strip quotes and call typinput function */
4213-
char *str = *isNull ? NULL : JsonbUnquote(jb);
4219+
char *str = *isNull ? NULL :
4220+
(isJsonb ? JsonbUnquote(jb) : JsonUnquote(js));
42144221

42154222
res = InputFunctionCall(&op->d.jsonexpr.input.func, str,
42164223
op->d.jsonexpr.input.typioparam,
@@ -4224,7 +4231,7 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
42244231
res = ExecEvalExpr(op->d.jsonexpr.result_expr, econtext, isNull);
42254232
}
42264233
else if (coercion && coercion->via_populate)
4227-
res = json_populate_type(res, JSONBOID,
4234+
res = json_populate_type(res, isJsonb ? JSONBOID : JSONOID,
42284235
jexpr->returning.typid,
42294236
jexpr->returning.typmod,
42304237
&op->d.jsonexpr.cache,
@@ -4287,7 +4294,7 @@ EvalJsonPathVar(void *cxt, bool isJsonb, char *varName, int varNameLen,
42874294
* corresponding SQL type and a pointer to the coercion state.
42884295
*/
42894296
Datum
4290-
ExecPrepareJsonItemCoercion(JsonItem *item,
4297+
ExecPrepareJsonItemCoercion(JsonItem *item, bool is_jsonb,
42914298
JsonReturning *returning,
42924299
struct JsonCoercionsState *coercions,
42934300
struct JsonCoercionState **pcoercion)
@@ -4297,14 +4304,11 @@ ExecPrepareJsonItemCoercion(JsonItem *item,
42974304
JsonItem buf;
42984305

42994306
if (JsonItemIsBinary(item) &&
4300-
JsonContainerIsScalar(JsonItemBinary(item).data))
4301-
{
4302-
bool res PG_USED_FOR_ASSERTS_ONLY;
4303-
4304-
res = JsonbExtractScalar(JsonItemBinary(item).data, JsonItemJbv(&buf));
4307+
(is_jsonb ?
4308+
JsonbExtractScalar(JsonItemBinary(item).data, JsonItemJbv(&buf)) :
4309+
JsonExtractScalar((JsonContainer *) JsonItemBinary(item).data,
4310+
JsonItemJbv(&buf))))
43054311
item = &buf;
4306-
Assert(res);
4307-
}
43084312

43094313
/* get coercion state reference and datum of the corresponding SQL type */
43104314
switch (JsonItemGetType(item))
@@ -4361,7 +4365,7 @@ ExecPrepareJsonItemCoercion(JsonItem *item,
43614365
case jbvObject:
43624366
case jbvBinary:
43634367
coercion = &coercions->composite;
4364-
res = JsonbPGetDatum(JsonItemToJsonb(item));
4368+
res = JsonItemToJsonxDatum(item, is_jsonb);
43654369
break;
43664370

43674371
default:
@@ -4376,7 +4380,8 @@ ExecPrepareJsonItemCoercion(JsonItem *item,
43764380

43774381
static Datum
43784382
ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
4379-
JsonExpr *jexpr, JsonPath *path, Datum item, bool *resnull)
4383+
JsonExpr *jexpr, JsonPath *path, Datum item, bool isjsonb,
4384+
bool *resnull)
43804385
{
43814386
bool empty = false;
43824387
Datum res = (Datum) 0;
@@ -4392,7 +4397,8 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
43924397
if (isnull)
43934398
{
43944399
/* execute domain checks for NULLs */
4395-
(void) ExecEvalJsonExprCoercion(op, econtext, res, resnull);
4400+
(void) ExecEvalJsonExprCoercion(op, econtext, res, resnull,
4401+
isjsonb);
43964402
*resnull = true;
43974403
return (Datum) 0;
43984404
}
@@ -4401,15 +4407,15 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
44014407
switch (jexpr->op)
44024408
{
44034409
case IS_JSON_QUERY:
4404-
res = JsonbPathQuery(item, path, jexpr->wrapper, &empty,
4405-
op->d.jsonexpr.args);
4410+
res = JsonPathQuery(item, path, jexpr->wrapper, &empty,
4411+
op->d.jsonexpr.args, isjsonb);
44064412
*resnull = !DatumGetPointer(res);
44074413
break;
44084414

44094415
case IS_JSON_VALUE:
44104416
{
4411-
JsonItem *jbv = JsonbPathValue(item, path, &empty,
4412-
op->d.jsonexpr.args);
4417+
JsonItem *jbv = JsonPathValue(item, path, &empty,
4418+
op->d.jsonexpr.args, isjsonb);
44134419
struct JsonCoercionState *jcstate;
44144420

44154421
if (!jbv) /* NULL or empty */
@@ -4424,12 +4430,12 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
44244430
jexpr->returning.typid == JSONBOID)
44254431
{
44264432
/* Use result coercion from json[b] to the output type */
4427-
res = JsonbPGetDatum(JsonItemToJsonb(jbv));
4433+
res = JsonItemToJsonxDatum(jbv, isjsonb);
44284434
break;
44294435
}
44304436

44314437
/* Use coercion from SQL/JSON item type to the output type */
4432-
res = ExecPrepareJsonItemCoercion(jbv,
4438+
res = ExecPrepareJsonItemCoercion(jbv, isjsonb,
44334439
&op->d.jsonexpr.jsexpr->returning,
44344440
&op->d.jsonexpr.coercions,
44354441
&jcstate);
@@ -4465,7 +4471,8 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
44654471

44664472
case IS_JSON_EXISTS:
44674473
*resnull = false;
4468-
return BoolGetDatum(JsonbPathExists(item, path, op->d.jsonexpr.args));
4474+
return BoolGetDatum(JsonPathExists(item, path, op->d.jsonexpr.args,
4475+
isjsonb));
44694476

44704477
default:
44714478
elog(ERROR, "unrecognized SQL/JSON expression op %d", jexpr->op);
@@ -4481,14 +4488,15 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
44814488

44824489
/* execute ON EMPTY behavior */
44834490
res = ExecEvalJsonBehavior(econtext, &jexpr->on_empty,
4484-
op->d.jsonexpr.default_on_empty, resnull);
4491+
op->d.jsonexpr.default_on_empty,
4492+
isjsonb, resnull);
44854493

44864494
/* result is already coerced in DEFAULT behavior case */
44874495
if (jexpr->on_empty.btype == JSON_BEHAVIOR_DEFAULT)
44884496
return res;
44894497
}
44904498

4491-
return ExecEvalJsonExprCoercion(op, econtext, res, resnull);
4499+
return ExecEvalJsonExprCoercion(op, econtext, res, resnull, isjsonb);
44924500
}
44934501

44944502
bool
@@ -4509,14 +4517,18 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45094517
Datum res = (Datum) 0;
45104518
JsonPath *path;
45114519
ListCell *lc;
4520+
Oid formattedType = exprType(jexpr->formatted_expr ?
4521+
jexpr->formatted_expr :
4522+
jexpr->raw_expr);
4523+
bool isjsonb = formattedType == JSONBOID;
45124524

45134525
*op->resnull = true; /* until we get a result */
45144526
*op->resvalue = (Datum) 0;
45154527

45164528
if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
45174529
{
45184530
/* execute domain checks for NULLs */
4519-
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
4531+
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull, isjsonb);
45204532

45214533
Assert(*op->resnull);
45224534
*op->resnull = true;
@@ -4539,7 +4551,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45394551
if (!ExecEvalJsonNeedsSubTransaction(jexpr))
45404552
{
45414553
/* No need to use PG_TRY/PG_CATCH with subtransactions. */
4542-
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
4554+
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item, isjsonb,
45434555
op->resnull);
45444556
}
45454557
else
@@ -4558,7 +4570,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45584570
PG_TRY();
45594571
{
45604572
res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
4561-
op->resnull);
4573+
isjsonb, op->resnull);
45624574

45634575
/* Commit the inner transaction, return to outer xact context */
45644576
ReleaseCurrentSubTransaction();
@@ -4585,12 +4597,13 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
45854597
/* Execute ON ERROR behavior. */
45864598
res = ExecEvalJsonBehavior(econtext, &jexpr->on_error,
45874599
op->d.jsonexpr.default_on_error,
4588-
op->resnull);
4600+
isjsonb, op->resnull);
45894601

45904602
if (jexpr->op != IS_JSON_EXISTS &&
45914603
/* result is already coerced in DEFAULT behavior case */
45924604
jexpr->on_error.btype != JSON_BEHAVIOR_DEFAULT)
4593-
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
4605+
res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull,
4606+
isjsonb);
45944607
}
45954608
PG_END_TRY();
45964609
}

src/backend/parser/parse_expr.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4773,13 +4773,10 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
47734773
JsonExpr *jsexpr = transformJsonExprCommon(pstate, func);
47744774
Node *contextItemExpr =
47754775
jsexpr->formatted_expr ? jsexpr->formatted_expr : jsexpr->raw_expr;
4776-
const char *func_name = NULL;
47774776

47784777
switch (func->op)
47794778
{
47804779
case IS_JSON_VALUE:
4781-
func_name = "JSON_VALUE";
4782-
47834780
transformJsonFuncExprOutput(pstate, func, jsexpr);
47844781

47854782
jsexpr->returning.format.type = JS_FORMAT_DEFAULT;
@@ -4800,8 +4797,6 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
48004797
break;
48014798

48024799
case IS_JSON_QUERY:
4803-
func_name = "JSON_QUERY";
4804-
48054800
transformJsonFuncExprOutput(pstate, func, jsexpr);
48064801

48074802
jsexpr->wrapper = func->wrapper;
@@ -4810,8 +4805,6 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
48104805
break;
48114806

48124807
case IS_JSON_EXISTS:
4813-
func_name = "JSON_EXISTS";
4814-
48154808
jsexpr->returning.format.type = JS_FORMAT_DEFAULT;
48164809
jsexpr->returning.format.encoding = JS_ENC_DEFAULT;
48174810
jsexpr->returning.format.location = -1;
@@ -4821,11 +4814,5 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
48214814
break;
48224815
}
48234816

4824-
if (exprType(contextItemExpr) != JSONBOID)
4825-
ereport(ERROR,
4826-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4827-
errmsg("%s() is not yet implemented for json type", func_name),
4828-
parser_errposition(pstate, func->location)));
4829-
48304817
return (Node *) jsexpr;
48314818
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,6 @@ static JsonbIteratorToken JsonxIteratorNext(JsonxIterator *it, JsonbValue *jbv,
362362
bool skipNested);
363363
static JsonbValue *JsonItemToJsonbValue(JsonItem *jsi, JsonbValue *jbv);
364364
static Jsonx *JsonbValueToJsonx(JsonbValue *jbv, bool isJsonb);
365-
static Datum JsonbValueToJsonxDatum(JsonbValue *jbv, bool isJsonb);
366-
static Datum JsonItemToJsonxDatum(JsonItem *jsi, bool isJsonb);
367365

368366
static bool tryToParseDatetime(text *fmt, text *datetime, char *tzname,
369367
bool strict, Datum *value, Oid *typid,
@@ -2992,15 +2990,15 @@ JsonbValueToJsonx(JsonbValue *jbv, bool isJsonb)
29922990
(Jsonx *) JsonbValueToJson(jbv);
29932991
}
29942992

2995-
static Datum
2993+
Datum
29962994
JsonbValueToJsonxDatum(JsonbValue *jbv, bool isJsonb)
29972995
{
29982996
return isJsonb ?
29992997
JsonbPGetDatum(JsonbValueToJsonb(jbv)) :
30002998
JsonPGetDatum(JsonbValueToJson(jbv));
30012999
}
30023000

3003-
static Datum
3001+
Datum
30043002
JsonItemToJsonxDatum(JsonItem *jsi, bool isJsonb)
30053003
{
30063004
JsonbValue jbv;
@@ -3335,29 +3333,29 @@ JsonItemInitDatetime(JsonItem *item, Datum val, Oid typid, int32 typmod, int tz)
33353333
/********************Interface to pgsql's executor***************************/
33363334

33373335
bool
3338-
JsonbPathExists(Datum jb, JsonPath *jp, List *vars)
3336+
JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool isJsonb)
33393337
{
3338+
Jsonx *js = DatumGetJsonxP(jb, isJsonb);
33403339
JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar,
3341-
(Jsonx *) DatumGetJsonbP(jb),
3342-
true, true, NULL);
3340+
js, isJsonb, true, NULL);
33433341

33443342
Assert(!jperIsError(res));
33453343

33463344
return res == jperOk;
33473345
}
33483346

33493347
Datum
3350-
JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
3351-
bool *empty, List *vars)
3348+
JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
3349+
List *vars, bool isJsonb)
33523350
{
3351+
Jsonx *js = DatumGetJsonxP(jb, isJsonb);
33533352
JsonItem *first;
33543353
bool wrap;
33553354
JsonValueList found = {0};
33563355
JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
33573356
int count;
33583357

3359-
jper = executeJsonPath(jp, vars, EvalJsonPathVar,
3360-
(Jsonx *) DatumGetJsonbP(jb), true, true, &found);
3358+
jper = executeJsonPath(jp, vars, EvalJsonPathVar, js, isJsonb, true, &found);
33613359
Assert(!jperIsError(jper));
33623360

33633361
count = JsonValueListLength(&found);
@@ -3382,7 +3380,11 @@ JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
33823380
}
33833381

33843382
if (wrap)
3385-
return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found, true)));
3383+
{
3384+
JsonbValue *arr = wrapItemsInArray(&found, isJsonb);
3385+
3386+
return JsonbValueToJsonxDatum(arr, isJsonb);
3387+
}
33863388

33873389
if (count > 1)
33883390
ereport(ERROR,
@@ -3393,22 +3395,22 @@ JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
33933395
"sequence into array")));
33943396

33953397
if (first)
3396-
return JsonbPGetDatum(JsonItemToJsonb(first));
3398+
return JsonItemToJsonxDatum(first, isJsonb);
33973399

33983400
*empty = true;
33993401
return PointerGetDatum(NULL);
34003402
}
34013403

34023404
JsonItem *
3403-
JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars)
3405+
JsonPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars, bool isJsonb)
34043406
{
3407+
Jsonx *js = DatumGetJsonxP(jb, isJsonb);
34053408
JsonItem *res;
34063409
JsonValueList found = { 0 };
34073410
JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
34083411
int count;
34093412

3410-
jper = executeJsonPath(jp, vars, EvalJsonPathVar,
3411-
(Jsonx *) DatumGetJsonbP(jb), true, true, &found);
3413+
jper = executeJsonPath(jp, vars, EvalJsonPathVar, js, isJsonb, true, &found);
34123414
Assert(!jperIsError(jper));
34133415

34143416
count = JsonValueListLength(&found);
@@ -3428,7 +3430,12 @@ JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars)
34283430

34293431
if (JsonItemIsBinary(res) &&
34303432
JsonContainerIsScalar(JsonItemBinary(res).data))
3431-
JsonbExtractScalar(JsonItemBinary(res).data, JsonItemJbv(res));
3433+
{
3434+
if (isJsonb)
3435+
JsonbExtractScalar(JsonItemBinary(res).data, JsonItemJbv(res));
3436+
else
3437+
JsonExtractScalar((JsonContainer *) JsonItemBinary(res).data, JsonItemJbv(res));
3438+
}
34323439

34333440
if (!JsonItemIsScalar(res))
34343441
ereport(ERROR,

0 commit comments

Comments
 (0)