Revert SQL/JSON features
The reverts the following and makes some associated cleanups: commit f79b803dc: Common SQL/JSON clauses commit f4fb45d15: SQL/JSON constructors commit 5f0adec25: Make STRING an unreserved_keyword. commit 33a377608: IS JSON predicate commit 1a36bc9db: SQL/JSON query functions commit 606948b05: SQL JSON functions commit 49082c2cc: RETURNING clause for JSON() and JSON_SCALAR() commit 4e34747c8: JSON_TABLE commit fadb48b00: PLAN clauses for JSON_TABLE commit 2ef6f11b0: Reduce running time of jsonb_sqljson test commit 14d3f24fa: Further improve jsonb_sqljson parallel test commit a6baa4bad: Documentation for SQL/JSON features commit b46bcf7a4: Improve readability of SQL/JSON documentation. commit 112fdb352: Fix finalization for json_objectagg and friends commit fcdb35c32: Fix transformJsonBehavior commit 4cd8717af: Improve a couple of sql/json error messages commit f7a605f63: Small cleanups in SQL/JSON code commit 9c3d25e17: Fix JSON_OBJECTAGG uniquefying bug commit a79153b7a: Claim SQL standard compliance for SQL/JSON features commit a1e7616d6: Rework SQL/JSON documentation commit 8d9f9634e: Fix errors in copyfuncs/equalfuncs support for JSON node types. commit 3c633f32b: Only allow returning string types or bytea from json_serialize commit 67b26703b: expression eval: Fix EEOP_JSON_CONSTRUCTOR and EEOP_JSONEXPR size. The release notes are also adjusted. Backpatch to release 15. Discussion: https://postgr.es/m/40d2c882-bcac-19a9-754d-4299e1d87ac7@postgresql.org
This commit is contained in:
parent
90247e742f
commit
2f2b18bd3f
File diff suppressed because it is too large
Load Diff
@ -157,7 +157,6 @@ INTERVAL
|
|||||||
INTO
|
INTO
|
||||||
IS
|
IS
|
||||||
JOIN
|
JOIN
|
||||||
JSON
|
|
||||||
JSON_ARRAY
|
JSON_ARRAY
|
||||||
JSON_ARRAYAGG
|
JSON_ARRAYAGG
|
||||||
JSON_EXISTS
|
JSON_EXISTS
|
||||||
|
@ -527,20 +527,20 @@ T652 SQL-dynamic statements in SQL routines NO
|
|||||||
T653 SQL-schema statements in external routines YES
|
T653 SQL-schema statements in external routines YES
|
||||||
T654 SQL-dynamic statements in external routines NO
|
T654 SQL-dynamic statements in external routines NO
|
||||||
T655 Cyclically dependent routines YES
|
T655 Cyclically dependent routines YES
|
||||||
T811 Basic SQL/JSON constructor functions YES
|
T811 Basic SQL/JSON constructor functions NO
|
||||||
T812 SQL/JSON: JSON_OBJECTAGG YES
|
T812 SQL/JSON: JSON_OBJECTAGG NO
|
||||||
T813 SQL/JSON: JSON_ARRAYAGG with ORDER BY YES
|
T813 SQL/JSON: JSON_ARRAYAGG with ORDER BY NO
|
||||||
T814 Colon in JSON_OBJECT or JSON_OBJECTAGG YES
|
T814 Colon in JSON_OBJECT or JSON_OBJECTAGG NO
|
||||||
T821 Basic SQL/JSON query operators YES
|
T821 Basic SQL/JSON query operators NO
|
||||||
T822 SQL/JSON: IS JSON WITH UNIQUE KEYS predicate YES
|
T822 SQL/JSON: IS JSON WITH UNIQUE KEYS predicate NO
|
||||||
T823 SQL/JSON: PASSING clause YES
|
T823 SQL/JSON: PASSING clause NO
|
||||||
T824 JSON_TABLE: specific PLAN clause YES
|
T824 JSON_TABLE: specific PLAN clause NO
|
||||||
T825 SQL/JSON: ON EMPTY and ON ERROR clauses YES
|
T825 SQL/JSON: ON EMPTY and ON ERROR clauses NO
|
||||||
T826 General value expression in ON ERROR or ON EMPTY clauses YES
|
T826 General value expression in ON ERROR or ON EMPTY clauses NO
|
||||||
T827 JSON_TABLE: sibling NESTED COLUMNS clauses YES
|
T827 JSON_TABLE: sibling NESTED COLUMNS clauses NO
|
||||||
T828 JSON_QUERY YES
|
T828 JSON_QUERY NO
|
||||||
T829 JSON_QUERY: array wrapper options YES
|
T829 JSON_QUERY: array wrapper options NO
|
||||||
T830 Enforcing unique keys in SQL/JSON constructor functions YES
|
T830 Enforcing unique keys in SQL/JSON constructor functions NO
|
||||||
T831 SQL/JSON path language: strict mode YES
|
T831 SQL/JSON path language: strict mode YES
|
||||||
T832 SQL/JSON path language: item method YES
|
T832 SQL/JSON path language: item method YES
|
||||||
T833 SQL/JSON path language: multiple subscripts YES
|
T833 SQL/JSON path language: multiple subscripts YES
|
||||||
@ -548,7 +548,7 @@ T834 SQL/JSON path language: wildcard member accessor YES
|
|||||||
T835 SQL/JSON path language: filter expressions YES
|
T835 SQL/JSON path language: filter expressions YES
|
||||||
T836 SQL/JSON path language: starts with predicate YES
|
T836 SQL/JSON path language: starts with predicate YES
|
||||||
T837 SQL/JSON path language: regex_like predicate YES
|
T837 SQL/JSON path language: regex_like predicate YES
|
||||||
T838 JSON_TABLE: PLAN DEFAULT clause YES
|
T838 JSON_TABLE: PLAN DEFAULT clause NO
|
||||||
T839 Formatted cast of datetimes to/from character strings NO
|
T839 Formatted cast of datetimes to/from character strings NO
|
||||||
M001 Datalinks NO
|
M001 Datalinks NO
|
||||||
M002 Datalinks via SQL/CLI NO
|
M002 Datalinks via SQL/CLI NO
|
||||||
|
@ -3851,13 +3851,7 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
|
|||||||
break;
|
break;
|
||||||
case T_TableFuncScan:
|
case T_TableFuncScan:
|
||||||
Assert(rte->rtekind == RTE_TABLEFUNC);
|
Assert(rte->rtekind == RTE_TABLEFUNC);
|
||||||
if (rte->tablefunc)
|
|
||||||
if (rte->tablefunc->functype == TFT_XMLTABLE)
|
|
||||||
objectname = "xmltable";
|
objectname = "xmltable";
|
||||||
else /* Must be TFT_JSON_TABLE */
|
|
||||||
objectname = "json_table";
|
|
||||||
else
|
|
||||||
objectname = NULL;
|
|
||||||
objecttag = "Table Function Name";
|
objecttag = "Table Function Name";
|
||||||
break;
|
break;
|
||||||
case T_ValuesScan:
|
case T_ValuesScan:
|
||||||
|
@ -47,9 +47,6 @@
|
|||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/json.h"
|
|
||||||
#include "utils/jsonb.h"
|
|
||||||
#include "utils/jsonpath.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
@ -88,40 +85,6 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
|
|||||||
bool nullcheck);
|
bool nullcheck);
|
||||||
|
|
||||||
|
|
||||||
static ExprState *
|
|
||||||
ExecInitExprInternal(Expr *node, PlanState *parent, ParamListInfo ext_params,
|
|
||||||
Datum *caseval, bool *casenull)
|
|
||||||
{
|
|
||||||
ExprState *state;
|
|
||||||
ExprEvalStep scratch = {0};
|
|
||||||
|
|
||||||
/* Special case: NULL expression produces a NULL ExprState pointer */
|
|
||||||
if (node == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Initialize ExprState with empty step list */
|
|
||||||
state = makeNode(ExprState);
|
|
||||||
state->expr = node;
|
|
||||||
state->parent = parent;
|
|
||||||
state->ext_params = ext_params;
|
|
||||||
state->innermost_caseval = caseval;
|
|
||||||
state->innermost_casenull = casenull;
|
|
||||||
|
|
||||||
/* Insert EEOP_*_FETCHSOME steps as needed */
|
|
||||||
ExecInitExprSlots(state, (Node *) node);
|
|
||||||
|
|
||||||
/* Compile the expression proper */
|
|
||||||
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
|
||||||
|
|
||||||
/* Finally, append a DONE step */
|
|
||||||
scratch.opcode = EEOP_DONE;
|
|
||||||
ExprEvalPushStep(state, &scratch);
|
|
||||||
|
|
||||||
ExecReadyExpr(state);
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExecInitExpr: prepare an expression tree for execution
|
* ExecInitExpr: prepare an expression tree for execution
|
||||||
*
|
*
|
||||||
@ -159,7 +122,32 @@ ExecInitExprInternal(Expr *node, PlanState *parent, ParamListInfo ext_params,
|
|||||||
ExprState *
|
ExprState *
|
||||||
ExecInitExpr(Expr *node, PlanState *parent)
|
ExecInitExpr(Expr *node, PlanState *parent)
|
||||||
{
|
{
|
||||||
return ExecInitExprInternal(node, parent, NULL, NULL, NULL);
|
ExprState *state;
|
||||||
|
ExprEvalStep scratch = {0};
|
||||||
|
|
||||||
|
/* Special case: NULL expression produces a NULL ExprState pointer */
|
||||||
|
if (node == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialize ExprState with empty step list */
|
||||||
|
state = makeNode(ExprState);
|
||||||
|
state->expr = node;
|
||||||
|
state->parent = parent;
|
||||||
|
state->ext_params = NULL;
|
||||||
|
|
||||||
|
/* Insert EEOP_*_FETCHSOME steps as needed */
|
||||||
|
ExecInitExprSlots(state, (Node *) node);
|
||||||
|
|
||||||
|
/* Compile the expression proper */
|
||||||
|
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
||||||
|
|
||||||
|
/* Finally, append a DONE step */
|
||||||
|
scratch.opcode = EEOP_DONE;
|
||||||
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
|
ExecReadyExpr(state);
|
||||||
|
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -171,20 +159,32 @@ ExecInitExpr(Expr *node, PlanState *parent)
|
|||||||
ExprState *
|
ExprState *
|
||||||
ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
|
ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
|
||||||
{
|
{
|
||||||
return ExecInitExprInternal(node, NULL, ext_params, NULL, NULL);
|
ExprState *state;
|
||||||
}
|
ExprEvalStep scratch = {0};
|
||||||
|
|
||||||
/*
|
/* Special case: NULL expression produces a NULL ExprState pointer */
|
||||||
* ExecInitExprWithCaseValue: prepare an expression tree for execution
|
if (node == NULL)
|
||||||
*
|
return NULL;
|
||||||
* This is the same as ExecInitExpr, except that a pointer to the value for
|
|
||||||
* CasTestExpr is passed here.
|
/* Initialize ExprState with empty step list */
|
||||||
*/
|
state = makeNode(ExprState);
|
||||||
ExprState *
|
state->expr = node;
|
||||||
ExecInitExprWithCaseValue(Expr *node, PlanState *parent,
|
state->parent = NULL;
|
||||||
Datum *caseval, bool *casenull)
|
state->ext_params = ext_params;
|
||||||
{
|
|
||||||
return ExecInitExprInternal(node, parent, NULL, caseval, casenull);
|
/* Insert EEOP_*_FETCHSOME steps as needed */
|
||||||
|
ExecInitExprSlots(state, (Node *) node);
|
||||||
|
|
||||||
|
/* Compile the expression proper */
|
||||||
|
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
|
||||||
|
|
||||||
|
/* Finally, append a DONE step */
|
||||||
|
scratch.opcode = EEOP_DONE;
|
||||||
|
ExprEvalPushStep(state, &scratch);
|
||||||
|
|
||||||
|
ExecReadyExpr(state);
|
||||||
|
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2411,263 +2411,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
|
||||||
|
|
||||||
ExecInitExprRec(jve->raw_expr, state, resv, resnull);
|
|
||||||
|
|
||||||
if (jve->formatted_expr)
|
|
||||||
{
|
|
||||||
Datum *innermost_caseval = state->innermost_caseval;
|
|
||||||
bool *innermost_isnull = state->innermost_casenull;
|
|
||||||
|
|
||||||
state->innermost_caseval = resv;
|
|
||||||
state->innermost_casenull = resnull;
|
|
||||||
|
|
||||||
ExecInitExprRec(jve->formatted_expr, state, resv, resnull);
|
|
||||||
|
|
||||||
state->innermost_caseval = innermost_caseval;
|
|
||||||
state->innermost_casenull = innermost_isnull;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
|
||||||
List *args = ctor->args;
|
|
||||||
ListCell *lc;
|
|
||||||
int nargs = list_length(args);
|
|
||||||
int argno = 0;
|
|
||||||
|
|
||||||
if (ctor->func)
|
|
||||||
{
|
|
||||||
ExecInitExprRec(ctor->func, state, resv, resnull);
|
|
||||||
}
|
|
||||||
else if ((ctor->type == JSCTOR_JSON_PARSE && !ctor->unique) ||
|
|
||||||
ctor->type == JSCTOR_JSON_SERIALIZE)
|
|
||||||
{
|
|
||||||
/* Use the value of the first argument as a result */
|
|
||||||
ExecInitExprRec(linitial(args), state, resv, resnull);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonConstructorExprState *jcstate;
|
|
||||||
|
|
||||||
jcstate = palloc0(sizeof(JsonConstructorExprState));
|
|
||||||
|
|
||||||
scratch.opcode = EEOP_JSON_CONSTRUCTOR;
|
|
||||||
scratch.d.json_constructor.jcstate = jcstate;
|
|
||||||
|
|
||||||
jcstate->constructor = ctor;
|
|
||||||
jcstate->arg_values = palloc(sizeof(Datum) * nargs);
|
|
||||||
jcstate->arg_nulls = palloc(sizeof(bool) * nargs);
|
|
||||||
jcstate->arg_types = palloc(sizeof(Oid) * nargs);
|
|
||||||
jcstate->nargs = nargs;
|
|
||||||
|
|
||||||
foreach(lc, args)
|
|
||||||
{
|
|
||||||
Expr *arg = (Expr *) lfirst(lc);
|
|
||||||
|
|
||||||
jcstate->arg_types[argno] = exprType((Node *) arg);
|
|
||||||
|
|
||||||
if (IsA(arg, Const))
|
|
||||||
{
|
|
||||||
/* Don't evaluate const arguments every round */
|
|
||||||
Const *con = (Const *) arg;
|
|
||||||
|
|
||||||
jcstate->arg_values[argno] = con->constvalue;
|
|
||||||
jcstate->arg_nulls[argno] = con->constisnull;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ExecInitExprRec(arg, state,
|
|
||||||
&jcstate->arg_values[argno],
|
|
||||||
&jcstate->arg_nulls[argno]);
|
|
||||||
}
|
|
||||||
argno++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* prepare type cache for datum_to_json[b]() */
|
|
||||||
if (ctor->type == JSCTOR_JSON_SCALAR)
|
|
||||||
{
|
|
||||||
bool is_jsonb =
|
|
||||||
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
|
||||||
|
|
||||||
jcstate->arg_type_cache =
|
|
||||||
palloc(sizeof(*jcstate->arg_type_cache) * nargs);
|
|
||||||
|
|
||||||
for (int i = 0; i < nargs; i++)
|
|
||||||
{
|
|
||||||
int category;
|
|
||||||
Oid outfuncid;
|
|
||||||
Oid typid = jcstate->arg_types[i];
|
|
||||||
|
|
||||||
if (is_jsonb)
|
|
||||||
{
|
|
||||||
JsonbTypeCategory jbcat;
|
|
||||||
|
|
||||||
jsonb_categorize_type(typid, &jbcat, &outfuncid);
|
|
||||||
|
|
||||||
category = (int) jbcat;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonTypeCategory jscat;
|
|
||||||
|
|
||||||
json_categorize_type(typid, &jscat, &outfuncid);
|
|
||||||
|
|
||||||
category = (int) jscat;
|
|
||||||
}
|
|
||||||
|
|
||||||
jcstate->arg_type_cache[i].outfuncid = outfuncid;
|
|
||||||
jcstate->arg_type_cache[i].category = category;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprEvalPushStep(state, &scratch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor->coercion)
|
|
||||||
{
|
|
||||||
Datum *innermost_caseval = state->innermost_caseval;
|
|
||||||
bool *innermost_isnull = state->innermost_casenull;
|
|
||||||
|
|
||||||
state->innermost_caseval = resv;
|
|
||||||
state->innermost_casenull = resnull;
|
|
||||||
|
|
||||||
ExecInitExprRec(ctor->coercion, state, resv, resnull);
|
|
||||||
|
|
||||||
state->innermost_caseval = innermost_caseval;
|
|
||||||
state->innermost_casenull = innermost_isnull;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
{
|
|
||||||
JsonIsPredicate *pred = (JsonIsPredicate *) node;
|
|
||||||
|
|
||||||
ExecInitExprRec((Expr *) pred->expr, state, resv, resnull);
|
|
||||||
|
|
||||||
scratch.opcode = EEOP_IS_JSON;
|
|
||||||
scratch.d.is_json.pred = pred;
|
|
||||||
|
|
||||||
ExprEvalPushStep(state, &scratch);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = castNode(JsonExpr, node);
|
|
||||||
JsonExprState *jsestate = palloc0(sizeof(JsonExprState));
|
|
||||||
ListCell *argexprlc;
|
|
||||||
ListCell *argnamelc;
|
|
||||||
|
|
||||||
scratch.opcode = EEOP_JSONEXPR;
|
|
||||||
scratch.d.jsonexpr.jsestate = jsestate;
|
|
||||||
|
|
||||||
jsestate->jsexpr = jexpr;
|
|
||||||
|
|
||||||
jsestate->formatted_expr =
|
|
||||||
palloc(sizeof(*jsestate->formatted_expr));
|
|
||||||
|
|
||||||
ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
|
|
||||||
&jsestate->formatted_expr->value,
|
|
||||||
&jsestate->formatted_expr->isnull);
|
|
||||||
|
|
||||||
jsestate->pathspec =
|
|
||||||
palloc(sizeof(*jsestate->pathspec));
|
|
||||||
|
|
||||||
ExecInitExprRec((Expr *) jexpr->path_spec, state,
|
|
||||||
&jsestate->pathspec->value,
|
|
||||||
&jsestate->pathspec->isnull);
|
|
||||||
|
|
||||||
jsestate->res_expr =
|
|
||||||
palloc(sizeof(*jsestate->res_expr));
|
|
||||||
|
|
||||||
jsestate->result_expr = jexpr->result_coercion
|
|
||||||
? ExecInitExprWithCaseValue((Expr *) jexpr->result_coercion->expr,
|
|
||||||
state->parent,
|
|
||||||
&jsestate->res_expr->value,
|
|
||||||
&jsestate->res_expr->isnull)
|
|
||||||
: NULL;
|
|
||||||
|
|
||||||
jsestate->default_on_empty = !jexpr->on_empty ? NULL :
|
|
||||||
ExecInitExpr((Expr *) jexpr->on_empty->default_expr,
|
|
||||||
state->parent);
|
|
||||||
|
|
||||||
jsestate->default_on_error =
|
|
||||||
ExecInitExpr((Expr *) jexpr->on_error->default_expr,
|
|
||||||
state->parent);
|
|
||||||
|
|
||||||
if (jexpr->omit_quotes ||
|
|
||||||
(jexpr->result_coercion && jexpr->result_coercion->via_io))
|
|
||||||
{
|
|
||||||
Oid typinput;
|
|
||||||
|
|
||||||
/* lookup the result type's input function */
|
|
||||||
getTypeInputInfo(jexpr->returning->typid, &typinput,
|
|
||||||
&jsestate->input.typioparam);
|
|
||||||
fmgr_info(typinput, &jsestate->input.func);
|
|
||||||
}
|
|
||||||
|
|
||||||
jsestate->args = NIL;
|
|
||||||
|
|
||||||
forboth(argexprlc, jexpr->passing_values,
|
|
||||||
argnamelc, jexpr->passing_names)
|
|
||||||
{
|
|
||||||
Expr *argexpr = (Expr *) lfirst(argexprlc);
|
|
||||||
String *argname = lfirst_node(String, argnamelc);
|
|
||||||
JsonPathVariableEvalContext *var = palloc(sizeof(*var));
|
|
||||||
|
|
||||||
var->name = pstrdup(argname->sval);
|
|
||||||
var->typid = exprType((Node *) argexpr);
|
|
||||||
var->typmod = exprTypmod((Node *) argexpr);
|
|
||||||
var->estate = ExecInitExpr(argexpr, state->parent);
|
|
||||||
var->econtext = NULL;
|
|
||||||
var->mcxt = NULL;
|
|
||||||
var->evaluated = false;
|
|
||||||
var->value = (Datum) 0;
|
|
||||||
var->isnull = true;
|
|
||||||
|
|
||||||
jsestate->args =
|
|
||||||
lappend(jsestate->args, var);
|
|
||||||
}
|
|
||||||
|
|
||||||
jsestate->cache = NULL;
|
|
||||||
|
|
||||||
if (jexpr->coercions)
|
|
||||||
{
|
|
||||||
JsonCoercion **coercion;
|
|
||||||
struct JsonCoercionState *cstate;
|
|
||||||
Datum *caseval;
|
|
||||||
bool *casenull;
|
|
||||||
|
|
||||||
jsestate->coercion_expr =
|
|
||||||
palloc(sizeof(*jsestate->coercion_expr));
|
|
||||||
|
|
||||||
caseval = &jsestate->coercion_expr->value;
|
|
||||||
casenull = &jsestate->coercion_expr->isnull;
|
|
||||||
|
|
||||||
for (cstate = &jsestate->coercions.null,
|
|
||||||
coercion = &jexpr->coercions->null;
|
|
||||||
coercion <= &jexpr->coercions->composite;
|
|
||||||
coercion++, cstate++)
|
|
||||||
{
|
|
||||||
cstate->coercion = *coercion;
|
|
||||||
cstate->estate = *coercion ?
|
|
||||||
ExecInitExprWithCaseValue((Expr *) (*coercion)->expr,
|
|
||||||
state->parent,
|
|
||||||
caseval, casenull) : NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ExprEvalPushStep(state, &scratch);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized node type: %d",
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
(int) nodeTag(node));
|
(int) nodeTag(node));
|
||||||
|
@ -57,31 +57,22 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/heaptoast.h"
|
#include "access/heaptoast.h"
|
||||||
#include "access/xact.h"
|
|
||||||
#include "catalog/pg_proc.h"
|
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/sequence.h"
|
#include "commands/sequence.h"
|
||||||
#include "executor/execExpr.h"
|
#include "executor/execExpr.h"
|
||||||
#include "executor/nodeSubplan.h"
|
#include "executor/nodeSubplan.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "parser/parse_expr.h"
|
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/date.h"
|
#include "utils/date.h"
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/expandedrecord.h"
|
#include "utils/expandedrecord.h"
|
||||||
#include "utils/json.h"
|
|
||||||
#include "utils/jsonb.h"
|
|
||||||
#include "utils/jsonfuncs.h"
|
|
||||||
#include "utils/jsonpath.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/resowner.h"
|
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
#include "utils/xml.h"
|
#include "utils/xml.h"
|
||||||
@ -488,9 +479,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
&&CASE_EEOP_GROUPING_FUNC,
|
&&CASE_EEOP_GROUPING_FUNC,
|
||||||
&&CASE_EEOP_WINDOW_FUNC,
|
&&CASE_EEOP_WINDOW_FUNC,
|
||||||
&&CASE_EEOP_SUBPLAN,
|
&&CASE_EEOP_SUBPLAN,
|
||||||
&&CASE_EEOP_JSON_CONSTRUCTOR,
|
|
||||||
&&CASE_EEOP_IS_JSON,
|
|
||||||
&&CASE_EEOP_JSONEXPR,
|
|
||||||
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
|
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
|
||||||
&&CASE_EEOP_AGG_DESERIALIZE,
|
&&CASE_EEOP_AGG_DESERIALIZE,
|
||||||
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
|
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
|
||||||
@ -1824,27 +1812,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
|||||||
{
|
{
|
||||||
/* too complex for an inline implementation */
|
/* too complex for an inline implementation */
|
||||||
ExecEvalAggOrderedTransTuple(state, op, econtext);
|
ExecEvalAggOrderedTransTuple(state, op, econtext);
|
||||||
EEO_NEXT();
|
|
||||||
}
|
|
||||||
|
|
||||||
EEO_CASE(EEOP_JSON_CONSTRUCTOR)
|
|
||||||
{
|
|
||||||
/* too complex for an inline implementation */
|
|
||||||
ExecEvalJsonConstructor(state, op, econtext);
|
|
||||||
EEO_NEXT();
|
|
||||||
}
|
|
||||||
|
|
||||||
EEO_CASE(EEOP_IS_JSON)
|
|
||||||
{
|
|
||||||
/* too complex for an inline implementation */
|
|
||||||
ExecEvalJsonIsPredicate(state, op);
|
|
||||||
EEO_NEXT();
|
|
||||||
}
|
|
||||||
|
|
||||||
EEO_CASE(EEOP_JSONEXPR)
|
|
||||||
{
|
|
||||||
/* too complex for an inline implementation */
|
|
||||||
ExecEvalJson(state, op, econtext);
|
|
||||||
EEO_NEXT();
|
EEO_NEXT();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3972,91 +3940,6 @@ ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op)
|
|
||||||
{
|
|
||||||
JsonIsPredicate *pred = op->d.is_json.pred;
|
|
||||||
Datum js = *op->resvalue;
|
|
||||||
Oid exprtype;
|
|
||||||
bool res;
|
|
||||||
|
|
||||||
if (*op->resnull)
|
|
||||||
{
|
|
||||||
*op->resvalue = BoolGetDatum(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
exprtype = exprType(pred->expr);
|
|
||||||
|
|
||||||
if (exprtype == TEXTOID || exprtype == JSONOID)
|
|
||||||
{
|
|
||||||
text *json = DatumGetTextP(js);
|
|
||||||
|
|
||||||
if (pred->item_type == JS_TYPE_ANY)
|
|
||||||
res = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (json_get_first_token(json, false))
|
|
||||||
{
|
|
||||||
case JSON_TOKEN_OBJECT_START:
|
|
||||||
res = pred->item_type == JS_TYPE_OBJECT;
|
|
||||||
break;
|
|
||||||
case JSON_TOKEN_ARRAY_START:
|
|
||||||
res = pred->item_type == JS_TYPE_ARRAY;
|
|
||||||
break;
|
|
||||||
case JSON_TOKEN_STRING:
|
|
||||||
case JSON_TOKEN_NUMBER:
|
|
||||||
case JSON_TOKEN_TRUE:
|
|
||||||
case JSON_TOKEN_FALSE:
|
|
||||||
case JSON_TOKEN_NULL:
|
|
||||||
res = pred->item_type == JS_TYPE_SCALAR;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Do full parsing pass only for uniqueness check or for JSON text
|
|
||||||
* validation.
|
|
||||||
*/
|
|
||||||
if (res && (pred->unique_keys || exprtype == TEXTOID))
|
|
||||||
res = json_validate(json, pred->unique_keys, false);
|
|
||||||
}
|
|
||||||
else if (exprtype == JSONBOID)
|
|
||||||
{
|
|
||||||
if (pred->item_type == JS_TYPE_ANY)
|
|
||||||
res = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Jsonb *jb = DatumGetJsonbP(js);
|
|
||||||
|
|
||||||
switch (pred->item_type)
|
|
||||||
{
|
|
||||||
case JS_TYPE_OBJECT:
|
|
||||||
res = JB_ROOT_IS_OBJECT(jb);
|
|
||||||
break;
|
|
||||||
case JS_TYPE_ARRAY:
|
|
||||||
res = JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb);
|
|
||||||
break;
|
|
||||||
case JS_TYPE_SCALAR:
|
|
||||||
res = JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
res = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Key uniqueness check is redundant for jsonb */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
res = false;
|
|
||||||
|
|
||||||
*op->resvalue = BoolGetDatum(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ExecEvalGroupingFunc
|
* ExecEvalGroupingFunc
|
||||||
*
|
*
|
||||||
@ -4619,629 +4502,3 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
|
|||||||
|
|
||||||
MemoryContextSwitchTo(oldContext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate a JSON constructor expression.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
|
||||||
ExprContext *econtext)
|
|
||||||
{
|
|
||||||
Datum res;
|
|
||||||
JsonConstructorExprState *jcstate = op->d.json_constructor.jcstate;
|
|
||||||
JsonConstructorExpr *ctor = jcstate->constructor;
|
|
||||||
bool is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
|
||||||
bool isnull = false;
|
|
||||||
|
|
||||||
if (ctor->type == JSCTOR_JSON_ARRAY)
|
|
||||||
res = (is_jsonb ?
|
|
||||||
jsonb_build_array_worker :
|
|
||||||
json_build_array_worker) (jcstate->nargs,
|
|
||||||
jcstate->arg_values,
|
|
||||||
jcstate->arg_nulls,
|
|
||||||
jcstate->arg_types,
|
|
||||||
ctor->absent_on_null);
|
|
||||||
else if (ctor->type == JSCTOR_JSON_OBJECT)
|
|
||||||
res = (is_jsonb ?
|
|
||||||
jsonb_build_object_worker :
|
|
||||||
json_build_object_worker) (jcstate->nargs,
|
|
||||||
jcstate->arg_values,
|
|
||||||
jcstate->arg_nulls,
|
|
||||||
jcstate->arg_types,
|
|
||||||
ctor->absent_on_null,
|
|
||||||
ctor->unique);
|
|
||||||
else if (ctor->type == JSCTOR_JSON_SCALAR)
|
|
||||||
{
|
|
||||||
if (jcstate->arg_nulls[0])
|
|
||||||
{
|
|
||||||
res = (Datum) 0;
|
|
||||||
isnull = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Datum value = jcstate->arg_values[0];
|
|
||||||
int category = jcstate->arg_type_cache[0].category;
|
|
||||||
Oid outfuncid = jcstate->arg_type_cache[0].outfuncid;
|
|
||||||
|
|
||||||
if (is_jsonb)
|
|
||||||
res = to_jsonb_worker(value, category, outfuncid);
|
|
||||||
else
|
|
||||||
res = to_json_worker(value, category, outfuncid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ctor->type == JSCTOR_JSON_PARSE)
|
|
||||||
{
|
|
||||||
if (jcstate->arg_nulls[0])
|
|
||||||
{
|
|
||||||
res = (Datum) 0;
|
|
||||||
isnull = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Datum value = jcstate->arg_values[0];
|
|
||||||
text *js = DatumGetTextP(value);
|
|
||||||
|
|
||||||
if (is_jsonb)
|
|
||||||
res = jsonb_from_text(js, true);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(void) json_validate(js, true, true);
|
|
||||||
res = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res = (Datum) 0;
|
|
||||||
elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
*op->resvalue = res;
|
|
||||||
*op->resnull = isnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate a JSON error/empty behavior result.
|
|
||||||
*/
|
|
||||||
static Datum
|
|
||||||
ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
|
|
||||||
ExprState *default_estate, bool *is_null)
|
|
||||||
{
|
|
||||||
*is_null = false;
|
|
||||||
|
|
||||||
switch (behavior->btype)
|
|
||||||
{
|
|
||||||
case JSON_BEHAVIOR_EMPTY_ARRAY:
|
|
||||||
return JsonbPGetDatum(JsonbMakeEmptyArray());
|
|
||||||
|
|
||||||
case JSON_BEHAVIOR_EMPTY_OBJECT:
|
|
||||||
return JsonbPGetDatum(JsonbMakeEmptyObject());
|
|
||||||
|
|
||||||
case JSON_BEHAVIOR_TRUE:
|
|
||||||
return BoolGetDatum(true);
|
|
||||||
|
|
||||||
case JSON_BEHAVIOR_FALSE:
|
|
||||||
return BoolGetDatum(false);
|
|
||||||
|
|
||||||
case JSON_BEHAVIOR_NULL:
|
|
||||||
case JSON_BEHAVIOR_UNKNOWN:
|
|
||||||
case JSON_BEHAVIOR_EMPTY:
|
|
||||||
*is_null = true;
|
|
||||||
return (Datum) 0;
|
|
||||||
|
|
||||||
case JSON_BEHAVIOR_DEFAULT:
|
|
||||||
return ExecEvalExpr(default_estate, econtext, is_null);
|
|
||||||
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unrecognized SQL/JSON behavior %d", behavior->btype);
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate a coercion of a JSON item to the target type.
|
|
||||||
*/
|
|
||||||
static Datum
|
|
||||||
ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
|
|
||||||
Datum res, bool *isNull, void *p, bool *error)
|
|
||||||
{
|
|
||||||
ExprState *estate = p;
|
|
||||||
JsonExprState *jsestate;
|
|
||||||
|
|
||||||
if (estate) /* coerce using specified expression */
|
|
||||||
return ExecEvalExpr(estate, econtext, isNull);
|
|
||||||
|
|
||||||
jsestate = op->d.jsonexpr.jsestate;
|
|
||||||
|
|
||||||
if (jsestate->jsexpr->op != JSON_EXISTS_OP)
|
|
||||||
{
|
|
||||||
JsonCoercion *coercion = jsestate->jsexpr->result_coercion;
|
|
||||||
JsonExpr *jexpr = jsestate->jsexpr;
|
|
||||||
Jsonb *jb = *isNull ? NULL : DatumGetJsonbP(res);
|
|
||||||
|
|
||||||
if ((coercion && coercion->via_io) ||
|
|
||||||
(jexpr->omit_quotes && !*isNull &&
|
|
||||||
JB_ROOT_IS_SCALAR(jb)))
|
|
||||||
{
|
|
||||||
/* strip quotes and call typinput function */
|
|
||||||
char *str = *isNull ? NULL : JsonbUnquote(jb);
|
|
||||||
|
|
||||||
return InputFunctionCall(&jsestate->input.func, str,
|
|
||||||
jsestate->input.typioparam,
|
|
||||||
jexpr->returning->typmod);
|
|
||||||
}
|
|
||||||
else if (coercion && coercion->via_populate)
|
|
||||||
return json_populate_type(res, JSONBOID,
|
|
||||||
jexpr->returning->typid,
|
|
||||||
jexpr->returning->typmod,
|
|
||||||
&jsestate->cache,
|
|
||||||
econtext->ecxt_per_query_memory,
|
|
||||||
isNull);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsestate->result_expr)
|
|
||||||
{
|
|
||||||
jsestate->res_expr->value = res;
|
|
||||||
jsestate->res_expr->isnull = *isNull;
|
|
||||||
|
|
||||||
res = ExecEvalExpr(jsestate->result_expr, econtext, isNull);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate a JSON path variable caching computed value.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
EvalJsonPathVar(void *cxt, char *varName, int varNameLen,
|
|
||||||
JsonbValue *val, JsonbValue *baseObject)
|
|
||||||
{
|
|
||||||
JsonPathVariableEvalContext *var = NULL;
|
|
||||||
List *vars = cxt;
|
|
||||||
ListCell *lc;
|
|
||||||
int id = 1;
|
|
||||||
|
|
||||||
if (!varName)
|
|
||||||
return list_length(vars);
|
|
||||||
|
|
||||||
foreach(lc, vars)
|
|
||||||
{
|
|
||||||
var = lfirst(lc);
|
|
||||||
|
|
||||||
if (!strncmp(var->name, varName, varNameLen))
|
|
||||||
break;
|
|
||||||
|
|
||||||
var = NULL;
|
|
||||||
id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!var)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!var->evaluated)
|
|
||||||
{
|
|
||||||
MemoryContext oldcxt = var->mcxt ?
|
|
||||||
MemoryContextSwitchTo(var->mcxt) : NULL;
|
|
||||||
|
|
||||||
var->value = ExecEvalExpr(var->estate, var->econtext, &var->isnull);
|
|
||||||
var->evaluated = true;
|
|
||||||
|
|
||||||
if (oldcxt)
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (var->isnull)
|
|
||||||
{
|
|
||||||
val->type = jbvNull;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonItemFromDatum(var->value, var->typid, var->typmod, val);
|
|
||||||
|
|
||||||
*baseObject = *val;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prepare SQL/JSON item coercion to the output type. Returned a datum of the
|
|
||||||
* corresponding SQL type and a pointer to the coercion state.
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
ExecPrepareJsonItemCoercion(JsonbValue *item,
|
|
||||||
JsonReturning *returning,
|
|
||||||
struct JsonCoercionsState *coercions,
|
|
||||||
struct JsonCoercionState **pcoercion)
|
|
||||||
{
|
|
||||||
struct JsonCoercionState *coercion;
|
|
||||||
Datum res;
|
|
||||||
JsonbValue buf;
|
|
||||||
|
|
||||||
if (item->type == jbvBinary &&
|
|
||||||
JsonContainerIsScalar(item->val.binary.data))
|
|
||||||
{
|
|
||||||
bool res PG_USED_FOR_ASSERTS_ONLY;
|
|
||||||
|
|
||||||
res = JsonbExtractScalar(item->val.binary.data, &buf);
|
|
||||||
item = &buf;
|
|
||||||
Assert(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get coercion state reference and datum of the corresponding SQL type */
|
|
||||||
switch (item->type)
|
|
||||||
{
|
|
||||||
case jbvNull:
|
|
||||||
coercion = &coercions->null;
|
|
||||||
res = (Datum) 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jbvString:
|
|
||||||
coercion = &coercions->string;
|
|
||||||
res = PointerGetDatum(cstring_to_text_with_len(item->val.string.val,
|
|
||||||
item->val.string.len));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jbvNumeric:
|
|
||||||
coercion = &coercions->numeric;
|
|
||||||
res = NumericGetDatum(item->val.numeric);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jbvBool:
|
|
||||||
coercion = &coercions->boolean;
|
|
||||||
res = BoolGetDatum(item->val.boolean);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jbvDatetime:
|
|
||||||
res = item->val.datetime.value;
|
|
||||||
switch (item->val.datetime.typid)
|
|
||||||
{
|
|
||||||
case DATEOID:
|
|
||||||
coercion = &coercions->date;
|
|
||||||
break;
|
|
||||||
case TIMEOID:
|
|
||||||
coercion = &coercions->time;
|
|
||||||
break;
|
|
||||||
case TIMETZOID:
|
|
||||||
coercion = &coercions->timetz;
|
|
||||||
break;
|
|
||||||
case TIMESTAMPOID:
|
|
||||||
coercion = &coercions->timestamp;
|
|
||||||
break;
|
|
||||||
case TIMESTAMPTZOID:
|
|
||||||
coercion = &coercions->timestamptz;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unexpected jsonb datetime type oid %u",
|
|
||||||
item->val.datetime.typid);
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jbvArray:
|
|
||||||
case jbvObject:
|
|
||||||
case jbvBinary:
|
|
||||||
coercion = &coercions->composite;
|
|
||||||
res = JsonbPGetDatum(JsonbValueToJsonb(item));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unexpected jsonb value type %d", item->type);
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*pcoercion = coercion;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef Datum (*JsonFunc) (ExprEvalStep *op, ExprContext *econtext,
|
|
||||||
Datum item, bool *resnull, void *p, bool *error);
|
|
||||||
|
|
||||||
static Datum
|
|
||||||
ExecEvalJsonExprSubtrans(JsonFunc func, ExprEvalStep *op,
|
|
||||||
ExprContext *econtext,
|
|
||||||
Datum res, bool *resnull,
|
|
||||||
void *p, bool *error, bool subtrans)
|
|
||||||
{
|
|
||||||
MemoryContext oldcontext;
|
|
||||||
ResourceOwner oldowner;
|
|
||||||
|
|
||||||
if (!subtrans)
|
|
||||||
/* No need to use subtransactions. */
|
|
||||||
return func(op, econtext, res, resnull, p, error);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
|
|
||||||
* execute the corresponding ON ERROR behavior then.
|
|
||||||
*/
|
|
||||||
oldcontext = CurrentMemoryContext;
|
|
||||||
oldowner = CurrentResourceOwner;
|
|
||||||
|
|
||||||
Assert(error);
|
|
||||||
|
|
||||||
BeginInternalSubTransaction(NULL);
|
|
||||||
/* Want to execute expressions inside function's memory context */
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
PG_TRY();
|
|
||||||
{
|
|
||||||
res = func(op, econtext, res, resnull, p, error);
|
|
||||||
|
|
||||||
/* Commit the inner transaction, return to outer xact context */
|
|
||||||
ReleaseCurrentSubTransaction();
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
CurrentResourceOwner = oldowner;
|
|
||||||
}
|
|
||||||
PG_CATCH();
|
|
||||||
{
|
|
||||||
ErrorData *edata;
|
|
||||||
int ecategory;
|
|
||||||
|
|
||||||
/* Save error info in oldcontext */
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
edata = CopyErrorData();
|
|
||||||
FlushErrorState();
|
|
||||||
|
|
||||||
/* Abort the inner transaction */
|
|
||||||
RollbackAndReleaseCurrentSubTransaction();
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
CurrentResourceOwner = oldowner;
|
|
||||||
|
|
||||||
ecategory = ERRCODE_TO_CATEGORY(edata->sqlerrcode);
|
|
||||||
|
|
||||||
if (ecategory != ERRCODE_DATA_EXCEPTION && /* jsonpath and other data
|
|
||||||
* errors */
|
|
||||||
ecategory != ERRCODE_INTEGRITY_CONSTRAINT_VIOLATION) /* domain errors */
|
|
||||||
ReThrowError(edata);
|
|
||||||
|
|
||||||
res = (Datum) 0;
|
|
||||||
*error = true;
|
|
||||||
}
|
|
||||||
PG_END_TRY();
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
JsonPath *path;
|
|
||||||
bool *error;
|
|
||||||
bool coercionInSubtrans;
|
|
||||||
} ExecEvalJsonExprContext;
|
|
||||||
|
|
||||||
static Datum
|
|
||||||
ExecEvalJsonExpr(ExprEvalStep *op, ExprContext *econtext,
|
|
||||||
Datum item, bool *resnull, void *pcxt,
|
|
||||||
bool *error)
|
|
||||||
{
|
|
||||||
ExecEvalJsonExprContext *cxt = pcxt;
|
|
||||||
JsonPath *path = cxt->path;
|
|
||||||
JsonExprState *jsestate = op->d.jsonexpr.jsestate;
|
|
||||||
JsonExpr *jexpr = jsestate->jsexpr;
|
|
||||||
ExprState *estate = NULL;
|
|
||||||
bool empty = false;
|
|
||||||
Datum res = (Datum) 0;
|
|
||||||
|
|
||||||
switch (jexpr->op)
|
|
||||||
{
|
|
||||||
case JSON_QUERY_OP:
|
|
||||||
res = JsonPathQuery(item, path, jexpr->wrapper, &empty, error,
|
|
||||||
jsestate->args);
|
|
||||||
if (error && *error)
|
|
||||||
{
|
|
||||||
*resnull = true;
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
*resnull = !DatumGetPointer(res);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JSON_VALUE_OP:
|
|
||||||
{
|
|
||||||
struct JsonCoercionState *jcstate;
|
|
||||||
JsonbValue *jbv = JsonPathValue(item, path, &empty, error,
|
|
||||||
jsestate->args);
|
|
||||||
|
|
||||||
if (error && *error)
|
|
||||||
return (Datum) 0;
|
|
||||||
|
|
||||||
if (!jbv) /* NULL or empty */
|
|
||||||
break;
|
|
||||||
|
|
||||||
Assert(!empty);
|
|
||||||
|
|
||||||
*resnull = false;
|
|
||||||
|
|
||||||
/* coerce scalar item to the output type */
|
|
||||||
if (jexpr->returning->typid == JSONOID ||
|
|
||||||
jexpr->returning->typid == JSONBOID)
|
|
||||||
{
|
|
||||||
/* Use result coercion from json[b] to the output type */
|
|
||||||
res = JsonbPGetDatum(JsonbValueToJsonb(jbv));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use coercion from SQL/JSON item type to the output type */
|
|
||||||
res = ExecPrepareJsonItemCoercion(jbv,
|
|
||||||
jsestate->jsexpr->returning,
|
|
||||||
&jsestate->coercions,
|
|
||||||
&jcstate);
|
|
||||||
|
|
||||||
if (jcstate->coercion &&
|
|
||||||
(jcstate->coercion->via_io ||
|
|
||||||
jcstate->coercion->via_populate))
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Coercion via I/O means here that the cast to the target
|
|
||||||
* type simply does not exist.
|
|
||||||
*/
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE),
|
|
||||||
errmsg("SQL/JSON item cannot be cast to target type")));
|
|
||||||
}
|
|
||||||
else if (!jcstate->estate)
|
|
||||||
return res; /* no coercion */
|
|
||||||
|
|
||||||
/* coerce using specific expression */
|
|
||||||
estate = jcstate->estate;
|
|
||||||
jsestate->coercion_expr->value = res;
|
|
||||||
jsestate->coercion_expr->isnull = *resnull;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JSON_EXISTS_OP:
|
|
||||||
{
|
|
||||||
bool exists = JsonPathExists(item, path,
|
|
||||||
jsestate->args,
|
|
||||||
error);
|
|
||||||
|
|
||||||
*resnull = error && *error;
|
|
||||||
res = BoolGetDatum(exists);
|
|
||||||
|
|
||||||
if (!jsestate->result_expr)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
/* coerce using result expression */
|
|
||||||
estate = jsestate->result_expr;
|
|
||||||
jsestate->res_expr->value = res;
|
|
||||||
jsestate->res_expr->isnull = *resnull;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JSON_TABLE_OP:
|
|
||||||
*resnull = false;
|
|
||||||
return item;
|
|
||||||
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unrecognized SQL/JSON expression op %d", jexpr->op);
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty)
|
|
||||||
{
|
|
||||||
Assert(jexpr->on_empty); /* it is not JSON_EXISTS */
|
|
||||||
|
|
||||||
if (jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NO_SQL_JSON_ITEM),
|
|
||||||
errmsg("no SQL/JSON item")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jexpr->on_empty->btype == JSON_BEHAVIOR_DEFAULT)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Execute DEFAULT expression as a coercion expression, because
|
|
||||||
* its result is already coerced to the target type.
|
|
||||||
*/
|
|
||||||
estate = jsestate->default_on_empty;
|
|
||||||
else
|
|
||||||
/* Execute ON EMPTY behavior */
|
|
||||||
res = ExecEvalJsonBehavior(econtext, jexpr->on_empty,
|
|
||||||
jsestate->default_on_empty,
|
|
||||||
resnull);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExecEvalJsonExprSubtrans(ExecEvalJsonExprCoercion, op, econtext,
|
|
||||||
res, resnull, estate, error,
|
|
||||||
cxt->coercionInSubtrans);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr,
|
|
||||||
struct JsonCoercionsState *coercions)
|
|
||||||
{
|
|
||||||
if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (jsexpr->op == JSON_EXISTS_OP && !jsexpr->result_coercion)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!coercions)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
|
||||||
* ExecEvalJson
|
|
||||||
* ----------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
|
|
||||||
{
|
|
||||||
ExecEvalJsonExprContext cxt;
|
|
||||||
JsonExprState *jsestate = op->d.jsonexpr.jsestate;
|
|
||||||
JsonExpr *jexpr = jsestate->jsexpr;
|
|
||||||
Datum item;
|
|
||||||
Datum res = (Datum) 0;
|
|
||||||
JsonPath *path;
|
|
||||||
ListCell *lc;
|
|
||||||
bool error = false;
|
|
||||||
bool needSubtrans;
|
|
||||||
bool throwErrors = jexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
|
|
||||||
|
|
||||||
*op->resnull = true; /* until we get a result */
|
|
||||||
*op->resvalue = (Datum) 0;
|
|
||||||
|
|
||||||
if (jsestate->formatted_expr->isnull || jsestate->pathspec->isnull)
|
|
||||||
{
|
|
||||||
/* execute domain checks for NULLs */
|
|
||||||
(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull,
|
|
||||||
NULL, NULL);
|
|
||||||
|
|
||||||
Assert(*op->resnull);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = jsestate->formatted_expr->value;
|
|
||||||
path = DatumGetJsonPathP(jsestate->pathspec->value);
|
|
||||||
|
|
||||||
/* reset JSON path variable contexts */
|
|
||||||
foreach(lc, jsestate->args)
|
|
||||||
{
|
|
||||||
JsonPathVariableEvalContext *var = lfirst(lc);
|
|
||||||
|
|
||||||
var->econtext = econtext;
|
|
||||||
var->evaluated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
needSubtrans = ExecEvalJsonNeedsSubTransaction(jexpr, &jsestate->coercions);
|
|
||||||
|
|
||||||
cxt.path = path;
|
|
||||||
cxt.error = throwErrors ? NULL : &error;
|
|
||||||
cxt.coercionInSubtrans = !needSubtrans && !throwErrors;
|
|
||||||
Assert(!needSubtrans || cxt.error);
|
|
||||||
|
|
||||||
res = ExecEvalJsonExprSubtrans(ExecEvalJsonExpr, op, econtext, item,
|
|
||||||
op->resnull, &cxt, cxt.error,
|
|
||||||
needSubtrans);
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
/* Execute ON ERROR behavior */
|
|
||||||
res = ExecEvalJsonBehavior(econtext, jexpr->on_error,
|
|
||||||
jsestate->default_on_error,
|
|
||||||
op->resnull);
|
|
||||||
|
|
||||||
/* result is already coerced in DEFAULT behavior case */
|
|
||||||
if (jexpr->on_error->btype != JSON_BEHAVIOR_DEFAULT)
|
|
||||||
res = ExecEvalJsonExprCoercion(op, econtext, res,
|
|
||||||
op->resnull,
|
|
||||||
NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
*op->resvalue = res;
|
|
||||||
}
|
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/jsonpath.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/xml.h"
|
#include "utils/xml.h"
|
||||||
@ -162,9 +161,8 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
|
|||||||
scanstate->ss.ps.qual =
|
scanstate->ss.ps.qual =
|
||||||
ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
|
ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
|
||||||
|
|
||||||
/* Only XMLTABLE and JSON_TABLE are supported currently */
|
/* Only XMLTABLE is supported currently */
|
||||||
scanstate->routine =
|
scanstate->routine = &XmlTableRoutine;
|
||||||
tf->functype == TFT_XMLTABLE ? &XmlTableRoutine : &JsonbTableRoutine;
|
|
||||||
|
|
||||||
scanstate->perTableCxt =
|
scanstate->perTableCxt =
|
||||||
AllocSetContextCreate(CurrentMemoryContext,
|
AllocSetContextCreate(CurrentMemoryContext,
|
||||||
@ -383,8 +381,6 @@ tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
|
|||||||
routine->SetNamespace(tstate, ns_name, ns_uri);
|
routine->SetNamespace(tstate, ns_name, ns_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (routine->SetRowFilter)
|
|
||||||
{
|
|
||||||
/* Install the row filter expression into the table builder context */
|
/* Install the row filter expression into the table builder context */
|
||||||
value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
|
value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
@ -393,7 +389,6 @@ tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
|
|||||||
errmsg("row filter expression must not be null")));
|
errmsg("row filter expression must not be null")));
|
||||||
|
|
||||||
routine->SetRowFilter(tstate, TextDatumGetCString(value));
|
routine->SetRowFilter(tstate, TextDatumGetCString(value));
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Install the column filter expressions into the table builder context.
|
* Install the column filter expressions into the table builder context.
|
||||||
|
@ -2395,24 +2395,6 @@ llvm_compile_expr(ExprState *state)
|
|||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
LLVMBuildBr(b, opblocks[opno + 1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EEOP_JSON_CONSTRUCTOR:
|
|
||||||
build_EvalXFunc(b, mod, "ExecEvalJsonConstructor",
|
|
||||||
v_state, op, v_econtext);
|
|
||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EEOP_IS_JSON:
|
|
||||||
build_EvalXFunc(b, mod, "ExecEvalJsonIsPredicate",
|
|
||||||
v_state, op);
|
|
||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EEOP_JSONEXPR:
|
|
||||||
build_EvalXFunc(b, mod, "ExecEvalJson",
|
|
||||||
v_state, op, v_econtext);
|
|
||||||
LLVMBuildBr(b, opblocks[opno + 1]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EEOP_LAST:
|
case EEOP_LAST:
|
||||||
Assert(false);
|
Assert(false);
|
||||||
break;
|
break;
|
||||||
|
@ -133,9 +133,6 @@ void *referenced_functions[] =
|
|||||||
ExecEvalSysVar,
|
ExecEvalSysVar,
|
||||||
ExecEvalWholeRowVar,
|
ExecEvalWholeRowVar,
|
||||||
ExecEvalXmlExpr,
|
ExecEvalXmlExpr,
|
||||||
ExecEvalJsonConstructor,
|
|
||||||
ExecEvalJsonIsPredicate,
|
|
||||||
ExecEvalJson,
|
|
||||||
MakeExpandedObjectReadOnlyInternal,
|
MakeExpandedObjectReadOnlyInternal,
|
||||||
slot_getmissingattrs,
|
slot_getmissingattrs,
|
||||||
slot_getsomeattrs_int,
|
slot_getsomeattrs_int,
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "utils/errcodes.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
|
||||||
@ -819,124 +818,3 @@ makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
|
|||||||
v->va_cols = va_cols;
|
v->va_cols = va_cols;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonFormat -
|
|
||||||
* creates a JsonFormat node
|
|
||||||
*/
|
|
||||||
JsonFormat *
|
|
||||||
makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
|
|
||||||
{
|
|
||||||
JsonFormat *jf = makeNode(JsonFormat);
|
|
||||||
|
|
||||||
jf->format_type = type;
|
|
||||||
jf->encoding = encoding;
|
|
||||||
jf->location = location;
|
|
||||||
|
|
||||||
return jf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonValueExpr -
|
|
||||||
* creates a JsonValueExpr node
|
|
||||||
*/
|
|
||||||
JsonValueExpr *
|
|
||||||
makeJsonValueExpr(Expr *expr, JsonFormat *format)
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = makeNode(JsonValueExpr);
|
|
||||||
|
|
||||||
jve->raw_expr = expr;
|
|
||||||
jve->formatted_expr = NULL;
|
|
||||||
jve->format = format;
|
|
||||||
|
|
||||||
return jve;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonBehavior -
|
|
||||||
* creates a JsonBehavior node
|
|
||||||
*/
|
|
||||||
JsonBehavior *
|
|
||||||
makeJsonBehavior(JsonBehaviorType type, Node *default_expr)
|
|
||||||
{
|
|
||||||
JsonBehavior *behavior = makeNode(JsonBehavior);
|
|
||||||
|
|
||||||
behavior->btype = type;
|
|
||||||
behavior->default_expr = default_expr;
|
|
||||||
|
|
||||||
return behavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonTableJoinedPlan -
|
|
||||||
* creates a joined JsonTablePlan node
|
|
||||||
*/
|
|
||||||
Node *
|
|
||||||
makeJsonTableJoinedPlan(JsonTablePlanJoinType type, Node *plan1, Node *plan2,
|
|
||||||
int location)
|
|
||||||
{
|
|
||||||
JsonTablePlan *n = makeNode(JsonTablePlan);
|
|
||||||
|
|
||||||
n->plan_type = JSTP_JOINED;
|
|
||||||
n->join_type = type;
|
|
||||||
n->plan1 = castNode(JsonTablePlan, plan1);
|
|
||||||
n->plan2 = castNode(JsonTablePlan, plan2);
|
|
||||||
n->location = location;
|
|
||||||
|
|
||||||
return (Node *) n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonEncoding -
|
|
||||||
* converts JSON encoding name to enum JsonEncoding
|
|
||||||
*/
|
|
||||||
JsonEncoding
|
|
||||||
makeJsonEncoding(char *name)
|
|
||||||
{
|
|
||||||
if (!pg_strcasecmp(name, "utf8"))
|
|
||||||
return JS_ENC_UTF8;
|
|
||||||
if (!pg_strcasecmp(name, "utf16"))
|
|
||||||
return JS_ENC_UTF16;
|
|
||||||
if (!pg_strcasecmp(name, "utf32"))
|
|
||||||
return JS_ENC_UTF32;
|
|
||||||
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("unrecognized JSON encoding: %s", name)));
|
|
||||||
|
|
||||||
return JS_ENC_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonKeyValue -
|
|
||||||
* creates a JsonKeyValue node
|
|
||||||
*/
|
|
||||||
Node *
|
|
||||||
makeJsonKeyValue(Node *key, Node *value)
|
|
||||||
{
|
|
||||||
JsonKeyValue *n = makeNode(JsonKeyValue);
|
|
||||||
|
|
||||||
n->key = (Expr *) key;
|
|
||||||
n->value = castNode(JsonValueExpr, value);
|
|
||||||
|
|
||||||
return (Node *) n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* makeJsonIsPredicate -
|
|
||||||
* creates a JsonIsPredicate node
|
|
||||||
*/
|
|
||||||
Node *
|
|
||||||
makeJsonIsPredicate(Node *expr, JsonFormat *format, JsonValueType item_type,
|
|
||||||
bool unique_keys, int location)
|
|
||||||
{
|
|
||||||
JsonIsPredicate *n = makeNode(JsonIsPredicate);
|
|
||||||
|
|
||||||
n->expr = expr;
|
|
||||||
n->format = format;
|
|
||||||
n->item_type = item_type;
|
|
||||||
n->unique_keys = unique_keys;
|
|
||||||
n->location = location;
|
|
||||||
|
|
||||||
return (Node *) n;
|
|
||||||
}
|
|
||||||
|
@ -250,25 +250,6 @@ exprType(const Node *expr)
|
|||||||
case T_PlaceHolderVar:
|
case T_PlaceHolderVar:
|
||||||
type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
||||||
break;
|
break;
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
const JsonValueExpr *jve = (const JsonValueExpr *) expr;
|
|
||||||
|
|
||||||
type = exprType((Node *) (jve->formatted_expr ? jve->formatted_expr : jve->raw_expr));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
type = ((const JsonConstructorExpr *) expr)->returning->typid;
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
type = BOOLOID;
|
|
||||||
break;
|
|
||||||
case T_JsonExpr:
|
|
||||||
type = ((const JsonExpr *) expr)->returning->typid;
|
|
||||||
break;
|
|
||||||
case T_JsonCoercion:
|
|
||||||
type = exprType(((const JsonCoercion *) expr)->expr);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||||
type = InvalidOid; /* keep compiler quiet */
|
type = InvalidOid; /* keep compiler quiet */
|
||||||
@ -501,14 +482,6 @@ exprTypmod(const Node *expr)
|
|||||||
return ((const SetToDefault *) expr)->typeMod;
|
return ((const SetToDefault *) expr)->typeMod;
|
||||||
case T_PlaceHolderVar:
|
case T_PlaceHolderVar:
|
||||||
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
||||||
case T_JsonValueExpr:
|
|
||||||
return exprTypmod((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
return ((const JsonConstructorExpr *) expr)->returning->typmod;
|
|
||||||
case T_JsonExpr:
|
|
||||||
return ((JsonExpr *) expr)->returning->typmod;
|
|
||||||
case T_JsonCoercion:
|
|
||||||
return exprTypmod(((const JsonCoercion *) expr)->expr);
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -991,37 +964,6 @@ exprCollation(const Node *expr)
|
|||||||
case T_PlaceHolderVar:
|
case T_PlaceHolderVar:
|
||||||
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
|
||||||
break;
|
break;
|
||||||
case T_JsonValueExpr:
|
|
||||||
coll = exprCollation((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
const JsonConstructorExpr *ctor = (const JsonConstructorExpr *) expr;
|
|
||||||
|
|
||||||
if (ctor->coercion)
|
|
||||||
coll = exprCollation((Node *) ctor->coercion);
|
|
||||||
else
|
|
||||||
coll = InvalidOid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
coll = InvalidOid; /* result is always an boolean type */
|
|
||||||
break;
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = (JsonExpr *) expr;
|
|
||||||
JsonCoercion *coercion = jexpr->result_coercion;
|
|
||||||
|
|
||||||
if (!coercion)
|
|
||||||
coll = InvalidOid;
|
|
||||||
else if (coercion->expr)
|
|
||||||
coll = exprCollation(coercion->expr);
|
|
||||||
else if (coercion->via_io || coercion->via_populate)
|
|
||||||
coll = coercion->collation;
|
|
||||||
else
|
|
||||||
coll = InvalidOid;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||||
coll = InvalidOid; /* keep compiler quiet */
|
coll = InvalidOid; /* keep compiler quiet */
|
||||||
@ -1234,39 +1176,6 @@ exprSetCollation(Node *expr, Oid collation)
|
|||||||
/* NextValueExpr's result is an integer type ... */
|
/* NextValueExpr's result is an integer type ... */
|
||||||
Assert(!OidIsValid(collation)); /* ... so never set a collation */
|
Assert(!OidIsValid(collation)); /* ... so never set a collation */
|
||||||
break;
|
break;
|
||||||
case T_JsonValueExpr:
|
|
||||||
exprSetCollation((Node *) ((JsonValueExpr *) expr)->formatted_expr,
|
|
||||||
collation);
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) expr;
|
|
||||||
|
|
||||||
if (ctor->coercion)
|
|
||||||
exprSetCollation((Node *) ctor->coercion, collation);
|
|
||||||
else
|
|
||||||
Assert(!OidIsValid(collation)); /* result is always a
|
|
||||||
* json[b] type */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
Assert(!OidIsValid(collation)); /* result is always boolean */
|
|
||||||
break;
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = (JsonExpr *) expr;
|
|
||||||
JsonCoercion *coercion = jexpr->result_coercion;
|
|
||||||
|
|
||||||
if (!coercion)
|
|
||||||
Assert(!OidIsValid(collation));
|
|
||||||
else if (coercion->expr)
|
|
||||||
exprSetCollation(coercion->expr, collation);
|
|
||||||
else if (coercion->via_io || coercion->via_populate)
|
|
||||||
coercion->collation = collation;
|
|
||||||
else
|
|
||||||
Assert(!OidIsValid(collation));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||||
break;
|
break;
|
||||||
@ -1713,24 +1622,6 @@ exprLocation(const Node *expr)
|
|||||||
case T_PartitionRangeDatum:
|
case T_PartitionRangeDatum:
|
||||||
loc = ((const PartitionRangeDatum *) expr)->location;
|
loc = ((const PartitionRangeDatum *) expr)->location;
|
||||||
break;
|
break;
|
||||||
case T_JsonValueExpr:
|
|
||||||
loc = exprLocation((Node *) ((const JsonValueExpr *) expr)->raw_expr);
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
loc = ((const JsonConstructorExpr *) expr)->location;
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
loc = ((const JsonIsPredicate *) expr)->location;
|
|
||||||
break;
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
const JsonExpr *jsexpr = (const JsonExpr *) expr;
|
|
||||||
|
|
||||||
/* consider both function name and leftmost arg */
|
|
||||||
loc = leftmostLoc(jsexpr->location,
|
|
||||||
exprLocation(jsexpr->formatted_expr));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
/* for any other node type it's just unknown... */
|
/* for any other node type it's just unknown... */
|
||||||
loc = -1;
|
loc = -1;
|
||||||
@ -2473,80 +2364,6 @@ expression_tree_walker(Node *node,
|
|||||||
return true;
|
return true;
|
||||||
if (walker(tf->coldefexprs, context))
|
if (walker(tf->coldefexprs, context))
|
||||||
return true;
|
return true;
|
||||||
if (walker(tf->colvalexprs, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jve->raw_expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jve->formatted_expr, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
|
||||||
|
|
||||||
if (walker(ctor->args, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->func, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->coercion, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
return walker(((JsonIsPredicate *) node)->expr, context);
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = (JsonExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jexpr->formatted_expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jexpr->result_coercion, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jexpr->passing_values, context))
|
|
||||||
return true;
|
|
||||||
/* we assume walker doesn't care about passing_names */
|
|
||||||
if (jexpr->on_empty &&
|
|
||||||
walker(jexpr->on_empty->default_expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jexpr->on_error->default_expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jexpr->coercions, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonCoercion:
|
|
||||||
return walker(((JsonCoercion *) node)->expr, context);
|
|
||||||
case T_JsonItemCoercions:
|
|
||||||
{
|
|
||||||
JsonItemCoercions *coercions = (JsonItemCoercions *) node;
|
|
||||||
|
|
||||||
if (walker(coercions->null, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->string, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->numeric, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->boolean, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->date, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->time, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->timetz, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->timestamp, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->timestamptz, context))
|
|
||||||
return true;
|
|
||||||
if (walker(coercions->composite, context))
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -2876,7 +2693,6 @@ expression_tree_mutator(Node *node,
|
|||||||
case T_RangeTblRef:
|
case T_RangeTblRef:
|
||||||
case T_SortGroupClause:
|
case T_SortGroupClause:
|
||||||
case T_CTESearchClause:
|
case T_CTESearchClause:
|
||||||
case T_JsonFormat:
|
|
||||||
return (Node *) copyObject(node);
|
return (Node *) copyObject(node);
|
||||||
case T_WithCheckOption:
|
case T_WithCheckOption:
|
||||||
{
|
{
|
||||||
@ -3517,102 +3333,6 @@ expression_tree_mutator(Node *node,
|
|||||||
MUTATE(newnode->rowexpr, tf->rowexpr, Node *);
|
MUTATE(newnode->rowexpr, tf->rowexpr, Node *);
|
||||||
MUTATE(newnode->colexprs, tf->colexprs, List *);
|
MUTATE(newnode->colexprs, tf->colexprs, List *);
|
||||||
MUTATE(newnode->coldefexprs, tf->coldefexprs, List *);
|
MUTATE(newnode->coldefexprs, tf->coldefexprs, List *);
|
||||||
MUTATE(newnode->colvalexprs, tf->colvalexprs, List *);
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonReturning:
|
|
||||||
{
|
|
||||||
JsonReturning *jr = (JsonReturning *) node;
|
|
||||||
JsonReturning *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, jr, JsonReturning);
|
|
||||||
MUTATE(newnode->format, jr->format, JsonFormat *);
|
|
||||||
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
|
||||||
JsonValueExpr *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, jve, JsonValueExpr);
|
|
||||||
MUTATE(newnode->raw_expr, jve->raw_expr, Expr *);
|
|
||||||
MUTATE(newnode->formatted_expr, jve->formatted_expr, Expr *);
|
|
||||||
MUTATE(newnode->format, jve->format, JsonFormat *);
|
|
||||||
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *jve = (JsonConstructorExpr *) node;
|
|
||||||
JsonConstructorExpr *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, jve, JsonConstructorExpr);
|
|
||||||
MUTATE(newnode->args, jve->args, List *);
|
|
||||||
MUTATE(newnode->func, jve->func, Expr *);
|
|
||||||
MUTATE(newnode->coercion, jve->coercion, Expr *);
|
|
||||||
MUTATE(newnode->returning, jve->returning, JsonReturning *);
|
|
||||||
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
{
|
|
||||||
JsonIsPredicate *pred = (JsonIsPredicate *) node;
|
|
||||||
JsonIsPredicate *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, pred, JsonIsPredicate);
|
|
||||||
MUTATE(newnode->expr, pred->expr, Node *);
|
|
||||||
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = (JsonExpr *) node;
|
|
||||||
JsonExpr *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, jexpr, JsonExpr);
|
|
||||||
MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
|
|
||||||
MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
|
|
||||||
MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
|
|
||||||
MUTATE(newnode->passing_values, jexpr->passing_values, List *);
|
|
||||||
/* assume mutator does not care about passing_names */
|
|
||||||
if (newnode->on_empty)
|
|
||||||
MUTATE(newnode->on_empty->default_expr,
|
|
||||||
jexpr->on_empty->default_expr, Node *);
|
|
||||||
MUTATE(newnode->on_error->default_expr,
|
|
||||||
jexpr->on_error->default_expr, Node *);
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonCoercion:
|
|
||||||
{
|
|
||||||
JsonCoercion *coercion = (JsonCoercion *) node;
|
|
||||||
JsonCoercion *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, coercion, JsonCoercion);
|
|
||||||
MUTATE(newnode->expr, coercion->expr, Node *);
|
|
||||||
return (Node *) newnode;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonItemCoercions:
|
|
||||||
{
|
|
||||||
JsonItemCoercions *coercions = (JsonItemCoercions *) node;
|
|
||||||
JsonItemCoercions *newnode;
|
|
||||||
|
|
||||||
FLATCOPY(newnode, coercions, JsonItemCoercions);
|
|
||||||
MUTATE(newnode->null, coercions->null, JsonCoercion *);
|
|
||||||
MUTATE(newnode->string, coercions->string, JsonCoercion *);
|
|
||||||
MUTATE(newnode->numeric, coercions->numeric, JsonCoercion *);
|
|
||||||
MUTATE(newnode->boolean, coercions->boolean, JsonCoercion *);
|
|
||||||
MUTATE(newnode->date, coercions->date, JsonCoercion *);
|
|
||||||
MUTATE(newnode->time, coercions->time, JsonCoercion *);
|
|
||||||
MUTATE(newnode->timetz, coercions->timetz, JsonCoercion *);
|
|
||||||
MUTATE(newnode->timestamp, coercions->timestamp, JsonCoercion *);
|
|
||||||
MUTATE(newnode->timestamptz, coercions->timestamptz, JsonCoercion *);
|
|
||||||
MUTATE(newnode->composite, coercions->composite, JsonCoercion *);
|
|
||||||
return (Node *) newnode;
|
return (Node *) newnode;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -3888,7 +3608,6 @@ raw_expression_tree_walker(Node *node,
|
|||||||
case T_ParamRef:
|
case T_ParamRef:
|
||||||
case T_A_Const:
|
case T_A_Const:
|
||||||
case T_A_Star:
|
case T_A_Star:
|
||||||
case T_JsonFormat:
|
|
||||||
/* primitive node types with no subnodes */
|
/* primitive node types with no subnodes */
|
||||||
break;
|
break;
|
||||||
case T_Alias:
|
case T_Alias:
|
||||||
@ -4351,211 +4070,6 @@ raw_expression_tree_walker(Node *node,
|
|||||||
case T_CommonTableExpr:
|
case T_CommonTableExpr:
|
||||||
/* search_clause and cycle_clause are not interesting here */
|
/* search_clause and cycle_clause are not interesting here */
|
||||||
return walker(((CommonTableExpr *) node)->ctequery, context);
|
return walker(((CommonTableExpr *) node)->ctequery, context);
|
||||||
case T_JsonReturning:
|
|
||||||
return walker(((JsonReturning *) node)->format, context);
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jve->raw_expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jve->formatted_expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jve->format, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonParseExpr:
|
|
||||||
{
|
|
||||||
JsonParseExpr *jpe = (JsonParseExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jpe->expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jpe->output, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonScalarExpr:
|
|
||||||
{
|
|
||||||
JsonScalarExpr *jse = (JsonScalarExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jse->expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jse->output, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonSerializeExpr:
|
|
||||||
{
|
|
||||||
JsonSerializeExpr *jse = (JsonSerializeExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jse->expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jse->output, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
|
||||||
|
|
||||||
if (walker(ctor->args, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->func, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->coercion, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->returning, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonOutput:
|
|
||||||
{
|
|
||||||
JsonOutput *out = (JsonOutput *) node;
|
|
||||||
|
|
||||||
if (walker(out->typeName, context))
|
|
||||||
return true;
|
|
||||||
if (walker(out->returning, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonKeyValue:
|
|
||||||
{
|
|
||||||
JsonKeyValue *jkv = (JsonKeyValue *) node;
|
|
||||||
|
|
||||||
if (walker(jkv->key, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jkv->value, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonObjectConstructor:
|
|
||||||
{
|
|
||||||
JsonObjectConstructor *joc = (JsonObjectConstructor *) node;
|
|
||||||
|
|
||||||
if (walker(joc->output, context))
|
|
||||||
return true;
|
|
||||||
if (walker(joc->exprs, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonArrayConstructor:
|
|
||||||
{
|
|
||||||
JsonArrayConstructor *jac = (JsonArrayConstructor *) node;
|
|
||||||
|
|
||||||
if (walker(jac->output, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jac->exprs, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonAggConstructor:
|
|
||||||
{
|
|
||||||
JsonAggConstructor *ctor = (JsonAggConstructor *) node;
|
|
||||||
|
|
||||||
if (walker(ctor->output, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->agg_order, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->agg_filter, context))
|
|
||||||
return true;
|
|
||||||
if (walker(ctor->over, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonObjectAgg:
|
|
||||||
{
|
|
||||||
JsonObjectAgg *joa = (JsonObjectAgg *) node;
|
|
||||||
|
|
||||||
if (walker(joa->constructor, context))
|
|
||||||
return true;
|
|
||||||
if (walker(joa->arg, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonArrayAgg:
|
|
||||||
{
|
|
||||||
JsonArrayAgg *jaa = (JsonArrayAgg *) node;
|
|
||||||
|
|
||||||
if (walker(jaa->constructor, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jaa->arg, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonArrayQueryConstructor:
|
|
||||||
{
|
|
||||||
JsonArrayQueryConstructor *jaqc = (JsonArrayQueryConstructor *) node;
|
|
||||||
|
|
||||||
if (walker(jaqc->output, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jaqc->query, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
return walker(((JsonIsPredicate *) node)->expr, context);
|
|
||||||
case T_JsonArgument:
|
|
||||||
return walker(((JsonArgument *) node)->val, context);
|
|
||||||
case T_JsonCommon:
|
|
||||||
{
|
|
||||||
JsonCommon *jc = (JsonCommon *) node;
|
|
||||||
|
|
||||||
if (walker(jc->expr, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jc->pathspec, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jc->passing, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonBehavior:
|
|
||||||
{
|
|
||||||
JsonBehavior *jb = (JsonBehavior *) node;
|
|
||||||
|
|
||||||
if (jb->btype == JSON_BEHAVIOR_DEFAULT &&
|
|
||||||
walker(jb->default_expr, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonFuncExpr:
|
|
||||||
{
|
|
||||||
JsonFuncExpr *jfe = (JsonFuncExpr *) node;
|
|
||||||
|
|
||||||
if (walker(jfe->common, context))
|
|
||||||
return true;
|
|
||||||
if (jfe->output && walker(jfe->output, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jfe->on_empty, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jfe->on_error, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonTable:
|
|
||||||
{
|
|
||||||
JsonTable *jt = (JsonTable *) node;
|
|
||||||
|
|
||||||
if (walker(jt->common, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jt->columns, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonTableColumn:
|
|
||||||
{
|
|
||||||
JsonTableColumn *jtc = (JsonTableColumn *) node;
|
|
||||||
|
|
||||||
if (walker(jtc->typeName, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jtc->on_empty, context))
|
|
||||||
return true;
|
|
||||||
if (walker(jtc->on_error, context))
|
|
||||||
return true;
|
|
||||||
if (jtc->coltype == JTC_NESTED && walker(jtc->columns, context))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized node type: %d",
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
(int) nodeTag(node));
|
(int) nodeTag(node));
|
||||||
|
@ -4923,8 +4923,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
|
|||||||
IsA(node, SQLValueFunction) ||
|
IsA(node, SQLValueFunction) ||
|
||||||
IsA(node, XmlExpr) ||
|
IsA(node, XmlExpr) ||
|
||||||
IsA(node, CoerceToDomain) ||
|
IsA(node, CoerceToDomain) ||
|
||||||
IsA(node, NextValueExpr) ||
|
IsA(node, NextValueExpr))
|
||||||
IsA(node, JsonExpr))
|
|
||||||
{
|
{
|
||||||
/* Treat all these as having cost 1 */
|
/* Treat all these as having cost 1 */
|
||||||
context->total.per_tuple += cpu_operator_cost;
|
context->total.per_tuple += cpu_operator_cost;
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/functions.h"
|
#include "executor/functions.h"
|
||||||
#include "executor/execExpr.h"
|
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
@ -51,9 +50,6 @@
|
|||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/json.h"
|
|
||||||
#include "utils/jsonb.h"
|
|
||||||
#include "utils/jsonpath.h"
|
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
@ -386,45 +382,6 @@ contain_mutable_functions_walker(Node *node, void *context)
|
|||||||
context))
|
context))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (IsA(node, JsonConstructorExpr))
|
|
||||||
{
|
|
||||||
const JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
|
||||||
ListCell *lc;
|
|
||||||
bool is_jsonb =
|
|
||||||
ctor->returning->format->format_type == JS_FORMAT_JSONB;
|
|
||||||
|
|
||||||
/* Check argument_type => json[b] conversions */
|
|
||||||
foreach(lc, ctor->args)
|
|
||||||
{
|
|
||||||
Oid typid = exprType(lfirst(lc));
|
|
||||||
|
|
||||||
if (is_jsonb ?
|
|
||||||
!to_jsonb_is_immutable(typid) :
|
|
||||||
!to_json_is_immutable(typid))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check all subnodes */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsA(node, JsonExpr))
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = castNode(JsonExpr, node);
|
|
||||||
Const *cnst;
|
|
||||||
|
|
||||||
if (!IsA(jexpr->path_spec, Const))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
cnst = castNode(Const, jexpr->path_spec);
|
|
||||||
|
|
||||||
Assert(cnst->consttype == JSONPATHOID);
|
|
||||||
if (cnst->constisnull)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return jspIsMutable(DatumGetJsonPathP(cnst->constvalue),
|
|
||||||
jexpr->passing_names, jexpr->passing_values);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsA(node, SQLValueFunction))
|
if (IsA(node, SQLValueFunction))
|
||||||
{
|
{
|
||||||
/* all variants of SQLValueFunction are stable */
|
/* all variants of SQLValueFunction are stable */
|
||||||
@ -896,18 +853,6 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
|
|||||||
context, 0);
|
context, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* JsonExpr is parallel-unsafe if subtransactions can be used. */
|
|
||||||
else if (IsA(node, JsonExpr))
|
|
||||||
{
|
|
||||||
JsonExpr *jsexpr = (JsonExpr *) node;
|
|
||||||
|
|
||||||
if (ExecEvalJsonNeedsSubTransaction(jsexpr, NULL))
|
|
||||||
{
|
|
||||||
context->max_hazard = PROPARALLEL_UNSAFE;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recurse to check arguments */
|
/* Recurse to check arguments */
|
||||||
return expression_tree_walker(node,
|
return expression_tree_walker(node,
|
||||||
max_parallel_hazard_walker,
|
max_parallel_hazard_walker,
|
||||||
@ -3567,29 +3512,6 @@ eval_const_expressions_mutator(Node *node,
|
|||||||
return ece_evaluate_expr((Node *) newcre);
|
return ece_evaluate_expr((Node *) newcre);
|
||||||
return (Node *) newcre;
|
return (Node *) newcre;
|
||||||
}
|
}
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
|
||||||
Node *raw = eval_const_expressions_mutator((Node *) jve->raw_expr,
|
|
||||||
context);
|
|
||||||
|
|
||||||
if (raw && IsA(raw, Const))
|
|
||||||
{
|
|
||||||
Node *formatted;
|
|
||||||
Node *save_case_val = context->case_val;
|
|
||||||
|
|
||||||
context->case_val = raw;
|
|
||||||
|
|
||||||
formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
|
|
||||||
context);
|
|
||||||
|
|
||||||
context->case_val = save_case_val;
|
|
||||||
|
|
||||||
if (formatted && IsA(formatted, Const))
|
|
||||||
return formatted;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ OBJS = \
|
|||||||
parse_enr.o \
|
parse_enr.o \
|
||||||
parse_expr.o \
|
parse_expr.o \
|
||||||
parse_func.o \
|
parse_func.o \
|
||||||
parse_jsontable.o \
|
|
||||||
parse_merge.o \
|
parse_merge.o \
|
||||||
parse_node.o \
|
parse_node.o \
|
||||||
parse_oper.o \
|
parse_oper.o \
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -690,9 +690,7 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
|
|||||||
char **names;
|
char **names;
|
||||||
int colno;
|
int colno;
|
||||||
|
|
||||||
/* Currently only XMLTABLE and JSON_TABLE are supported */
|
/* Currently only XMLTABLE is supported */
|
||||||
|
|
||||||
tf->functype = TFT_XMLTABLE;
|
|
||||||
constructName = "XMLTABLE";
|
constructName = "XMLTABLE";
|
||||||
docType = XMLOID;
|
docType = XMLOID;
|
||||||
|
|
||||||
@ -1099,17 +1097,13 @@ transformFromClauseItem(ParseState *pstate, Node *n,
|
|||||||
rtr->rtindex = nsitem->p_rtindex;
|
rtr->rtindex = nsitem->p_rtindex;
|
||||||
return (Node *) rtr;
|
return (Node *) rtr;
|
||||||
}
|
}
|
||||||
else if (IsA(n, RangeTableFunc) || IsA(n, JsonTable))
|
else if (IsA(n, RangeTableFunc))
|
||||||
{
|
{
|
||||||
/* table function is like a plain relation */
|
/* table function is like a plain relation */
|
||||||
RangeTblRef *rtr;
|
RangeTblRef *rtr;
|
||||||
ParseNamespaceItem *nsitem;
|
ParseNamespaceItem *nsitem;
|
||||||
|
|
||||||
if (IsA(n, RangeTableFunc))
|
|
||||||
nsitem = transformRangeTableFunc(pstate, (RangeTableFunc *) n);
|
nsitem = transformRangeTableFunc(pstate, (RangeTableFunc *) n);
|
||||||
else
|
|
||||||
nsitem = transformJsonTable(pstate, (JsonTable *) n);
|
|
||||||
|
|
||||||
*top_nsitem = nsitem;
|
*top_nsitem = nsitem;
|
||||||
*namespace = list_make1(nsitem);
|
*namespace = list_make1(nsitem);
|
||||||
rtr = makeNode(RangeTblRef);
|
rtr = makeNode(RangeTblRef);
|
||||||
|
@ -691,13 +691,6 @@ assign_collations_walker(Node *node, assign_collations_context *context)
|
|||||||
&loccontext);
|
&loccontext);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_JsonExpr:
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Context item and PASSING arguments are already
|
|
||||||
* marked with collations in parse_expr.c.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,732 +0,0 @@
|
|||||||
/*-------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* parse_jsontable.c
|
|
||||||
* parsing of JSON_TABLE
|
|
||||||
*
|
|
||||||
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* IDENTIFICATION
|
|
||||||
* src/backend/parser/parse_jsontable.c
|
|
||||||
*
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "postgres.h"
|
|
||||||
|
|
||||||
#include "catalog/pg_collation.h"
|
|
||||||
#include "catalog/pg_type.h"
|
|
||||||
#include "miscadmin.h"
|
|
||||||
#include "nodes/makefuncs.h"
|
|
||||||
#include "nodes/nodeFuncs.h"
|
|
||||||
#include "optimizer/optimizer.h"
|
|
||||||
#include "parser/parse_clause.h"
|
|
||||||
#include "parser/parse_collate.h"
|
|
||||||
#include "parser/parse_expr.h"
|
|
||||||
#include "parser/parse_relation.h"
|
|
||||||
#include "parser/parse_type.h"
|
|
||||||
#include "utils/builtins.h"
|
|
||||||
#include "utils/json.h"
|
|
||||||
#include "utils/lsyscache.h"
|
|
||||||
|
|
||||||
/* Context for JSON_TABLE transformation */
|
|
||||||
typedef struct JsonTableContext
|
|
||||||
{
|
|
||||||
ParseState *pstate; /* parsing state */
|
|
||||||
JsonTable *table; /* untransformed node */
|
|
||||||
TableFunc *tablefunc; /* transformed node */
|
|
||||||
List *pathNames; /* list of all path and columns names */
|
|
||||||
int pathNameId; /* path name id counter */
|
|
||||||
Oid contextItemTypid; /* type oid of context item (json/jsonb) */
|
|
||||||
} JsonTableContext;
|
|
||||||
|
|
||||||
static JsonTableParent *transformJsonTableColumns(JsonTableContext *cxt,
|
|
||||||
JsonTablePlan *plan,
|
|
||||||
List *columns,
|
|
||||||
char *pathSpec,
|
|
||||||
char **pathName,
|
|
||||||
int location);
|
|
||||||
|
|
||||||
static Node *
|
|
||||||
makeStringConst(char *str, int location)
|
|
||||||
{
|
|
||||||
A_Const *n = makeNode(A_Const);
|
|
||||||
|
|
||||||
n->val.node.type = T_String;
|
|
||||||
n->val.sval.sval = str;
|
|
||||||
n->location = location;
|
|
||||||
|
|
||||||
return (Node *) n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Transform JSON_TABLE column
|
|
||||||
* - regular column into JSON_VALUE()
|
|
||||||
* - FORMAT JSON column into JSON_QUERY()
|
|
||||||
* - EXISTS column into JSON_EXISTS()
|
|
||||||
*/
|
|
||||||
static Node *
|
|
||||||
transformJsonTableColumn(JsonTableColumn *jtc, Node *contextItemExpr,
|
|
||||||
List *passingArgs, bool errorOnError)
|
|
||||||
{
|
|
||||||
JsonFuncExpr *jfexpr = makeNode(JsonFuncExpr);
|
|
||||||
JsonCommon *common = makeNode(JsonCommon);
|
|
||||||
JsonOutput *output = makeNode(JsonOutput);
|
|
||||||
char *pathspec;
|
|
||||||
JsonFormat *default_format;
|
|
||||||
|
|
||||||
jfexpr->op =
|
|
||||||
jtc->coltype == JTC_REGULAR ? JSON_VALUE_OP :
|
|
||||||
jtc->coltype == JTC_EXISTS ? JSON_EXISTS_OP : JSON_QUERY_OP;
|
|
||||||
jfexpr->common = common;
|
|
||||||
jfexpr->output = output;
|
|
||||||
jfexpr->on_empty = jtc->on_empty;
|
|
||||||
jfexpr->on_error = jtc->on_error;
|
|
||||||
if (!jfexpr->on_error && errorOnError)
|
|
||||||
jfexpr->on_error = makeJsonBehavior(JSON_BEHAVIOR_ERROR, NULL);
|
|
||||||
jfexpr->omit_quotes = jtc->omit_quotes;
|
|
||||||
jfexpr->wrapper = jtc->wrapper;
|
|
||||||
jfexpr->location = jtc->location;
|
|
||||||
|
|
||||||
output->typeName = jtc->typeName;
|
|
||||||
output->returning = makeNode(JsonReturning);
|
|
||||||
output->returning->format = jtc->format;
|
|
||||||
|
|
||||||
default_format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
|
|
||||||
|
|
||||||
common->pathname = NULL;
|
|
||||||
common->expr = makeJsonValueExpr((Expr *) contextItemExpr, default_format);
|
|
||||||
common->passing = passingArgs;
|
|
||||||
|
|
||||||
if (jtc->pathspec)
|
|
||||||
pathspec = jtc->pathspec;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Construct default path as '$."column_name"' */
|
|
||||||
StringInfoData path;
|
|
||||||
|
|
||||||
initStringInfo(&path);
|
|
||||||
|
|
||||||
appendStringInfoString(&path, "$.");
|
|
||||||
escape_json(&path, jtc->name);
|
|
||||||
|
|
||||||
pathspec = path.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
common->pathspec = makeStringConst(pathspec, -1);
|
|
||||||
|
|
||||||
return (Node *) jfexpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
isJsonTablePathNameDuplicate(JsonTableContext *cxt, const char *pathname)
|
|
||||||
{
|
|
||||||
ListCell *lc;
|
|
||||||
|
|
||||||
foreach(lc, cxt->pathNames)
|
|
||||||
{
|
|
||||||
if (!strcmp(pathname, (const char *) lfirst(lc)))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Register the column name in the path name list. */
|
|
||||||
static void
|
|
||||||
registerJsonTableColumn(JsonTableContext *cxt, char *colname)
|
|
||||||
{
|
|
||||||
if (isJsonTablePathNameDuplicate(cxt, colname))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DUPLICATE_ALIAS),
|
|
||||||
errmsg("duplicate JSON_TABLE column name: %s", colname),
|
|
||||||
errhint("JSON_TABLE column names must be distinct from one another.")));
|
|
||||||
|
|
||||||
cxt->pathNames = lappend(cxt->pathNames, colname);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively register all nested column names in the path name list. */
|
|
||||||
static void
|
|
||||||
registerAllJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|
||||||
{
|
|
||||||
ListCell *lc;
|
|
||||||
|
|
||||||
foreach(lc, columns)
|
|
||||||
{
|
|
||||||
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
|
|
||||||
|
|
||||||
if (jtc->coltype == JTC_NESTED)
|
|
||||||
{
|
|
||||||
if (jtc->pathname)
|
|
||||||
registerJsonTableColumn(cxt, jtc->pathname);
|
|
||||||
|
|
||||||
registerAllJsonTableColumns(cxt, jtc->columns);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
registerJsonTableColumn(cxt, jtc->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generate a new unique JSON_TABLE path name. */
|
|
||||||
static char *
|
|
||||||
generateJsonTablePathName(JsonTableContext *cxt)
|
|
||||||
{
|
|
||||||
char namebuf[32];
|
|
||||||
char *name = namebuf;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
snprintf(namebuf, sizeof(namebuf), "json_table_path_%d",
|
|
||||||
++cxt->pathNameId);
|
|
||||||
} while (isJsonTablePathNameDuplicate(cxt, name));
|
|
||||||
|
|
||||||
name = pstrdup(name);
|
|
||||||
cxt->pathNames = lappend(cxt->pathNames, name);
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Collect sibling path names from plan to the specified list. */
|
|
||||||
static void
|
|
||||||
collectSiblingPathsInJsonTablePlan(JsonTablePlan *plan, List **paths)
|
|
||||||
{
|
|
||||||
if (plan->plan_type == JSTP_SIMPLE)
|
|
||||||
*paths = lappend(*paths, plan->pathname);
|
|
||||||
else if (plan->plan_type == JSTP_JOINED)
|
|
||||||
{
|
|
||||||
if (plan->join_type == JSTPJ_INNER ||
|
|
||||||
plan->join_type == JSTPJ_OUTER)
|
|
||||||
{
|
|
||||||
Assert(plan->plan1->plan_type == JSTP_SIMPLE);
|
|
||||||
*paths = lappend(*paths, plan->plan1->pathname);
|
|
||||||
}
|
|
||||||
else if (plan->join_type == JSTPJ_CROSS ||
|
|
||||||
plan->join_type == JSTPJ_UNION)
|
|
||||||
{
|
|
||||||
collectSiblingPathsInJsonTablePlan(plan->plan1, paths);
|
|
||||||
collectSiblingPathsInJsonTablePlan(plan->plan2, paths);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(ERROR, "invalid JSON_TABLE join type %d",
|
|
||||||
plan->join_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Validate child JSON_TABLE plan by checking that:
|
|
||||||
* - all nested columns have path names specified
|
|
||||||
* - all nested columns have corresponding node in the sibling plan
|
|
||||||
* - plan does not contain duplicate or extra nodes
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
validateJsonTableChildPlan(ParseState *pstate, JsonTablePlan *plan,
|
|
||||||
List *columns)
|
|
||||||
{
|
|
||||||
ListCell *lc1;
|
|
||||||
List *siblings = NIL;
|
|
||||||
int nchildren = 0;
|
|
||||||
|
|
||||||
if (plan)
|
|
||||||
collectSiblingPathsInJsonTablePlan(plan, &siblings);
|
|
||||||
|
|
||||||
foreach(lc1, columns)
|
|
||||||
{
|
|
||||||
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc1));
|
|
||||||
|
|
||||||
if (jtc->coltype == JTC_NESTED)
|
|
||||||
{
|
|
||||||
ListCell *lc2;
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
if (!jtc->pathname)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("nested JSON_TABLE columns must contain an explicit AS pathname specification if an explicit PLAN clause is used"),
|
|
||||||
parser_errposition(pstate, jtc->location)));
|
|
||||||
|
|
||||||
/* find nested path name in the list of sibling path names */
|
|
||||||
foreach(lc2, siblings)
|
|
||||||
{
|
|
||||||
if ((found = !strcmp(jtc->pathname, lfirst(lc2))))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid JSON_TABLE plan"),
|
|
||||||
errdetail("Plan node for nested path %s was not found in plan.", jtc->pathname),
|
|
||||||
parser_errposition(pstate, jtc->location)));
|
|
||||||
|
|
||||||
nchildren++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list_length(siblings) > nchildren)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid JSON_TABLE plan"),
|
|
||||||
errdetail("Plan node contains some extra or duplicate sibling nodes."),
|
|
||||||
parser_errposition(pstate, plan ? plan->location : -1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
static JsonTableColumn *
|
|
||||||
findNestedJsonTableColumn(List *columns, const char *pathname)
|
|
||||||
{
|
|
||||||
ListCell *lc;
|
|
||||||
|
|
||||||
foreach(lc, columns)
|
|
||||||
{
|
|
||||||
JsonTableColumn *jtc = castNode(JsonTableColumn, lfirst(lc));
|
|
||||||
|
|
||||||
if (jtc->coltype == JTC_NESTED &&
|
|
||||||
jtc->pathname &&
|
|
||||||
!strcmp(jtc->pathname, pathname))
|
|
||||||
return jtc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Node *
|
|
||||||
transformNestedJsonTableColumn(JsonTableContext *cxt, JsonTableColumn *jtc,
|
|
||||||
JsonTablePlan *plan)
|
|
||||||
{
|
|
||||||
JsonTableParent *node;
|
|
||||||
char *pathname = jtc->pathname;
|
|
||||||
|
|
||||||
node = transformJsonTableColumns(cxt, plan, jtc->columns, jtc->pathspec,
|
|
||||||
&pathname, jtc->location);
|
|
||||||
node->name = pstrdup(pathname);
|
|
||||||
|
|
||||||
return (Node *) node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Node *
|
|
||||||
makeJsonTableSiblingJoin(bool cross, Node *lnode, Node *rnode)
|
|
||||||
{
|
|
||||||
JsonTableSibling *join = makeNode(JsonTableSibling);
|
|
||||||
|
|
||||||
join->larg = lnode;
|
|
||||||
join->rarg = rnode;
|
|
||||||
join->cross = cross;
|
|
||||||
|
|
||||||
return (Node *) join;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Recursively transform child JSON_TABLE plan.
|
|
||||||
*
|
|
||||||
* Default plan is transformed into a cross/union join of its nested columns.
|
|
||||||
* Simple and outer/inner plans are transformed into a JsonTableParent by
|
|
||||||
* finding and transforming corresponding nested column.
|
|
||||||
* Sibling plans are recursively transformed into a JsonTableSibling.
|
|
||||||
*/
|
|
||||||
static Node *
|
|
||||||
transformJsonTableChildPlan(JsonTableContext *cxt, JsonTablePlan *plan,
|
|
||||||
List *columns)
|
|
||||||
{
|
|
||||||
JsonTableColumn *jtc = NULL;
|
|
||||||
|
|
||||||
if (!plan || plan->plan_type == JSTP_DEFAULT)
|
|
||||||
{
|
|
||||||
/* unspecified or default plan */
|
|
||||||
Node *res = NULL;
|
|
||||||
ListCell *lc;
|
|
||||||
bool cross = plan && (plan->join_type & JSTPJ_CROSS);
|
|
||||||
|
|
||||||
/* transform all nested columns into cross/union join */
|
|
||||||
foreach(lc, columns)
|
|
||||||
{
|
|
||||||
JsonTableColumn *col = castNode(JsonTableColumn, lfirst(lc));
|
|
||||||
Node *node;
|
|
||||||
|
|
||||||
if (col->coltype != JTC_NESTED)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
node = transformNestedJsonTableColumn(cxt, col, plan);
|
|
||||||
|
|
||||||
/* join transformed node with previous sibling nodes */
|
|
||||||
res = res ? makeJsonTableSiblingJoin(cross, res, node) : node;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else if (plan->plan_type == JSTP_SIMPLE)
|
|
||||||
{
|
|
||||||
jtc = findNestedJsonTableColumn(columns, plan->pathname);
|
|
||||||
}
|
|
||||||
else if (plan->plan_type == JSTP_JOINED)
|
|
||||||
{
|
|
||||||
if (plan->join_type == JSTPJ_INNER ||
|
|
||||||
plan->join_type == JSTPJ_OUTER)
|
|
||||||
{
|
|
||||||
Assert(plan->plan1->plan_type == JSTP_SIMPLE);
|
|
||||||
jtc = findNestedJsonTableColumn(columns, plan->plan1->pathname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Node *node1 = transformJsonTableChildPlan(cxt, plan->plan1,
|
|
||||||
columns);
|
|
||||||
Node *node2 = transformJsonTableChildPlan(cxt, plan->plan2,
|
|
||||||
columns);
|
|
||||||
|
|
||||||
return makeJsonTableSiblingJoin(plan->join_type == JSTPJ_CROSS,
|
|
||||||
node1, node2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(ERROR, "invalid JSON_TABLE plan type %d", plan->plan_type);
|
|
||||||
|
|
||||||
if (!jtc)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid JSON_TABLE plan"),
|
|
||||||
errdetail("Path name was %s not found in nested columns list.",
|
|
||||||
plan->pathname),
|
|
||||||
parser_errposition(cxt->pstate, plan->location)));
|
|
||||||
|
|
||||||
return transformNestedJsonTableColumn(cxt, jtc, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check whether type is json/jsonb, array, or record. */
|
|
||||||
static bool
|
|
||||||
typeIsComposite(Oid typid)
|
|
||||||
{
|
|
||||||
char typtype;
|
|
||||||
|
|
||||||
if (typid == JSONOID ||
|
|
||||||
typid == JSONBOID ||
|
|
||||||
typid == RECORDOID ||
|
|
||||||
type_is_array(typid))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
typtype = get_typtype(typid);
|
|
||||||
|
|
||||||
if (typtype == TYPTYPE_COMPOSITE)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (typtype == TYPTYPE_DOMAIN)
|
|
||||||
return typeIsComposite(getBaseType(typid));
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Append transformed non-nested JSON_TABLE columns to the TableFunc node */
|
|
||||||
static void
|
|
||||||
appendJsonTableColumns(JsonTableContext *cxt, List *columns)
|
|
||||||
{
|
|
||||||
ListCell *col;
|
|
||||||
ParseState *pstate = cxt->pstate;
|
|
||||||
JsonTable *jt = cxt->table;
|
|
||||||
TableFunc *tf = cxt->tablefunc;
|
|
||||||
bool errorOnError = jt->on_error &&
|
|
||||||
jt->on_error->btype == JSON_BEHAVIOR_ERROR;
|
|
||||||
|
|
||||||
foreach(col, columns)
|
|
||||||
{
|
|
||||||
JsonTableColumn *rawc = castNode(JsonTableColumn, lfirst(col));
|
|
||||||
Oid typid;
|
|
||||||
int32 typmod;
|
|
||||||
Node *colexpr;
|
|
||||||
|
|
||||||
if (rawc->name)
|
|
||||||
{
|
|
||||||
/* make sure column names are unique */
|
|
||||||
ListCell *colname;
|
|
||||||
|
|
||||||
foreach(colname, tf->colnames)
|
|
||||||
if (!strcmp((const char *) colname, rawc->name))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("column name \"%s\" is not unique",
|
|
||||||
rawc->name),
|
|
||||||
parser_errposition(pstate, rawc->location)));
|
|
||||||
|
|
||||||
tf->colnames = lappend(tf->colnames,
|
|
||||||
makeString(pstrdup(rawc->name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine the type and typmod for the new column. FOR ORDINALITY
|
|
||||||
* columns are INTEGER by standard; the others are user-specified.
|
|
||||||
*/
|
|
||||||
switch (rawc->coltype)
|
|
||||||
{
|
|
||||||
case JTC_FOR_ORDINALITY:
|
|
||||||
colexpr = NULL;
|
|
||||||
typid = INT4OID;
|
|
||||||
typmod = -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case JTC_REGULAR:
|
|
||||||
typenameTypeIdAndMod(pstate, rawc->typeName, &typid, &typmod);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use implicit FORMAT JSON for composite types (arrays and
|
|
||||||
* records)
|
|
||||||
*/
|
|
||||||
if (typeIsComposite(typid))
|
|
||||||
rawc->coltype = JTC_FORMATTED;
|
|
||||||
else if (rawc->wrapper != JSW_NONE)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("cannot use WITH WRAPPER clause with scalar columns"),
|
|
||||||
parser_errposition(pstate, rawc->location)));
|
|
||||||
else if (rawc->omit_quotes)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("cannot use OMIT QUOTES clause with scalar columns"),
|
|
||||||
parser_errposition(pstate, rawc->location)));
|
|
||||||
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
case JTC_EXISTS:
|
|
||||||
case JTC_FORMATTED:
|
|
||||||
{
|
|
||||||
Node *je;
|
|
||||||
CaseTestExpr *param = makeNode(CaseTestExpr);
|
|
||||||
|
|
||||||
param->collation = InvalidOid;
|
|
||||||
param->typeId = cxt->contextItemTypid;
|
|
||||||
param->typeMod = -1;
|
|
||||||
|
|
||||||
je = transformJsonTableColumn(rawc, (Node *) param,
|
|
||||||
NIL, errorOnError);
|
|
||||||
|
|
||||||
colexpr = transformExpr(pstate, je, EXPR_KIND_FROM_FUNCTION);
|
|
||||||
assign_expr_collations(pstate, colexpr);
|
|
||||||
|
|
||||||
typid = exprType(colexpr);
|
|
||||||
typmod = exprTypmod(colexpr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case JTC_NESTED:
|
|
||||||
continue;
|
|
||||||
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unknown JSON_TABLE column type: %d", rawc->coltype);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
tf->coltypes = lappend_oid(tf->coltypes, typid);
|
|
||||||
tf->coltypmods = lappend_int(tf->coltypmods, typmod);
|
|
||||||
tf->colcollations = lappend_oid(tf->colcollations, get_typcollation(typid));
|
|
||||||
tf->colvalexprs = lappend(tf->colvalexprs, colexpr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create transformed JSON_TABLE parent plan node by appending all non-nested
|
|
||||||
* columns to the TableFunc node and remembering their indices in the
|
|
||||||
* colvalexprs list.
|
|
||||||
*/
|
|
||||||
static JsonTableParent *
|
|
||||||
makeParentJsonTableNode(JsonTableContext *cxt, char *pathSpec, List *columns)
|
|
||||||
{
|
|
||||||
JsonTableParent *node = makeNode(JsonTableParent);
|
|
||||||
|
|
||||||
node->path = makeConst(JSONPATHOID, -1, InvalidOid, -1,
|
|
||||||
DirectFunctionCall1(jsonpath_in,
|
|
||||||
CStringGetDatum(pathSpec)),
|
|
||||||
false, false);
|
|
||||||
|
|
||||||
/* save start of column range */
|
|
||||||
node->colMin = list_length(cxt->tablefunc->colvalexprs);
|
|
||||||
|
|
||||||
appendJsonTableColumns(cxt, columns);
|
|
||||||
|
|
||||||
/* save end of column range */
|
|
||||||
node->colMax = list_length(cxt->tablefunc->colvalexprs) - 1;
|
|
||||||
|
|
||||||
node->errorOnError =
|
|
||||||
cxt->table->on_error &&
|
|
||||||
cxt->table->on_error->btype == JSON_BEHAVIOR_ERROR;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JsonTableParent *
|
|
||||||
transformJsonTableColumns(JsonTableContext *cxt, JsonTablePlan *plan,
|
|
||||||
List *columns, char *pathSpec, char **pathName,
|
|
||||||
int location)
|
|
||||||
{
|
|
||||||
JsonTableParent *node;
|
|
||||||
JsonTablePlan *childPlan;
|
|
||||||
bool defaultPlan = !plan || plan->plan_type == JSTP_DEFAULT;
|
|
||||||
|
|
||||||
if (!*pathName)
|
|
||||||
{
|
|
||||||
if (cxt->table->plan)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid JSON_TABLE expression"),
|
|
||||||
errdetail("JSON_TABLE columns must contain "
|
|
||||||
"explicit AS pathname specification if "
|
|
||||||
"explicit PLAN clause is used"),
|
|
||||||
parser_errposition(cxt->pstate, location)));
|
|
||||||
|
|
||||||
*pathName = generateJsonTablePathName(cxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultPlan)
|
|
||||||
childPlan = plan;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* validate parent and child plans */
|
|
||||||
JsonTablePlan *parentPlan;
|
|
||||||
|
|
||||||
if (plan->plan_type == JSTP_JOINED)
|
|
||||||
{
|
|
||||||
if (plan->join_type != JSTPJ_INNER &&
|
|
||||||
plan->join_type != JSTPJ_OUTER)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid JSON_TABLE plan"),
|
|
||||||
errdetail("Expected INNER or OUTER JSON_TABLE plan node."),
|
|
||||||
parser_errposition(cxt->pstate, plan->location)));
|
|
||||||
|
|
||||||
parentPlan = plan->plan1;
|
|
||||||
childPlan = plan->plan2;
|
|
||||||
|
|
||||||
Assert(parentPlan->plan_type != JSTP_JOINED);
|
|
||||||
Assert(parentPlan->pathname);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parentPlan = plan;
|
|
||||||
childPlan = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(parentPlan->pathname, *pathName))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("invalid JSON_TABLE plan"),
|
|
||||||
errdetail("Path name mismatch: expected %s but %s is given.",
|
|
||||||
*pathName, parentPlan->pathname),
|
|
||||||
parser_errposition(cxt->pstate, plan->location)));
|
|
||||||
|
|
||||||
validateJsonTableChildPlan(cxt->pstate, childPlan, columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* transform only non-nested columns */
|
|
||||||
node = makeParentJsonTableNode(cxt, pathSpec, columns);
|
|
||||||
node->name = pstrdup(*pathName);
|
|
||||||
|
|
||||||
if (childPlan || defaultPlan)
|
|
||||||
{
|
|
||||||
/* transform recursively nested columns */
|
|
||||||
node->child = transformJsonTableChildPlan(cxt, childPlan, columns);
|
|
||||||
if (node->child)
|
|
||||||
node->outerJoin = !plan || (plan->join_type & JSTPJ_OUTER);
|
|
||||||
/* else: default plan case, no children found */
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* transformJsonTable -
|
|
||||||
* Transform a raw JsonTable into TableFunc.
|
|
||||||
*
|
|
||||||
* Transform the document-generating expression, the row-generating expression,
|
|
||||||
* the column-generating expressions, and the default value expressions.
|
|
||||||
*/
|
|
||||||
ParseNamespaceItem *
|
|
||||||
transformJsonTable(ParseState *pstate, JsonTable *jt)
|
|
||||||
{
|
|
||||||
JsonTableContext cxt;
|
|
||||||
TableFunc *tf = makeNode(TableFunc);
|
|
||||||
JsonFuncExpr *jfe = makeNode(JsonFuncExpr);
|
|
||||||
JsonTablePlan *plan = jt->plan;
|
|
||||||
JsonCommon *jscommon;
|
|
||||||
char *rootPathName = jt->common->pathname;
|
|
||||||
char *rootPath;
|
|
||||||
bool is_lateral;
|
|
||||||
|
|
||||||
cxt.pstate = pstate;
|
|
||||||
cxt.table = jt;
|
|
||||||
cxt.tablefunc = tf;
|
|
||||||
cxt.pathNames = NIL;
|
|
||||||
cxt.pathNameId = 0;
|
|
||||||
|
|
||||||
if (rootPathName)
|
|
||||||
registerJsonTableColumn(&cxt, rootPathName);
|
|
||||||
|
|
||||||
registerAllJsonTableColumns(&cxt, jt->columns);
|
|
||||||
|
|
||||||
#if 0 /* XXX it' unclear from the standard whether
|
|
||||||
* root path name is mandatory or not */
|
|
||||||
if (plan && plan->plan_type != JSTP_DEFAULT && !rootPathName)
|
|
||||||
{
|
|
||||||
/* Assign root path name and create corresponding plan node */
|
|
||||||
JsonTablePlan *rootNode = makeNode(JsonTablePlan);
|
|
||||||
JsonTablePlan *rootPlan = (JsonTablePlan *)
|
|
||||||
makeJsonTableJoinedPlan(JSTPJ_OUTER, (Node *) rootNode,
|
|
||||||
(Node *) plan, jt->location);
|
|
||||||
|
|
||||||
rootPathName = generateJsonTablePathName(&cxt);
|
|
||||||
|
|
||||||
rootNode->plan_type = JSTP_SIMPLE;
|
|
||||||
rootNode->pathname = rootPathName;
|
|
||||||
|
|
||||||
plan = rootPlan;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
jscommon = copyObject(jt->common);
|
|
||||||
jscommon->pathspec = makeStringConst(pstrdup("$"), -1);
|
|
||||||
|
|
||||||
jfe->op = JSON_TABLE_OP;
|
|
||||||
jfe->common = jscommon;
|
|
||||||
jfe->on_error = jt->on_error;
|
|
||||||
jfe->location = jt->common->location;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We make lateral_only names of this level visible, whether or not the
|
|
||||||
* RangeTableFunc is explicitly marked LATERAL. This is needed for SQL
|
|
||||||
* spec compliance and seems useful on convenience grounds for all
|
|
||||||
* functions in FROM.
|
|
||||||
*
|
|
||||||
* (LATERAL can't nest within a single pstate level, so we don't need
|
|
||||||
* save/restore logic here.)
|
|
||||||
*/
|
|
||||||
Assert(!pstate->p_lateral_active);
|
|
||||||
pstate->p_lateral_active = true;
|
|
||||||
|
|
||||||
tf->functype = TFT_JSON_TABLE;
|
|
||||||
tf->docexpr = transformExpr(pstate, (Node *) jfe, EXPR_KIND_FROM_FUNCTION);
|
|
||||||
|
|
||||||
cxt.contextItemTypid = exprType(tf->docexpr);
|
|
||||||
|
|
||||||
if (!IsA(jt->common->pathspec, A_Const) ||
|
|
||||||
castNode(A_Const, jt->common->pathspec)->val.node.type != T_String)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("only string constants supported in JSON_TABLE path specification"),
|
|
||||||
parser_errposition(pstate,
|
|
||||||
exprLocation(jt->common->pathspec))));
|
|
||||||
|
|
||||||
rootPath = castNode(A_Const, jt->common->pathspec)->val.sval.sval;
|
|
||||||
|
|
||||||
tf->plan = (Node *) transformJsonTableColumns(&cxt, plan, jt->columns,
|
|
||||||
rootPath, &rootPathName,
|
|
||||||
jt->common->location);
|
|
||||||
|
|
||||||
tf->ordinalitycol = -1; /* undefine ordinality column number */
|
|
||||||
tf->location = jt->location;
|
|
||||||
|
|
||||||
pstate->p_lateral_active = false;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
|
|
||||||
* there are any lateral cross-references in it.
|
|
||||||
*/
|
|
||||||
is_lateral = jt->lateral || contain_vars_of_level((Node *) tf, 0);
|
|
||||||
|
|
||||||
return addRangeTableEntryForTableFunc(pstate,
|
|
||||||
tf, jt->alias, is_lateral, true);
|
|
||||||
}
|
|
@ -2017,7 +2017,7 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
|
|||||||
bool inFromCl)
|
bool inFromCl)
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||||
char *refname;
|
char *refname = alias ? alias->aliasname : pstrdup("xmltable");
|
||||||
Alias *eref;
|
Alias *eref;
|
||||||
int numaliases;
|
int numaliases;
|
||||||
|
|
||||||
@ -2035,8 +2035,7 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
|
|||||||
Assert(list_length(tf->coltypmods) == list_length(tf->colnames));
|
Assert(list_length(tf->coltypmods) == list_length(tf->colnames));
|
||||||
Assert(list_length(tf->colcollations) == list_length(tf->colnames));
|
Assert(list_length(tf->colcollations) == list_length(tf->colnames));
|
||||||
|
|
||||||
refname = alias ? alias->aliasname :
|
refname = alias ? alias->aliasname : pstrdup("xmltable");
|
||||||
pstrdup(tf->functype == TFT_XMLTABLE ? "xmltable" : "json_table");
|
|
||||||
|
|
||||||
rte->rtekind = RTE_TABLEFUNC;
|
rte->rtekind = RTE_TABLEFUNC;
|
||||||
rte->relid = InvalidOid;
|
rte->relid = InvalidOid;
|
||||||
@ -2059,7 +2058,7 @@ addRangeTableEntryForTableFunc(ParseState *pstate,
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||||
errmsg("%s function has %d columns available but %d columns specified",
|
errmsg("%s function has %d columns available but %d columns specified",
|
||||||
tf->functype == TFT_XMLTABLE ? "XMLTABLE" : "JSON_TABLE",
|
"XMLTABLE",
|
||||||
list_length(tf->colnames), numaliases)));
|
list_length(tf->colnames), numaliases)));
|
||||||
|
|
||||||
rte->eref = eref;
|
rte->eref = eref;
|
||||||
|
@ -1957,46 +1957,6 @@ FigureColnameInternal(Node *node, char **name)
|
|||||||
case T_XmlSerialize:
|
case T_XmlSerialize:
|
||||||
*name = "xmlserialize";
|
*name = "xmlserialize";
|
||||||
return 2;
|
return 2;
|
||||||
case T_JsonParseExpr:
|
|
||||||
*name = "json";
|
|
||||||
return 2;
|
|
||||||
case T_JsonScalarExpr:
|
|
||||||
*name = "json_scalar";
|
|
||||||
return 2;
|
|
||||||
case T_JsonSerializeExpr:
|
|
||||||
*name = "json_serialize";
|
|
||||||
return 2;
|
|
||||||
case T_JsonObjectConstructor:
|
|
||||||
*name = "json_object";
|
|
||||||
return 2;
|
|
||||||
case T_JsonArrayConstructor:
|
|
||||||
case T_JsonArrayQueryConstructor:
|
|
||||||
*name = "json_array";
|
|
||||||
return 2;
|
|
||||||
case T_JsonObjectAgg:
|
|
||||||
*name = "json_objectagg";
|
|
||||||
return 2;
|
|
||||||
case T_JsonArrayAgg:
|
|
||||||
*name = "json_arrayagg";
|
|
||||||
return 2;
|
|
||||||
case T_JsonFuncExpr:
|
|
||||||
/* make SQL/JSON functions act like a regular function */
|
|
||||||
switch (((JsonFuncExpr *) node)->op)
|
|
||||||
{
|
|
||||||
case JSON_QUERY_OP:
|
|
||||||
*name = "json_query";
|
|
||||||
return 2;
|
|
||||||
case JSON_VALUE_OP:
|
|
||||||
*name = "json_value";
|
|
||||||
return 2;
|
|
||||||
case JSON_EXISTS_OP:
|
|
||||||
*name = "json_exists";
|
|
||||||
return 2;
|
|
||||||
case JSON_TABLE_OP:
|
|
||||||
*name = "json_table";
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -150,9 +150,6 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
|||||||
case USCONST:
|
case USCONST:
|
||||||
cur_token_length = strlen(yyextra->core_yy_extra.scanbuf + *llocp);
|
cur_token_length = strlen(yyextra->core_yy_extra.scanbuf + *llocp);
|
||||||
break;
|
break;
|
||||||
case WITHOUT:
|
|
||||||
cur_token_length = 7;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return cur_token;
|
return cur_token;
|
||||||
}
|
}
|
||||||
@ -224,19 +221,6 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
|||||||
case ORDINALITY:
|
case ORDINALITY:
|
||||||
cur_token = WITH_LA;
|
cur_token = WITH_LA;
|
||||||
break;
|
break;
|
||||||
case UNIQUE:
|
|
||||||
cur_token = WITH_LA_UNIQUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WITHOUT:
|
|
||||||
/* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
|
|
||||||
switch (next_token)
|
|
||||||
{
|
|
||||||
case TIME:
|
|
||||||
cur_token = WITHOUT_LA;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -294,10 +294,6 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
|
|||||||
else
|
else
|
||||||
buf = pstrdup("character varying");
|
buf = pstrdup("character varying");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case JSONOID:
|
|
||||||
buf = pstrdup("json");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
|
@ -1045,6 +1045,11 @@ typedef struct NUMProc
|
|||||||
*L_currency_symbol;
|
*L_currency_symbol;
|
||||||
} NUMProc;
|
} NUMProc;
|
||||||
|
|
||||||
|
/* Return flags for DCH_from_char() */
|
||||||
|
#define DCH_DATED 0x01
|
||||||
|
#define DCH_TIMED 0x02
|
||||||
|
#define DCH_ZONED 0x04
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Functions
|
* Functions
|
||||||
* ----------
|
* ----------
|
||||||
@ -6707,43 +6712,3 @@ float8_to_char(PG_FUNCTION_ARGS)
|
|||||||
NUM_TOCHAR_finish;
|
NUM_TOCHAR_finish;
|
||||||
PG_RETURN_TEXT_P(result);
|
PG_RETURN_TEXT_P(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
datetime_format_flags(const char *fmt_str, bool *have_error)
|
|
||||||
{
|
|
||||||
bool incache;
|
|
||||||
int fmt_len = strlen(fmt_str);
|
|
||||||
int result;
|
|
||||||
FormatNode *format;
|
|
||||||
|
|
||||||
if (fmt_len > DCH_CACHE_SIZE)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Allocate new memory if format picture is bigger than static cache
|
|
||||||
* and do not use cache (call parser always)
|
|
||||||
*/
|
|
||||||
incache = false;
|
|
||||||
|
|
||||||
format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
|
|
||||||
|
|
||||||
parse_format(format, fmt_str, DCH_keywords,
|
|
||||||
DCH_suff, DCH_index, DCH_FLAG, NULL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Use cache buffers
|
|
||||||
*/
|
|
||||||
DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, false);
|
|
||||||
|
|
||||||
incache = true;
|
|
||||||
format = ent->format;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = DCH_datetime_type(format, have_error);
|
|
||||||
|
|
||||||
if (!incache)
|
|
||||||
pfree(format);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -13,10 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/hash.h"
|
|
||||||
#include "catalog/pg_proc.h"
|
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "common/hashfn.h"
|
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -30,41 +27,20 @@
|
|||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
/* Common context for key uniqueness check */
|
typedef enum /* type categories for datum_to_json */
|
||||||
typedef struct HTAB *JsonUniqueCheckState; /* hash table for key names */
|
|
||||||
|
|
||||||
/* Hash entry for JsonUniqueCheckState */
|
|
||||||
typedef struct JsonUniqueHashEntry
|
|
||||||
{
|
{
|
||||||
const char *key;
|
JSONTYPE_NULL, /* null, so we didn't bother to identify */
|
||||||
int key_len;
|
JSONTYPE_BOOL, /* boolean (built-in types only) */
|
||||||
int object_id;
|
JSONTYPE_NUMERIC, /* numeric (ditto) */
|
||||||
} JsonUniqueHashEntry;
|
JSONTYPE_DATE, /* we use special formatting for datetimes */
|
||||||
|
JSONTYPE_TIMESTAMP,
|
||||||
/* Context for key uniqueness check in builder functions */
|
JSONTYPE_TIMESTAMPTZ,
|
||||||
typedef struct JsonUniqueBuilderState
|
JSONTYPE_JSON, /* JSON itself (and JSONB) */
|
||||||
{
|
JSONTYPE_ARRAY, /* array */
|
||||||
JsonUniqueCheckState check; /* unique check */
|
JSONTYPE_COMPOSITE, /* composite */
|
||||||
StringInfoData skipped_keys; /* skipped keys with NULL values */
|
JSONTYPE_CAST, /* something with an explicit cast to JSON */
|
||||||
MemoryContext mcxt; /* context for saving skipped keys */
|
JSONTYPE_OTHER /* all else */
|
||||||
} JsonUniqueBuilderState;
|
} JsonTypeCategory;
|
||||||
|
|
||||||
/* Element of object stack for key uniqueness check during json parsing */
|
|
||||||
typedef struct JsonUniqueStackEntry
|
|
||||||
{
|
|
||||||
struct JsonUniqueStackEntry *parent;
|
|
||||||
int object_id;
|
|
||||||
} JsonUniqueStackEntry;
|
|
||||||
|
|
||||||
/* State for key uniqueness check during json parsing */
|
|
||||||
typedef struct JsonUniqueParsingState
|
|
||||||
{
|
|
||||||
JsonLexContext *lex;
|
|
||||||
JsonUniqueCheckState check;
|
|
||||||
JsonUniqueStackEntry *stack;
|
|
||||||
int id_counter;
|
|
||||||
bool unique;
|
|
||||||
} JsonUniqueParsingState;
|
|
||||||
|
|
||||||
typedef struct JsonAggState
|
typedef struct JsonAggState
|
||||||
{
|
{
|
||||||
@ -73,7 +49,6 @@ typedef struct JsonAggState
|
|||||||
Oid key_output_func;
|
Oid key_output_func;
|
||||||
JsonTypeCategory val_category;
|
JsonTypeCategory val_category;
|
||||||
Oid val_output_func;
|
Oid val_output_func;
|
||||||
JsonUniqueBuilderState unique_check;
|
|
||||||
} JsonAggState;
|
} JsonAggState;
|
||||||
|
|
||||||
static void composite_to_json(Datum composite, StringInfo result,
|
static void composite_to_json(Datum composite, StringInfo result,
|
||||||
@ -84,6 +59,9 @@ static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
|
|||||||
bool use_line_feeds);
|
bool use_line_feeds);
|
||||||
static void array_to_json_internal(Datum array, StringInfo result,
|
static void array_to_json_internal(Datum array, StringInfo result,
|
||||||
bool use_line_feeds);
|
bool use_line_feeds);
|
||||||
|
static void json_categorize_type(Oid typoid,
|
||||||
|
JsonTypeCategory *tcategory,
|
||||||
|
Oid *outfuncoid);
|
||||||
static void datum_to_json(Datum val, bool is_null, StringInfo result,
|
static void datum_to_json(Datum val, bool is_null, StringInfo result,
|
||||||
JsonTypeCategory tcategory, Oid outfuncoid,
|
JsonTypeCategory tcategory, Oid outfuncoid,
|
||||||
bool key_scalar);
|
bool key_scalar);
|
||||||
@ -162,7 +140,7 @@ json_recv(PG_FUNCTION_ARGS)
|
|||||||
* output function OID. If the returned category is JSONTYPE_CAST, we
|
* output function OID. If the returned category is JSONTYPE_CAST, we
|
||||||
* return the OID of the type->JSON cast function instead.
|
* return the OID of the type->JSON cast function instead.
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
json_categorize_type(Oid typoid,
|
json_categorize_type(Oid typoid,
|
||||||
JsonTypeCategory *tcategory,
|
JsonTypeCategory *tcategory,
|
||||||
Oid *outfuncoid)
|
Oid *outfuncoid)
|
||||||
@ -744,48 +722,6 @@ row_to_json_pretty(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
|
||||||
to_json_worker(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
|
|
||||||
{
|
|
||||||
StringInfo result = makeStringInfo();
|
|
||||||
|
|
||||||
datum_to_json(val, false, result, tcategory, outfuncoid, false);
|
|
||||||
|
|
||||||
return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
to_json_is_immutable(Oid typoid)
|
|
||||||
{
|
|
||||||
JsonTypeCategory tcategory;
|
|
||||||
Oid outfuncoid;
|
|
||||||
|
|
||||||
json_categorize_type(typoid, &tcategory, &outfuncoid);
|
|
||||||
|
|
||||||
switch (tcategory)
|
|
||||||
{
|
|
||||||
case JSONTYPE_BOOL:
|
|
||||||
case JSONTYPE_JSON:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case JSONTYPE_DATE:
|
|
||||||
case JSONTYPE_TIMESTAMP:
|
|
||||||
case JSONTYPE_TIMESTAMPTZ:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case JSONTYPE_ARRAY:
|
|
||||||
return false; /* TODO recurse into elements */
|
|
||||||
|
|
||||||
case JSONTYPE_COMPOSITE:
|
|
||||||
return false; /* TODO recurse into fields */
|
|
||||||
|
|
||||||
case JSONTYPE_NUMERIC:
|
|
||||||
case JSONTYPE_CAST:
|
|
||||||
default:
|
|
||||||
return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL function to_json(anyvalue)
|
* SQL function to_json(anyvalue)
|
||||||
*/
|
*/
|
||||||
@ -794,6 +730,7 @@ to_json(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Datum val = PG_GETARG_DATUM(0);
|
Datum val = PG_GETARG_DATUM(0);
|
||||||
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
||||||
|
StringInfo result;
|
||||||
JsonTypeCategory tcategory;
|
JsonTypeCategory tcategory;
|
||||||
Oid outfuncoid;
|
Oid outfuncoid;
|
||||||
|
|
||||||
@ -805,7 +742,11 @@ to_json(PG_FUNCTION_ARGS)
|
|||||||
json_categorize_type(val_type,
|
json_categorize_type(val_type,
|
||||||
&tcategory, &outfuncoid);
|
&tcategory, &outfuncoid);
|
||||||
|
|
||||||
PG_RETURN_DATUM(to_json_worker(val, tcategory, outfuncoid));
|
result = makeStringInfo();
|
||||||
|
|
||||||
|
datum_to_json(val, false, result, tcategory, outfuncoid, false);
|
||||||
|
|
||||||
|
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -813,8 +754,8 @@ to_json(PG_FUNCTION_ARGS)
|
|||||||
*
|
*
|
||||||
* aggregate input column as a json array value.
|
* aggregate input column as a json array value.
|
||||||
*/
|
*/
|
||||||
static Datum
|
Datum
|
||||||
json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
json_agg_transfn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
MemoryContext aggcontext,
|
MemoryContext aggcontext,
|
||||||
oldcontext;
|
oldcontext;
|
||||||
@ -854,13 +795,8 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
state = (JsonAggState *) PG_GETARG_POINTER(0);
|
state = (JsonAggState *) PG_GETARG_POINTER(0);
|
||||||
}
|
|
||||||
|
|
||||||
if (absent_on_null && PG_ARGISNULL(1))
|
|
||||||
PG_RETURN_POINTER(state);
|
|
||||||
|
|
||||||
if (state->str->len > 1)
|
|
||||||
appendStringInfoString(state->str, ", ");
|
appendStringInfoString(state->str, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
/* fast path for NULLs */
|
/* fast path for NULLs */
|
||||||
if (PG_ARGISNULL(1))
|
if (PG_ARGISNULL(1))
|
||||||
@ -873,7 +809,7 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
val = PG_GETARG_DATUM(1);
|
val = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
/* add some whitespace if structured type and not first item */
|
/* add some whitespace if structured type and not first item */
|
||||||
if (!PG_ARGISNULL(0) && state->str->len > 1 &&
|
if (!PG_ARGISNULL(0) &&
|
||||||
(state->val_category == JSONTYPE_ARRAY ||
|
(state->val_category == JSONTYPE_ARRAY ||
|
||||||
state->val_category == JSONTYPE_COMPOSITE))
|
state->val_category == JSONTYPE_COMPOSITE))
|
||||||
{
|
{
|
||||||
@ -891,25 +827,6 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
PG_RETURN_POINTER(state);
|
PG_RETURN_POINTER(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* json_agg aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_agg_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return json_agg_transfn_worker(fcinfo, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* json_agg_strict aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_agg_strict_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return json_agg_transfn_worker(fcinfo, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* json_agg final function
|
* json_agg final function
|
||||||
*/
|
*/
|
||||||
@ -933,108 +850,18 @@ json_agg_finalfn(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
|
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Functions implementing hash table for key uniqueness check */
|
|
||||||
static uint32
|
|
||||||
json_unique_hash(const void *key, Size keysize)
|
|
||||||
{
|
|
||||||
const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
|
|
||||||
uint32 hash = hash_bytes_uint32(entry->object_id);
|
|
||||||
|
|
||||||
hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
|
|
||||||
|
|
||||||
return DatumGetUInt32(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
json_unique_hash_match(const void *key1, const void *key2, Size keysize)
|
|
||||||
{
|
|
||||||
const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
|
|
||||||
const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
|
|
||||||
|
|
||||||
if (entry1->object_id != entry2->object_id)
|
|
||||||
return entry1->object_id > entry2->object_id ? 1 : -1;
|
|
||||||
|
|
||||||
if (entry1->key_len != entry2->key_len)
|
|
||||||
return entry1->key_len > entry2->key_len ? 1 : -1;
|
|
||||||
|
|
||||||
return strncmp(entry1->key, entry2->key, entry1->key_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions implementing object key uniqueness check */
|
|
||||||
static void
|
|
||||||
json_unique_check_init(JsonUniqueCheckState *cxt)
|
|
||||||
{
|
|
||||||
HASHCTL ctl;
|
|
||||||
|
|
||||||
memset(&ctl, 0, sizeof(ctl));
|
|
||||||
ctl.keysize = sizeof(JsonUniqueHashEntry);
|
|
||||||
ctl.entrysize = sizeof(JsonUniqueHashEntry);
|
|
||||||
ctl.hcxt = CurrentMemoryContext;
|
|
||||||
ctl.hash = json_unique_hash;
|
|
||||||
ctl.match = json_unique_hash_match;
|
|
||||||
|
|
||||||
*cxt = hash_create("json object hashtable",
|
|
||||||
32,
|
|
||||||
&ctl,
|
|
||||||
HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
|
|
||||||
{
|
|
||||||
JsonUniqueHashEntry entry;
|
|
||||||
bool found;
|
|
||||||
|
|
||||||
entry.key = key;
|
|
||||||
entry.key_len = strlen(key);
|
|
||||||
entry.object_id = object_id;
|
|
||||||
|
|
||||||
(void) hash_search(*cxt, &entry, HASH_ENTER, &found);
|
|
||||||
|
|
||||||
return !found;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
json_unique_builder_init(JsonUniqueBuilderState *cxt)
|
|
||||||
{
|
|
||||||
json_unique_check_init(&cxt->check);
|
|
||||||
cxt->mcxt = CurrentMemoryContext;
|
|
||||||
cxt->skipped_keys.data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On-demand initialization of skipped_keys StringInfo structure */
|
|
||||||
static StringInfo
|
|
||||||
json_unique_builder_get_skipped_keys(JsonUniqueBuilderState *cxt)
|
|
||||||
{
|
|
||||||
StringInfo out = &cxt->skipped_keys;
|
|
||||||
|
|
||||||
if (!out->data)
|
|
||||||
{
|
|
||||||
MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
|
|
||||||
|
|
||||||
initStringInfo(out);
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* json_object_agg transition function.
|
* json_object_agg transition function.
|
||||||
*
|
*
|
||||||
* aggregate two input columns as a single json object value.
|
* aggregate two input columns as a single json object value.
|
||||||
*/
|
*/
|
||||||
static Datum
|
Datum
|
||||||
json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
json_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||||
bool absent_on_null, bool unique_keys)
|
|
||||||
{
|
{
|
||||||
MemoryContext aggcontext,
|
MemoryContext aggcontext,
|
||||||
oldcontext;
|
oldcontext;
|
||||||
JsonAggState *state;
|
JsonAggState *state;
|
||||||
StringInfo out;
|
|
||||||
Datum arg;
|
Datum arg;
|
||||||
bool skip;
|
|
||||||
int key_offset;
|
|
||||||
|
|
||||||
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
||||||
{
|
{
|
||||||
@ -1055,10 +882,6 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
oldcontext = MemoryContextSwitchTo(aggcontext);
|
oldcontext = MemoryContextSwitchTo(aggcontext);
|
||||||
state = (JsonAggState *) palloc(sizeof(JsonAggState));
|
state = (JsonAggState *) palloc(sizeof(JsonAggState));
|
||||||
state->str = makeStringInfo();
|
state->str = makeStringInfo();
|
||||||
if (unique_keys)
|
|
||||||
json_unique_builder_init(&state->unique_check);
|
|
||||||
else
|
|
||||||
memset(&state->unique_check, 0, sizeof(state->unique_check));
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
||||||
@ -1086,6 +909,7 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
state = (JsonAggState *) PG_GETARG_POINTER(0);
|
state = (JsonAggState *) PG_GETARG_POINTER(0);
|
||||||
|
appendStringInfoString(state->str, ", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1101,49 +925,11 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("field name must not be null")));
|
errmsg("field name must not be null")));
|
||||||
|
|
||||||
/* Skip null values if absent_on_null */
|
|
||||||
skip = absent_on_null && PG_ARGISNULL(2);
|
|
||||||
|
|
||||||
if (skip)
|
|
||||||
{
|
|
||||||
/* If key uniqueness check is needed we must save skipped keys */
|
|
||||||
if (!unique_keys)
|
|
||||||
PG_RETURN_POINTER(state);
|
|
||||||
|
|
||||||
out = json_unique_builder_get_skipped_keys(&state->unique_check);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out = state->str;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Append comma delimiter only if we have already outputted some
|
|
||||||
* fields after the initial string "{ ".
|
|
||||||
*/
|
|
||||||
if (out->len > 2)
|
|
||||||
appendStringInfoString(out, ", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
arg = PG_GETARG_DATUM(1);
|
arg = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
key_offset = out->len;
|
datum_to_json(arg, false, state->str, state->key_category,
|
||||||
|
|
||||||
datum_to_json(arg, false, out, state->key_category,
|
|
||||||
state->key_output_func, true);
|
state->key_output_func, true);
|
||||||
|
|
||||||
if (unique_keys)
|
|
||||||
{
|
|
||||||
const char *key = &out->data[key_offset];
|
|
||||||
|
|
||||||
if (!json_unique_check_key(&state->unique_check.check, key, 0))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
|
||||||
errmsg("duplicate JSON key %s", key)));
|
|
||||||
|
|
||||||
if (skip)
|
|
||||||
PG_RETURN_POINTER(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoString(state->str, " : ");
|
appendStringInfoString(state->str, " : ");
|
||||||
|
|
||||||
if (PG_ARGISNULL(2))
|
if (PG_ARGISNULL(2))
|
||||||
@ -1157,42 +943,6 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
PG_RETURN_POINTER(state);
|
PG_RETURN_POINTER(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* json_object_agg aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_object_agg_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return json_object_agg_transfn_worker(fcinfo, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* json_object_agg_strict aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return json_object_agg_transfn_worker(fcinfo, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* json_object_agg_unique aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return json_object_agg_transfn_worker(fcinfo, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* json_object_agg_unique_strict aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return json_object_agg_transfn_worker(fcinfo, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* json_object_agg final function.
|
* json_object_agg final function.
|
||||||
*/
|
*/
|
||||||
@ -1234,14 +984,25 @@ catenate_stringinfo_string(StringInfo buffer, const char *addon)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function json_build_object(variadic "any")
|
||||||
|
*/
|
||||||
Datum
|
Datum
|
||||||
json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
json_build_object(PG_FUNCTION_ARGS)
|
||||||
bool absent_on_null, bool unique_keys)
|
|
||||||
{
|
{
|
||||||
|
int nargs;
|
||||||
int i;
|
int i;
|
||||||
const char *sep = "";
|
const char *sep = "";
|
||||||
StringInfo result;
|
StringInfo result;
|
||||||
JsonUniqueBuilderState unique_check;
|
Datum *args;
|
||||||
|
bool *nulls;
|
||||||
|
Oid *types;
|
||||||
|
|
||||||
|
/* fetch argument values to build the object */
|
||||||
|
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
|
||||||
|
|
||||||
|
if (nargs < 0)
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
if (nargs % 2 != 0)
|
if (nargs % 2 != 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -1255,32 +1016,10 @@ json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
|
|
||||||
appendStringInfoChar(result, '{');
|
appendStringInfoChar(result, '{');
|
||||||
|
|
||||||
if (unique_keys)
|
|
||||||
json_unique_builder_init(&unique_check);
|
|
||||||
|
|
||||||
for (i = 0; i < nargs; i += 2)
|
for (i = 0; i < nargs; i += 2)
|
||||||
{
|
|
||||||
StringInfo out;
|
|
||||||
bool skip;
|
|
||||||
int key_offset;
|
|
||||||
|
|
||||||
/* Skip null values if absent_on_null */
|
|
||||||
skip = absent_on_null && nulls[i + 1];
|
|
||||||
|
|
||||||
if (skip)
|
|
||||||
{
|
|
||||||
/* If key uniqueness check is needed we must save skipped keys */
|
|
||||||
if (!unique_keys)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
out = json_unique_builder_get_skipped_keys(&unique_check);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
appendStringInfoString(result, sep);
|
appendStringInfoString(result, sep);
|
||||||
sep = ", ";
|
sep = ", ";
|
||||||
out = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* process key */
|
/* process key */
|
||||||
if (nulls[i])
|
if (nulls[i])
|
||||||
@ -1289,24 +1028,7 @@ json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
errmsg("argument %d cannot be null", i + 1),
|
errmsg("argument %d cannot be null", i + 1),
|
||||||
errhint("Object keys should be text.")));
|
errhint("Object keys should be text.")));
|
||||||
|
|
||||||
/* save key offset before key appending */
|
add_json(args[i], false, result, types[i], true);
|
||||||
key_offset = out->len;
|
|
||||||
|
|
||||||
add_json(args[i], false, out, types[i], true);
|
|
||||||
|
|
||||||
if (unique_keys)
|
|
||||||
{
|
|
||||||
/* check key uniqueness after key appending */
|
|
||||||
const char *key = &out->data[key_offset];
|
|
||||||
|
|
||||||
if (!json_unique_check_key(&unique_check.check, key, 0))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
|
||||||
errmsg("duplicate JSON key %s", key)));
|
|
||||||
|
|
||||||
if (skip)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfoString(result, " : ");
|
appendStringInfoString(result, " : ");
|
||||||
|
|
||||||
@ -1316,27 +1038,7 @@ json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
|
|
||||||
appendStringInfoChar(result, '}');
|
appendStringInfoChar(result, '}');
|
||||||
|
|
||||||
return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
|
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SQL function json_build_object(variadic "any")
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_build_object(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
Datum *args;
|
|
||||||
bool *nulls;
|
|
||||||
Oid *types;
|
|
||||||
|
|
||||||
/* build argument values to build the object */
|
|
||||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
|
||||||
&args, &types, &nulls);
|
|
||||||
|
|
||||||
if (nargs < 0)
|
|
||||||
PG_RETURN_NULL();
|
|
||||||
|
|
||||||
PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1348,13 +1050,25 @@ json_build_object_noargs(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
|
PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function json_build_array(variadic "any")
|
||||||
|
*/
|
||||||
Datum
|
Datum
|
||||||
json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
json_build_array(PG_FUNCTION_ARGS)
|
||||||
bool absent_on_null)
|
|
||||||
{
|
{
|
||||||
|
int nargs;
|
||||||
int i;
|
int i;
|
||||||
const char *sep = "";
|
const char *sep = "";
|
||||||
StringInfo result;
|
StringInfo result;
|
||||||
|
Datum *args;
|
||||||
|
bool *nulls;
|
||||||
|
Oid *types;
|
||||||
|
|
||||||
|
/* fetch argument values to build the array */
|
||||||
|
nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
|
||||||
|
|
||||||
|
if (nargs < 0)
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
result = makeStringInfo();
|
result = makeStringInfo();
|
||||||
|
|
||||||
@ -1362,9 +1076,6 @@ json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
|
|
||||||
for (i = 0; i < nargs; i++)
|
for (i = 0; i < nargs; i++)
|
||||||
{
|
{
|
||||||
if (absent_on_null && nulls[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
appendStringInfoString(result, sep);
|
appendStringInfoString(result, sep);
|
||||||
sep = ", ";
|
sep = ", ";
|
||||||
add_json(args[i], nulls[i], result, types[i], false);
|
add_json(args[i], nulls[i], result, types[i], false);
|
||||||
@ -1372,27 +1083,7 @@ json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
|
|
||||||
appendStringInfoChar(result, ']');
|
appendStringInfoChar(result, ']');
|
||||||
|
|
||||||
return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
|
PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SQL function json_build_array(variadic "any")
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
json_build_array(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
Datum *args;
|
|
||||||
bool *nulls;
|
|
||||||
Oid *types;
|
|
||||||
|
|
||||||
/* build argument values to build the object */
|
|
||||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
|
||||||
&args, &types, &nulls);
|
|
||||||
|
|
||||||
if (nargs < 0)
|
|
||||||
PG_RETURN_NULL();
|
|
||||||
|
|
||||||
PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1618,106 +1309,6 @@ escape_json(StringInfo buf, const char *str)
|
|||||||
appendStringInfoCharMacro(buf, '"');
|
appendStringInfoCharMacro(buf, '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Semantic actions for key uniqueness check */
|
|
||||||
static void
|
|
||||||
json_unique_object_start(void *_state)
|
|
||||||
{
|
|
||||||
JsonUniqueParsingState *state = _state;
|
|
||||||
JsonUniqueStackEntry *entry;
|
|
||||||
|
|
||||||
if (!state->unique)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* push object entry to stack */
|
|
||||||
entry = palloc(sizeof(*entry));
|
|
||||||
entry->object_id = state->id_counter++;
|
|
||||||
entry->parent = state->stack;
|
|
||||||
state->stack = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
json_unique_object_end(void *_state)
|
|
||||||
{
|
|
||||||
JsonUniqueParsingState *state = _state;
|
|
||||||
JsonUniqueStackEntry *entry;
|
|
||||||
|
|
||||||
if (!state->unique)
|
|
||||||
return;
|
|
||||||
|
|
||||||
entry = state->stack;
|
|
||||||
state->stack = entry->parent; /* pop object from stack */
|
|
||||||
pfree(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
json_unique_object_field_start(void *_state, char *field, bool isnull)
|
|
||||||
{
|
|
||||||
JsonUniqueParsingState *state = _state;
|
|
||||||
JsonUniqueStackEntry *entry;
|
|
||||||
|
|
||||||
if (!state->unique)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* find key collision in the current object */
|
|
||||||
if (json_unique_check_key(&state->check, field, state->stack->object_id))
|
|
||||||
return;
|
|
||||||
|
|
||||||
state->unique = false;
|
|
||||||
|
|
||||||
/* pop all objects entries */
|
|
||||||
while ((entry = state->stack))
|
|
||||||
{
|
|
||||||
state->stack = entry->parent;
|
|
||||||
pfree(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate JSON text and additionally check key uniqueness */
|
|
||||||
bool
|
|
||||||
json_validate(text *json, bool check_unique_keys, bool throw_error)
|
|
||||||
{
|
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
|
|
||||||
JsonSemAction uniqueSemAction = {0};
|
|
||||||
JsonUniqueParsingState state;
|
|
||||||
JsonParseErrorType result;
|
|
||||||
|
|
||||||
if (check_unique_keys)
|
|
||||||
{
|
|
||||||
state.lex = lex;
|
|
||||||
state.stack = NULL;
|
|
||||||
state.id_counter = 0;
|
|
||||||
state.unique = true;
|
|
||||||
json_unique_check_init(&state.check);
|
|
||||||
|
|
||||||
uniqueSemAction.semstate = &state;
|
|
||||||
uniqueSemAction.object_start = json_unique_object_start;
|
|
||||||
uniqueSemAction.object_field_start = json_unique_object_field_start;
|
|
||||||
uniqueSemAction.object_end = json_unique_object_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
|
|
||||||
|
|
||||||
if (result != JSON_SUCCESS)
|
|
||||||
{
|
|
||||||
if (throw_error)
|
|
||||||
json_ereport_error(result, lex);
|
|
||||||
|
|
||||||
return false; /* invalid json */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_unique_keys && !state.unique)
|
|
||||||
{
|
|
||||||
if (throw_error)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
|
||||||
errmsg("duplicate JSON object key value")));
|
|
||||||
|
|
||||||
return false; /* not unique keys */
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; /* ok */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL function json_typeof(json) -> text
|
* SQL function json_typeof(json) -> text
|
||||||
*
|
*
|
||||||
@ -1733,13 +1324,21 @@ json_validate(text *json, bool check_unique_keys, bool throw_error)
|
|||||||
Datum
|
Datum
|
||||||
json_typeof(PG_FUNCTION_ARGS)
|
json_typeof(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json;
|
||||||
char *type;
|
|
||||||
|
JsonLexContext *lex;
|
||||||
JsonTokenType tok;
|
JsonTokenType tok;
|
||||||
|
char *type;
|
||||||
|
JsonParseErrorType result;
|
||||||
|
|
||||||
|
json = PG_GETARG_TEXT_PP(0);
|
||||||
|
lex = makeJsonLexContext(json, false);
|
||||||
|
|
||||||
/* Lex exactly one token from the input and check its type. */
|
/* Lex exactly one token from the input and check its type. */
|
||||||
tok = json_get_first_token(json, true);
|
result = json_lex(lex);
|
||||||
|
if (result != JSON_SUCCESS)
|
||||||
|
json_ereport_error(result, lex);
|
||||||
|
tok = lex->token_type;
|
||||||
switch (tok)
|
switch (tok)
|
||||||
{
|
{
|
||||||
case JSON_TOKEN_OBJECT_START:
|
case JSON_TOKEN_OBJECT_START:
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include "access/htup_details.h"
|
#include "access/htup_details.h"
|
||||||
#include "access/transam.h"
|
#include "access/transam.h"
|
||||||
#include "catalog/pg_proc.h"
|
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
@ -34,9 +33,25 @@ typedef struct JsonbInState
|
|||||||
{
|
{
|
||||||
JsonbParseState *parseState;
|
JsonbParseState *parseState;
|
||||||
JsonbValue *res;
|
JsonbValue *res;
|
||||||
bool unique_keys;
|
|
||||||
} JsonbInState;
|
} JsonbInState;
|
||||||
|
|
||||||
|
/* unlike with json categories, we need to treat json and jsonb differently */
|
||||||
|
typedef enum /* type categories for datum_to_jsonb */
|
||||||
|
{
|
||||||
|
JSONBTYPE_NULL, /* null, so we didn't bother to identify */
|
||||||
|
JSONBTYPE_BOOL, /* boolean (built-in types only) */
|
||||||
|
JSONBTYPE_NUMERIC, /* numeric (ditto) */
|
||||||
|
JSONBTYPE_DATE, /* we use special formatting for datetimes */
|
||||||
|
JSONBTYPE_TIMESTAMP, /* we use special formatting for timestamp */
|
||||||
|
JSONBTYPE_TIMESTAMPTZ, /* ... and timestamptz */
|
||||||
|
JSONBTYPE_JSON, /* JSON */
|
||||||
|
JSONBTYPE_JSONB, /* JSONB */
|
||||||
|
JSONBTYPE_ARRAY, /* array */
|
||||||
|
JSONBTYPE_COMPOSITE, /* composite */
|
||||||
|
JSONBTYPE_JSONCAST, /* something with an explicit cast to JSON */
|
||||||
|
JSONBTYPE_OTHER /* all else */
|
||||||
|
} JsonbTypeCategory;
|
||||||
|
|
||||||
typedef struct JsonbAggState
|
typedef struct JsonbAggState
|
||||||
{
|
{
|
||||||
JsonbInState *res;
|
JsonbInState *res;
|
||||||
@ -46,7 +61,7 @@ typedef struct JsonbAggState
|
|||||||
Oid val_output_func;
|
Oid val_output_func;
|
||||||
} JsonbAggState;
|
} JsonbAggState;
|
||||||
|
|
||||||
static inline Datum jsonb_from_cstring(char *json, int len, bool unique_keys);
|
static inline Datum jsonb_from_cstring(char *json, int len);
|
||||||
static size_t checkStringLen(size_t len);
|
static size_t checkStringLen(size_t len);
|
||||||
static void jsonb_in_object_start(void *pstate);
|
static void jsonb_in_object_start(void *pstate);
|
||||||
static void jsonb_in_object_end(void *pstate);
|
static void jsonb_in_object_end(void *pstate);
|
||||||
@ -55,11 +70,17 @@ static void jsonb_in_array_end(void *pstate);
|
|||||||
static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
|
static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
|
||||||
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
|
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
|
||||||
static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
|
static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
|
||||||
|
static void jsonb_categorize_type(Oid typoid,
|
||||||
|
JsonbTypeCategory *tcategory,
|
||||||
|
Oid *outfuncoid);
|
||||||
static void composite_to_jsonb(Datum composite, JsonbInState *result);
|
static void composite_to_jsonb(Datum composite, JsonbInState *result);
|
||||||
static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
|
static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
|
||||||
Datum *vals, bool *nulls, int *valcount,
|
Datum *vals, bool *nulls, int *valcount,
|
||||||
JsonbTypeCategory tcategory, Oid outfuncoid);
|
JsonbTypeCategory tcategory, Oid outfuncoid);
|
||||||
static void array_to_jsonb_internal(Datum array, JsonbInState *result);
|
static void array_to_jsonb_internal(Datum array, JsonbInState *result);
|
||||||
|
static void jsonb_categorize_type(Oid typoid,
|
||||||
|
JsonbTypeCategory *tcategory,
|
||||||
|
Oid *outfuncoid);
|
||||||
static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
|
static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
|
||||||
JsonbTypeCategory tcategory, Oid outfuncoid,
|
JsonbTypeCategory tcategory, Oid outfuncoid,
|
||||||
bool key_scalar);
|
bool key_scalar);
|
||||||
@ -77,7 +98,7 @@ jsonb_in(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
char *json = PG_GETARG_CSTRING(0);
|
char *json = PG_GETARG_CSTRING(0);
|
||||||
|
|
||||||
return jsonb_from_cstring(json, strlen(json), false);
|
return jsonb_from_cstring(json, strlen(json));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -101,7 +122,7 @@ jsonb_recv(PG_FUNCTION_ARGS)
|
|||||||
else
|
else
|
||||||
elog(ERROR, "unsupported jsonb version number %d", version);
|
elog(ERROR, "unsupported jsonb version number %d", version);
|
||||||
|
|
||||||
return jsonb_from_cstring(str, nbytes, false);
|
return jsonb_from_cstring(str, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -142,14 +163,6 @@ jsonb_send(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
|
||||||
jsonb_from_text(text *js, bool unique_keys)
|
|
||||||
{
|
|
||||||
return jsonb_from_cstring(VARDATA_ANY(js),
|
|
||||||
VARSIZE_ANY_EXHDR(js),
|
|
||||||
unique_keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the type name of a jsonb container.
|
* Get the type name of a jsonb container.
|
||||||
*/
|
*/
|
||||||
@ -240,7 +253,7 @@ jsonb_typeof(PG_FUNCTION_ARGS)
|
|||||||
* Uses the json parser (with hooks) to construct a jsonb.
|
* Uses the json parser (with hooks) to construct a jsonb.
|
||||||
*/
|
*/
|
||||||
static inline Datum
|
static inline Datum
|
||||||
jsonb_from_cstring(char *json, int len, bool unique_keys)
|
jsonb_from_cstring(char *json, int len)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex;
|
JsonLexContext *lex;
|
||||||
JsonbInState state;
|
JsonbInState state;
|
||||||
@ -250,8 +263,6 @@ jsonb_from_cstring(char *json, int len, bool unique_keys)
|
|||||||
memset(&sem, 0, sizeof(sem));
|
memset(&sem, 0, sizeof(sem));
|
||||||
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
|
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
|
||||||
|
|
||||||
state.unique_keys = unique_keys;
|
|
||||||
|
|
||||||
sem.semstate = (void *) &state;
|
sem.semstate = (void *) &state;
|
||||||
|
|
||||||
sem.object_start = jsonb_in_object_start;
|
sem.object_start = jsonb_in_object_start;
|
||||||
@ -286,7 +297,6 @@ jsonb_in_object_start(void *pstate)
|
|||||||
JsonbInState *_state = (JsonbInState *) pstate;
|
JsonbInState *_state = (JsonbInState *) pstate;
|
||||||
|
|
||||||
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
|
_state->res = pushJsonbValue(&_state->parseState, WJB_BEGIN_OBJECT, NULL);
|
||||||
_state->parseState->unique_keys = _state->unique_keys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -609,7 +619,7 @@ add_indent(StringInfo out, bool indent, int level)
|
|||||||
* output function OID. If the returned category is JSONBTYPE_JSONCAST,
|
* output function OID. If the returned category is JSONBTYPE_JSONCAST,
|
||||||
* we return the OID of the relevant cast function instead.
|
* we return the OID of the relevant cast function instead.
|
||||||
*/
|
*/
|
||||||
void
|
static void
|
||||||
jsonb_categorize_type(Oid typoid,
|
jsonb_categorize_type(Oid typoid,
|
||||||
JsonbTypeCategory *tcategory,
|
JsonbTypeCategory *tcategory,
|
||||||
Oid *outfuncoid)
|
Oid *outfuncoid)
|
||||||
@ -1115,51 +1125,6 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
|
|||||||
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
|
datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
|
||||||
to_jsonb_worker(Datum val, JsonbTypeCategory tcategory, Oid outfuncoid)
|
|
||||||
{
|
|
||||||
JsonbInState result;
|
|
||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
|
||||||
|
|
||||||
datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
|
|
||||||
|
|
||||||
return JsonbPGetDatum(JsonbValueToJsonb(result.res));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
to_jsonb_is_immutable(Oid typoid)
|
|
||||||
{
|
|
||||||
JsonbTypeCategory tcategory;
|
|
||||||
Oid outfuncoid;
|
|
||||||
|
|
||||||
jsonb_categorize_type(typoid, &tcategory, &outfuncoid);
|
|
||||||
|
|
||||||
switch (tcategory)
|
|
||||||
{
|
|
||||||
case JSONBTYPE_BOOL:
|
|
||||||
case JSONBTYPE_JSON:
|
|
||||||
case JSONBTYPE_JSONB:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case JSONBTYPE_DATE:
|
|
||||||
case JSONBTYPE_TIMESTAMP:
|
|
||||||
case JSONBTYPE_TIMESTAMPTZ:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case JSONBTYPE_ARRAY:
|
|
||||||
return false; /* TODO recurse into elements */
|
|
||||||
|
|
||||||
case JSONBTYPE_COMPOSITE:
|
|
||||||
return false; /* TODO recurse into fields */
|
|
||||||
|
|
||||||
case JSONBTYPE_NUMERIC:
|
|
||||||
case JSONBTYPE_JSONCAST:
|
|
||||||
default:
|
|
||||||
return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL function to_jsonb(anyvalue)
|
* SQL function to_jsonb(anyvalue)
|
||||||
*/
|
*/
|
||||||
@ -1168,6 +1133,7 @@ to_jsonb(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Datum val = PG_GETARG_DATUM(0);
|
Datum val = PG_GETARG_DATUM(0);
|
||||||
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
||||||
|
JsonbInState result;
|
||||||
JsonbTypeCategory tcategory;
|
JsonbTypeCategory tcategory;
|
||||||
Oid outfuncoid;
|
Oid outfuncoid;
|
||||||
|
|
||||||
@ -1179,15 +1145,31 @@ to_jsonb(PG_FUNCTION_ARGS)
|
|||||||
jsonb_categorize_type(val_type,
|
jsonb_categorize_type(val_type,
|
||||||
&tcategory, &outfuncoid);
|
&tcategory, &outfuncoid);
|
||||||
|
|
||||||
PG_RETURN_DATUM(to_jsonb_worker(val, tcategory, outfuncoid));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
|
|
||||||
|
datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL function jsonb_build_object(variadic "any")
|
||||||
|
*/
|
||||||
Datum
|
Datum
|
||||||
jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
jsonb_build_object(PG_FUNCTION_ARGS)
|
||||||
bool absent_on_null, bool unique_keys)
|
|
||||||
{
|
{
|
||||||
|
int nargs;
|
||||||
int i;
|
int i;
|
||||||
JsonbInState result;
|
JsonbInState result;
|
||||||
|
Datum *args;
|
||||||
|
bool *nulls;
|
||||||
|
Oid *types;
|
||||||
|
|
||||||
|
/* build argument values to build the object */
|
||||||
|
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
|
||||||
|
|
||||||
|
if (nargs < 0)
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
if (nargs % 2 != 0)
|
if (nargs % 2 != 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -1200,26 +1182,15 @@ jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
memset(&result, 0, sizeof(JsonbInState));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
|
|
||||||
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
|
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
|
||||||
result.parseState->unique_keys = unique_keys;
|
|
||||||
result.parseState->skip_nulls = absent_on_null;
|
|
||||||
|
|
||||||
for (i = 0; i < nargs; i += 2)
|
for (i = 0; i < nargs; i += 2)
|
||||||
{
|
{
|
||||||
/* process key */
|
/* process key */
|
||||||
bool skip;
|
|
||||||
|
|
||||||
if (nulls[i])
|
if (nulls[i])
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("argument %d: key must not be null", i + 1)));
|
errmsg("argument %d: key must not be null", i + 1)));
|
||||||
|
|
||||||
/* skip null values if absent_on_null */
|
|
||||||
skip = absent_on_null && nulls[i + 1];
|
|
||||||
|
|
||||||
/* we need to save skipped keys for the key uniqueness check */
|
|
||||||
if (skip && !unique_keys)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
add_jsonb(args[i], false, &result, types[i], true);
|
add_jsonb(args[i], false, &result, types[i], true);
|
||||||
|
|
||||||
/* process value */
|
/* process value */
|
||||||
@ -1228,27 +1199,7 @@ jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|||||||
|
|
||||||
result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
|
result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
|
||||||
|
|
||||||
return JsonbPGetDatum(JsonbValueToJsonb(result.res));
|
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SQL function jsonb_build_object(variadic "any")
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_build_object(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
Datum *args;
|
|
||||||
bool *nulls;
|
|
||||||
Oid *types;
|
|
||||||
|
|
||||||
/* build argument values to build the object */
|
|
||||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
|
||||||
&args, &types, &nulls);
|
|
||||||
|
|
||||||
if (nargs < 0)
|
|
||||||
PG_RETURN_NULL();
|
|
||||||
|
|
||||||
PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1267,50 +1218,36 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
|
||||||
jsonb_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
|
|
||||||
bool absent_on_null)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
JsonbInState result;
|
|
||||||
|
|
||||||
memset(&result, 0, sizeof(JsonbInState));
|
|
||||||
|
|
||||||
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
|
|
||||||
|
|
||||||
for (i = 0; i < nargs; i++)
|
|
||||||
{
|
|
||||||
if (absent_on_null && nulls[i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
add_jsonb(args[i], nulls[i], &result, types[i], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
|
|
||||||
|
|
||||||
return JsonbPGetDatum(JsonbValueToJsonb(result.res));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL function jsonb_build_array(variadic "any")
|
* SQL function jsonb_build_array(variadic "any")
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
jsonb_build_array(PG_FUNCTION_ARGS)
|
jsonb_build_array(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
int nargs;
|
||||||
|
int i;
|
||||||
|
JsonbInState result;
|
||||||
Datum *args;
|
Datum *args;
|
||||||
bool *nulls;
|
bool *nulls;
|
||||||
Oid *types;
|
Oid *types;
|
||||||
|
|
||||||
/* build argument values to build the object */
|
/* build argument values to build the array */
|
||||||
int nargs = extract_variadic_args(fcinfo, 0, true,
|
nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
|
||||||
&args, &types, &nulls);
|
|
||||||
|
|
||||||
if (nargs < 0)
|
if (nargs < 0)
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false));
|
memset(&result, 0, sizeof(JsonbInState));
|
||||||
}
|
|
||||||
|
|
||||||
|
result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
|
add_jsonb(args[i], nulls[i], &result, types[i], false);
|
||||||
|
|
||||||
|
result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* degenerate case of jsonb_build_array where it gets 0 arguments.
|
* degenerate case of jsonb_build_array where it gets 0 arguments.
|
||||||
@ -1545,8 +1482,6 @@ clone_parse_state(JsonbParseState *state)
|
|||||||
{
|
{
|
||||||
ocursor->contVal = icursor->contVal;
|
ocursor->contVal = icursor->contVal;
|
||||||
ocursor->size = icursor->size;
|
ocursor->size = icursor->size;
|
||||||
ocursor->unique_keys = icursor->unique_keys;
|
|
||||||
ocursor->skip_nulls = icursor->skip_nulls;
|
|
||||||
icursor = icursor->next;
|
icursor = icursor->next;
|
||||||
if (icursor == NULL)
|
if (icursor == NULL)
|
||||||
break;
|
break;
|
||||||
@ -1558,8 +1493,12 @@ clone_parse_state(JsonbParseState *state)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Datum
|
|
||||||
jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
/*
|
||||||
|
* jsonb_agg aggregate function
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
jsonb_agg_transfn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
MemoryContext oldcontext,
|
MemoryContext oldcontext,
|
||||||
aggcontext;
|
aggcontext;
|
||||||
@ -1607,9 +1546,6 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
result = state->res;
|
result = state->res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absent_on_null && PG_ARGISNULL(1))
|
|
||||||
PG_RETURN_POINTER(state);
|
|
||||||
|
|
||||||
/* turn the argument into jsonb in the normal function context */
|
/* turn the argument into jsonb in the normal function context */
|
||||||
|
|
||||||
val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
|
val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
|
||||||
@ -1679,24 +1615,6 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
|
|||||||
PG_RETURN_POINTER(state);
|
PG_RETURN_POINTER(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* jsonb_agg aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_agg_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return jsonb_agg_transfn_worker(fcinfo, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* jsonb_agg_strict aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_agg_strict_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return jsonb_agg_transfn_worker(fcinfo, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
jsonb_agg_finalfn(PG_FUNCTION_ARGS)
|
jsonb_agg_finalfn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -1729,9 +1647,11 @@ jsonb_agg_finalfn(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_POINTER(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Datum
|
/*
|
||||||
jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
* jsonb_object_agg aggregate function
|
||||||
bool absent_on_null, bool unique_keys)
|
*/
|
||||||
|
Datum
|
||||||
|
jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
MemoryContext oldcontext,
|
MemoryContext oldcontext,
|
||||||
aggcontext;
|
aggcontext;
|
||||||
@ -1745,7 +1665,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
*jbval;
|
*jbval;
|
||||||
JsonbValue v;
|
JsonbValue v;
|
||||||
JsonbIteratorToken type;
|
JsonbIteratorToken type;
|
||||||
bool skip;
|
|
||||||
|
|
||||||
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
||||||
{
|
{
|
||||||
@ -1765,9 +1684,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
state->res = result;
|
state->res = result;
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
result->res = pushJsonbValue(&result->parseState,
|
||||||
WJB_BEGIN_OBJECT, NULL);
|
WJB_BEGIN_OBJECT, NULL);
|
||||||
result->parseState->unique_keys = unique_keys;
|
|
||||||
result->parseState->skip_nulls = absent_on_null;
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
||||||
@ -1803,15 +1719,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("field name must not be null")));
|
errmsg("field name must not be null")));
|
||||||
|
|
||||||
/*
|
|
||||||
* Skip null values if absent_on_null unless key uniqueness check is
|
|
||||||
* needed (because we must save keys in this case).
|
|
||||||
*/
|
|
||||||
skip = absent_on_null && PG_ARGISNULL(2);
|
|
||||||
|
|
||||||
if (skip && !unique_keys)
|
|
||||||
PG_RETURN_POINTER(state);
|
|
||||||
|
|
||||||
val = PG_GETARG_DATUM(1);
|
val = PG_GETARG_DATUM(1);
|
||||||
|
|
||||||
memset(&elem, 0, sizeof(JsonbInState));
|
memset(&elem, 0, sizeof(JsonbInState));
|
||||||
@ -1867,16 +1774,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
}
|
}
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
result->res = pushJsonbValue(&result->parseState,
|
||||||
WJB_KEY, &v);
|
WJB_KEY, &v);
|
||||||
|
|
||||||
if (skip)
|
|
||||||
{
|
|
||||||
v.type = jbvNull;
|
|
||||||
result->res = pushJsonbValue(&result->parseState,
|
|
||||||
WJB_VALUE, &v);
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
PG_RETURN_POINTER(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case WJB_END_ARRAY:
|
case WJB_END_ARRAY:
|
||||||
break;
|
break;
|
||||||
@ -1949,43 +1846,6 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
|
|||||||
PG_RETURN_POINTER(state);
|
PG_RETURN_POINTER(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* jsonb_object_agg aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return jsonb_object_agg_transfn_worker(fcinfo, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* jsonb_object_agg_strict aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_object_agg_strict_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return jsonb_object_agg_transfn_worker(fcinfo, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* jsonb_object_agg_unique aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_object_agg_unique_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return jsonb_object_agg_transfn_worker(fcinfo, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* jsonb_object_agg_unique_strict aggregate function
|
|
||||||
*/
|
|
||||||
Datum
|
|
||||||
jsonb_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
|
|
||||||
{
|
|
||||||
return jsonb_object_agg_transfn_worker(fcinfo, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
|
jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
@ -2217,65 +2077,3 @@ jsonb_float8(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
PG_RETURN_DATUM(retValue);
|
PG_RETURN_DATUM(retValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Construct an empty array jsonb.
|
|
||||||
*/
|
|
||||||
Jsonb *
|
|
||||||
JsonbMakeEmptyArray(void)
|
|
||||||
{
|
|
||||||
JsonbValue jbv;
|
|
||||||
|
|
||||||
jbv.type = jbvArray;
|
|
||||||
jbv.val.array.elems = NULL;
|
|
||||||
jbv.val.array.nElems = 0;
|
|
||||||
jbv.val.array.rawScalar = false;
|
|
||||||
|
|
||||||
return JsonbValueToJsonb(&jbv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Construct an empty object jsonb.
|
|
||||||
*/
|
|
||||||
Jsonb *
|
|
||||||
JsonbMakeEmptyObject(void)
|
|
||||||
{
|
|
||||||
JsonbValue jbv;
|
|
||||||
|
|
||||||
jbv.type = jbvObject;
|
|
||||||
jbv.val.object.pairs = NULL;
|
|
||||||
jbv.val.object.nPairs = 0;
|
|
||||||
|
|
||||||
return JsonbValueToJsonb(&jbv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert jsonb to a C-string stripping quotes from scalar strings.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
JsonbUnquote(Jsonb *jb)
|
|
||||||
{
|
|
||||||
if (JB_ROOT_IS_SCALAR(jb))
|
|
||||||
{
|
|
||||||
JsonbValue v;
|
|
||||||
|
|
||||||
(void) JsonbExtractScalar(&jb->root, &v);
|
|
||||||
|
|
||||||
if (v.type == jbvString)
|
|
||||||
return pnstrdup(v.val.string.val, v.val.string.len);
|
|
||||||
else if (v.type == jbvBool)
|
|
||||||
return pstrdup(v.val.boolean ? "true" : "false");
|
|
||||||
else if (v.type == jbvNumeric)
|
|
||||||
return DatumGetCString(DirectFunctionCall1(numeric_out,
|
|
||||||
PointerGetDatum(v.val.numeric)));
|
|
||||||
else if (v.type == jbvNull)
|
|
||||||
return pstrdup("null");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elog(ERROR, "unrecognized jsonb value type %d", v.type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return JsonbToCString(NULL, &jb->root, VARSIZE(jb));
|
|
||||||
}
|
|
||||||
|
@ -64,8 +64,7 @@ static int lengthCompareJsonbStringValue(const void *a, const void *b);
|
|||||||
static int lengthCompareJsonbString(const char *val1, int len1,
|
static int lengthCompareJsonbString(const char *val1, int len1,
|
||||||
const char *val2, int len2);
|
const char *val2, int len2);
|
||||||
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
|
static int lengthCompareJsonbPair(const void *a, const void *b, void *arg);
|
||||||
static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
|
static void uniqueifyJsonbObject(JsonbValue *object);
|
||||||
bool skip_nulls);
|
|
||||||
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
|
static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
|
||||||
JsonbIteratorToken seq,
|
JsonbIteratorToken seq,
|
||||||
JsonbValue *scalarVal);
|
JsonbValue *scalarVal);
|
||||||
@ -690,9 +689,7 @@ pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq,
|
|||||||
appendElement(*pstate, scalarVal);
|
appendElement(*pstate, scalarVal);
|
||||||
break;
|
break;
|
||||||
case WJB_END_OBJECT:
|
case WJB_END_OBJECT:
|
||||||
uniqueifyJsonbObject(&(*pstate)->contVal,
|
uniqueifyJsonbObject(&(*pstate)->contVal);
|
||||||
(*pstate)->unique_keys,
|
|
||||||
(*pstate)->skip_nulls);
|
|
||||||
/* fall through! */
|
/* fall through! */
|
||||||
case WJB_END_ARRAY:
|
case WJB_END_ARRAY:
|
||||||
/* Steps here common to WJB_END_OBJECT case */
|
/* Steps here common to WJB_END_OBJECT case */
|
||||||
@ -735,9 +732,6 @@ pushState(JsonbParseState **pstate)
|
|||||||
JsonbParseState *ns = palloc(sizeof(JsonbParseState));
|
JsonbParseState *ns = palloc(sizeof(JsonbParseState));
|
||||||
|
|
||||||
ns->next = *pstate;
|
ns->next = *pstate;
|
||||||
ns->unique_keys = false;
|
|
||||||
ns->skip_nulls = false;
|
|
||||||
|
|
||||||
return ns;
|
return ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1942,7 +1936,7 @@ lengthCompareJsonbPair(const void *a, const void *b, void *binequal)
|
|||||||
* Sort and unique-ify pairs in JsonbValue object
|
* Sort and unique-ify pairs in JsonbValue object
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
|
uniqueifyJsonbObject(JsonbValue *object)
|
||||||
{
|
{
|
||||||
bool hasNonUniq = false;
|
bool hasNonUniq = false;
|
||||||
|
|
||||||
@ -1952,32 +1946,15 @@ uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
|
|||||||
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
|
qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
|
||||||
lengthCompareJsonbPair, &hasNonUniq);
|
lengthCompareJsonbPair, &hasNonUniq);
|
||||||
|
|
||||||
if (hasNonUniq && unique_keys)
|
if (hasNonUniq)
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
|
|
||||||
errmsg("duplicate JSON object key value")));
|
|
||||||
|
|
||||||
if (hasNonUniq || skip_nulls)
|
|
||||||
{
|
{
|
||||||
JsonbPair *ptr,
|
JsonbPair *ptr = object->val.object.pairs + 1,
|
||||||
*res;
|
*res = object->val.object.pairs;
|
||||||
|
|
||||||
while (skip_nulls && object->val.object.nPairs > 0 &&
|
|
||||||
object->val.object.pairs->value.type == jbvNull)
|
|
||||||
{
|
|
||||||
/* If skip_nulls is true, remove leading items with null */
|
|
||||||
object->val.object.pairs++;
|
|
||||||
object->val.object.nPairs--;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = object->val.object.pairs + 1;
|
|
||||||
res = object->val.object.pairs;
|
|
||||||
|
|
||||||
while (ptr - object->val.object.pairs < object->val.object.nPairs)
|
while (ptr - object->val.object.pairs < object->val.object.nPairs)
|
||||||
{
|
{
|
||||||
/* Avoid copying over duplicate or null */
|
/* Avoid copying over duplicate */
|
||||||
if (lengthCompareJsonbStringValue(ptr, res) != 0 &&
|
if (lengthCompareJsonbStringValue(ptr, res) != 0)
|
||||||
(!skip_nulls || ptr->value.type != jbvNull))
|
|
||||||
{
|
{
|
||||||
res++;
|
res++;
|
||||||
if (ptr != res)
|
if (ptr != res)
|
||||||
|
@ -2656,11 +2656,11 @@ populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
|
|||||||
|
|
||||||
check_stack_depth();
|
check_stack_depth();
|
||||||
|
|
||||||
if (jbv->type != jbvBinary ||
|
if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc))
|
||||||
!JsonContainerIsArray(jbc) ||
|
|
||||||
JsonContainerIsScalar(jbc))
|
|
||||||
populate_array_report_expected_array(ctx, ndim - 1);
|
populate_array_report_expected_array(ctx, ndim - 1);
|
||||||
|
|
||||||
|
Assert(!JsonContainerIsScalar(jbc));
|
||||||
|
|
||||||
it = JsonbIteratorInit(jbc);
|
it = JsonbIteratorInit(jbc);
|
||||||
|
|
||||||
tok = JsonbIteratorNext(&it, &val, true);
|
tok = JsonbIteratorNext(&it, &val, true);
|
||||||
@ -3132,51 +3132,6 @@ populate_record_field(ColumnIOData *col,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* recursively populate specified type from a json/jsonb value */
|
|
||||||
Datum
|
|
||||||
json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod,
|
|
||||||
void **cache, MemoryContext mcxt, bool *isnull)
|
|
||||||
{
|
|
||||||
JsValue jsv = {0};
|
|
||||||
JsonbValue jbv;
|
|
||||||
|
|
||||||
jsv.is_json = json_type == JSONOID;
|
|
||||||
|
|
||||||
if (*isnull)
|
|
||||||
{
|
|
||||||
if (jsv.is_json)
|
|
||||||
jsv.val.json.str = NULL;
|
|
||||||
else
|
|
||||||
jsv.val.jsonb = NULL;
|
|
||||||
}
|
|
||||||
else if (jsv.is_json)
|
|
||||||
{
|
|
||||||
text *json = DatumGetTextPP(json_val);
|
|
||||||
|
|
||||||
jsv.val.json.str = VARDATA_ANY(json);
|
|
||||||
jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
|
|
||||||
jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in
|
|
||||||
* populate_composite() */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Jsonb *jsonb = DatumGetJsonbP(json_val);
|
|
||||||
|
|
||||||
jsv.val.jsonb = &jbv;
|
|
||||||
|
|
||||||
/* fill binary jsonb value pointing to jb */
|
|
||||||
jbv.type = jbvBinary;
|
|
||||||
jbv.val.binary.data = &jsonb->root;
|
|
||||||
jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!*cache)
|
|
||||||
*cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
|
|
||||||
|
|
||||||
return populate_record_field(*cache, typid, typmod, NULL, mcxt,
|
|
||||||
PointerGetDatum(NULL), &jsv, isnull);
|
|
||||||
}
|
|
||||||
|
|
||||||
static RecordIOData *
|
static RecordIOData *
|
||||||
allocate_record_info(MemoryContext mcxt, int ncolumns)
|
allocate_record_info(MemoryContext mcxt, int ncolumns)
|
||||||
{
|
{
|
||||||
@ -5566,23 +5521,3 @@ transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype
|
|||||||
else
|
else
|
||||||
appendStringInfoString(_state->strval, token);
|
appendStringInfoString(_state->strval, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonTokenType
|
|
||||||
json_get_first_token(text *json, bool throw_error)
|
|
||||||
{
|
|
||||||
JsonLexContext *lex;
|
|
||||||
JsonParseErrorType result;
|
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, false);
|
|
||||||
|
|
||||||
/* Lex exactly one token from the input and check its type. */
|
|
||||||
result = json_lex(lex);
|
|
||||||
|
|
||||||
if (result == JSON_SUCCESS)
|
|
||||||
return lex->token_type;
|
|
||||||
|
|
||||||
if (throw_error)
|
|
||||||
json_ereport_error(result, lex);
|
|
||||||
|
|
||||||
return JSON_TOKEN_INVALID; /* invalid json */
|
|
||||||
}
|
|
||||||
|
@ -67,9 +67,7 @@
|
|||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/formatting.h"
|
|
||||||
#include "utils/json.h"
|
#include "utils/json.h"
|
||||||
#include "utils/jsonpath.h"
|
#include "utils/jsonpath.h"
|
||||||
|
|
||||||
@ -1079,258 +1077,3 @@ jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SQL/JSON datatype status: */
|
|
||||||
typedef enum JsonPathDatatypeStatus
|
|
||||||
{
|
|
||||||
jpdsNonDateTime, /* null, bool, numeric, string, array, object */
|
|
||||||
jpdsUnknownDateTime, /* unknown datetime type */
|
|
||||||
jpdsDateTimeZoned, /* timetz, timestamptz */
|
|
||||||
jpdsDateTimeNonZoned /* time, timestamp, date */
|
|
||||||
} JsonPathDatatypeStatus;
|
|
||||||
|
|
||||||
/* Context for jspIsMutableWalker() */
|
|
||||||
typedef struct JsonPathMutableContext
|
|
||||||
{
|
|
||||||
List *varnames; /* list of variable names */
|
|
||||||
List *varexprs; /* list of variable expressions */
|
|
||||||
JsonPathDatatypeStatus current; /* status of @ item */
|
|
||||||
bool lax; /* jsonpath is lax or strict */
|
|
||||||
bool mutable; /* resulting mutability status */
|
|
||||||
} JsonPathMutableContext;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Recursive walker for jspIsMutable()
|
|
||||||
*/
|
|
||||||
static JsonPathDatatypeStatus
|
|
||||||
jspIsMutableWalker(JsonPathItem *jpi, JsonPathMutableContext *cxt)
|
|
||||||
{
|
|
||||||
JsonPathItem next;
|
|
||||||
JsonPathDatatypeStatus status = jpdsNonDateTime;
|
|
||||||
|
|
||||||
while (!cxt->mutable)
|
|
||||||
{
|
|
||||||
JsonPathItem arg;
|
|
||||||
JsonPathDatatypeStatus leftStatus;
|
|
||||||
JsonPathDatatypeStatus rightStatus;
|
|
||||||
|
|
||||||
switch (jpi->type)
|
|
||||||
{
|
|
||||||
case jpiRoot:
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiCurrent:
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
status = cxt->current;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiFilter:
|
|
||||||
{
|
|
||||||
JsonPathDatatypeStatus prevStatus = cxt->current;
|
|
||||||
|
|
||||||
cxt->current = status;
|
|
||||||
jspGetArg(jpi, &arg);
|
|
||||||
jspIsMutableWalker(&arg, cxt);
|
|
||||||
|
|
||||||
cxt->current = prevStatus;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case jpiVariable:
|
|
||||||
{
|
|
||||||
int32 len;
|
|
||||||
const char *name = jspGetString(jpi, &len);
|
|
||||||
ListCell *lc1;
|
|
||||||
ListCell *lc2;
|
|
||||||
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
|
|
||||||
forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
|
|
||||||
{
|
|
||||||
String *varname = lfirst_node(String, lc1);
|
|
||||||
Node *varexpr = lfirst(lc2);
|
|
||||||
|
|
||||||
if (strncmp(varname->sval, name, len))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (exprType(varexpr))
|
|
||||||
{
|
|
||||||
case DATEOID:
|
|
||||||
case TIMEOID:
|
|
||||||
case TIMESTAMPOID:
|
|
||||||
status = jpdsDateTimeNonZoned;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TIMETZOID:
|
|
||||||
case TIMESTAMPTZOID:
|
|
||||||
status = jpdsDateTimeZoned;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
status = jpdsNonDateTime;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case jpiEqual:
|
|
||||||
case jpiNotEqual:
|
|
||||||
case jpiLess:
|
|
||||||
case jpiGreater:
|
|
||||||
case jpiLessOrEqual:
|
|
||||||
case jpiGreaterOrEqual:
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
jspGetLeftArg(jpi, &arg);
|
|
||||||
leftStatus = jspIsMutableWalker(&arg, cxt);
|
|
||||||
|
|
||||||
jspGetRightArg(jpi, &arg);
|
|
||||||
rightStatus = jspIsMutableWalker(&arg, cxt);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Comparison of datetime type with different timezone status
|
|
||||||
* is mutable.
|
|
||||||
*/
|
|
||||||
if (leftStatus != jpdsNonDateTime &&
|
|
||||||
rightStatus != jpdsNonDateTime &&
|
|
||||||
(leftStatus == jpdsUnknownDateTime ||
|
|
||||||
rightStatus == jpdsUnknownDateTime ||
|
|
||||||
leftStatus != rightStatus))
|
|
||||||
cxt->mutable = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiNot:
|
|
||||||
case jpiIsUnknown:
|
|
||||||
case jpiExists:
|
|
||||||
case jpiPlus:
|
|
||||||
case jpiMinus:
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
jspGetArg(jpi, &arg);
|
|
||||||
jspIsMutableWalker(&arg, cxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiAnd:
|
|
||||||
case jpiOr:
|
|
||||||
case jpiAdd:
|
|
||||||
case jpiSub:
|
|
||||||
case jpiMul:
|
|
||||||
case jpiDiv:
|
|
||||||
case jpiMod:
|
|
||||||
case jpiStartsWith:
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
jspGetLeftArg(jpi, &arg);
|
|
||||||
jspIsMutableWalker(&arg, cxt);
|
|
||||||
jspGetRightArg(jpi, &arg);
|
|
||||||
jspIsMutableWalker(&arg, cxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiIndexArray:
|
|
||||||
for (int i = 0; i < jpi->content.array.nelems; i++)
|
|
||||||
{
|
|
||||||
JsonPathItem from;
|
|
||||||
JsonPathItem to;
|
|
||||||
|
|
||||||
if (jspGetArraySubscript(jpi, &from, &to, i))
|
|
||||||
jspIsMutableWalker(&to, cxt);
|
|
||||||
|
|
||||||
jspIsMutableWalker(&from, cxt);
|
|
||||||
}
|
|
||||||
/* FALLTHROUGH */
|
|
||||||
|
|
||||||
case jpiAnyArray:
|
|
||||||
if (!cxt->lax)
|
|
||||||
status = jpdsNonDateTime;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiAny:
|
|
||||||
if (jpi->content.anybounds.first > 0)
|
|
||||||
status = jpdsNonDateTime;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiDatetime:
|
|
||||||
if (jpi->content.arg)
|
|
||||||
{
|
|
||||||
char *template;
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
jspGetArg(jpi, &arg);
|
|
||||||
if (arg.type != jpiString)
|
|
||||||
{
|
|
||||||
status = jpdsNonDateTime;
|
|
||||||
break; /* there will be runtime error */
|
|
||||||
}
|
|
||||||
|
|
||||||
template = jspGetString(&arg, NULL);
|
|
||||||
flags = datetime_format_flags(template, NULL);
|
|
||||||
if (flags & DCH_ZONED)
|
|
||||||
status = jpdsDateTimeZoned;
|
|
||||||
else
|
|
||||||
status = jpdsDateTimeNonZoned;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = jpdsUnknownDateTime;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case jpiLikeRegex:
|
|
||||||
Assert(status == jpdsNonDateTime);
|
|
||||||
jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
|
|
||||||
jspIsMutableWalker(&arg, cxt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* literals */
|
|
||||||
case jpiNull:
|
|
||||||
case jpiString:
|
|
||||||
case jpiNumeric:
|
|
||||||
case jpiBool:
|
|
||||||
/* accessors */
|
|
||||||
case jpiKey:
|
|
||||||
case jpiAnyKey:
|
|
||||||
/* special items */
|
|
||||||
case jpiSubscript:
|
|
||||||
case jpiLast:
|
|
||||||
/* item methods */
|
|
||||||
case jpiType:
|
|
||||||
case jpiSize:
|
|
||||||
case jpiAbs:
|
|
||||||
case jpiFloor:
|
|
||||||
case jpiCeiling:
|
|
||||||
case jpiDouble:
|
|
||||||
case jpiKeyValue:
|
|
||||||
status = jpdsNonDateTime;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!jspGetNext(jpi, &next))
|
|
||||||
break;
|
|
||||||
|
|
||||||
jpi = &next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check whether jsonpath expression is immutable or not.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
|
|
||||||
{
|
|
||||||
JsonPathMutableContext cxt;
|
|
||||||
JsonPathItem jpi;
|
|
||||||
|
|
||||||
cxt.varnames = varnames;
|
|
||||||
cxt.varexprs = varexprs;
|
|
||||||
cxt.current = jpdsNonDateTime;
|
|
||||||
cxt.lax = (path->header & JSONPATH_LAX) != 0;
|
|
||||||
cxt.mutable = false;
|
|
||||||
|
|
||||||
jspInit(&jpi, path);
|
|
||||||
jspIsMutableWalker(&jpi, &cxt);
|
|
||||||
|
|
||||||
return cxt.mutable;
|
|
||||||
}
|
|
||||||
|
@ -61,11 +61,9 @@
|
|||||||
|
|
||||||
#include "catalog/pg_collation.h"
|
#include "catalog/pg_collation.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "executor/execExpr.h"
|
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
|
||||||
#include "regex/regex.h"
|
#include "regex/regex.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/date.h"
|
#include "utils/date.h"
|
||||||
@ -76,8 +74,6 @@
|
|||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/json.h"
|
#include "utils/json.h"
|
||||||
#include "utils/jsonpath.h"
|
#include "utils/jsonpath.h"
|
||||||
#include "utils/lsyscache.h"
|
|
||||||
#include "utils/memutils.h"
|
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
#include "utils/varlena.h"
|
#include "utils/varlena.h"
|
||||||
|
|
||||||
@ -90,16 +86,12 @@ typedef struct JsonBaseObjectInfo
|
|||||||
int id;
|
int id;
|
||||||
} JsonBaseObjectInfo;
|
} JsonBaseObjectInfo;
|
||||||
|
|
||||||
typedef int (*JsonPathVarCallback) (void *vars, char *varName, int varNameLen,
|
|
||||||
JsonbValue *val, JsonbValue *baseObject);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Context of jsonpath execution.
|
* Context of jsonpath execution.
|
||||||
*/
|
*/
|
||||||
typedef struct JsonPathExecContext
|
typedef struct JsonPathExecContext
|
||||||
{
|
{
|
||||||
void *vars; /* variables to substitute into jsonpath */
|
Jsonb *vars; /* variables to substitute into jsonpath */
|
||||||
JsonPathVarCallback getVar;
|
|
||||||
JsonbValue *root; /* for $ evaluation */
|
JsonbValue *root; /* for $ evaluation */
|
||||||
JsonbValue *current; /* for @ evaluation */
|
JsonbValue *current; /* for @ evaluation */
|
||||||
JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
|
JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
|
||||||
@ -159,59 +151,6 @@ typedef struct JsonValueListIterator
|
|||||||
ListCell *next;
|
ListCell *next;
|
||||||
} JsonValueListIterator;
|
} JsonValueListIterator;
|
||||||
|
|
||||||
/* Structures for JSON_TABLE execution */
|
|
||||||
typedef struct JsonTableScanState JsonTableScanState;
|
|
||||||
typedef struct JsonTableJoinState JsonTableJoinState;
|
|
||||||
|
|
||||||
struct JsonTableScanState
|
|
||||||
{
|
|
||||||
JsonTableScanState *parent;
|
|
||||||
JsonTableJoinState *nested;
|
|
||||||
MemoryContext mcxt;
|
|
||||||
JsonPath *path;
|
|
||||||
List *args;
|
|
||||||
JsonValueList found;
|
|
||||||
JsonValueListIterator iter;
|
|
||||||
Datum current;
|
|
||||||
int ordinal;
|
|
||||||
bool currentIsNull;
|
|
||||||
bool outerJoin;
|
|
||||||
bool errorOnError;
|
|
||||||
bool advanceNested;
|
|
||||||
bool reset;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct JsonTableJoinState
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
JsonTableJoinState *left;
|
|
||||||
JsonTableJoinState *right;
|
|
||||||
bool cross;
|
|
||||||
bool advanceRight;
|
|
||||||
} join;
|
|
||||||
JsonTableScanState scan;
|
|
||||||
} u;
|
|
||||||
bool is_join;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* random number to identify JsonTableContext */
|
|
||||||
#define JSON_TABLE_CONTEXT_MAGIC 418352867
|
|
||||||
|
|
||||||
typedef struct JsonTableContext
|
|
||||||
{
|
|
||||||
int magic;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
ExprState *expr;
|
|
||||||
JsonTableScanState *scan;
|
|
||||||
} *colexprs;
|
|
||||||
JsonTableScanState root;
|
|
||||||
bool empty;
|
|
||||||
} JsonTableContext;
|
|
||||||
|
|
||||||
/* strict/lax flags is decomposed into four [un]wrap/error flags */
|
/* strict/lax flags is decomposed into four [un]wrap/error flags */
|
||||||
#define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
|
#define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
|
||||||
#define jspAutoUnwrap(cxt) ((cxt)->laxMode)
|
#define jspAutoUnwrap(cxt) ((cxt)->laxMode)
|
||||||
@ -234,8 +173,7 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
|
|||||||
void *param);
|
void *param);
|
||||||
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
|
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
|
||||||
|
|
||||||
static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
|
static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
|
||||||
JsonPathVarCallback getVar,
|
|
||||||
Jsonb *json, bool throwErrors,
|
Jsonb *json, bool throwErrors,
|
||||||
JsonValueList *result, bool useTz);
|
JsonValueList *result, bool useTz);
|
||||||
static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
|
static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
|
||||||
@ -287,10 +225,7 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
|
|||||||
static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
|
static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
|
||||||
JsonbValue *value);
|
JsonbValue *value);
|
||||||
static void getJsonPathVariable(JsonPathExecContext *cxt,
|
static void getJsonPathVariable(JsonPathExecContext *cxt,
|
||||||
JsonPathItem *variable, JsonbValue *value);
|
JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
|
||||||
static int getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
|
|
||||||
int varNameLen, JsonbValue *val,
|
|
||||||
JsonbValue *baseObject);
|
|
||||||
static int JsonbArraySize(JsonbValue *jb);
|
static int JsonbArraySize(JsonbValue *jb);
|
||||||
static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
|
static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
|
||||||
JsonbValue *rv, void *p);
|
JsonbValue *rv, void *p);
|
||||||
@ -302,7 +237,6 @@ static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
|
|||||||
JsonPathItem *jsp, JsonbValue *jb, int32 *index);
|
JsonPathItem *jsp, JsonbValue *jb, int32 *index);
|
||||||
static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
|
static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
|
||||||
JsonbValue *jbv, int32 id);
|
JsonbValue *jbv, int32 id);
|
||||||
static void JsonValueListClear(JsonValueList *jvl);
|
|
||||||
static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
|
static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
|
||||||
static int JsonValueListLength(const JsonValueList *jvl);
|
static int JsonValueListLength(const JsonValueList *jvl);
|
||||||
static bool JsonValueListIsEmpty(JsonValueList *jvl);
|
static bool JsonValueListIsEmpty(JsonValueList *jvl);
|
||||||
@ -320,12 +254,6 @@ static JsonbValue *wrapItemsInArray(const JsonValueList *items);
|
|||||||
static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
|
static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
|
||||||
bool useTz, bool *have_error);
|
bool useTz, bool *have_error);
|
||||||
|
|
||||||
|
|
||||||
static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt,
|
|
||||||
Node *plan, JsonTableScanState *parent);
|
|
||||||
static bool JsonTableNextRow(JsonTableScanState *scan);
|
|
||||||
|
|
||||||
|
|
||||||
/****************** User interface to JsonPath executor ********************/
|
/****************** User interface to JsonPath executor ********************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -355,8 +283,7 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
|
|||||||
silent = PG_GETARG_BOOL(3);
|
silent = PG_GETARG_BOOL(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
|
res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
|
||||||
jb, !silent, NULL, tz);
|
|
||||||
|
|
||||||
PG_FREE_IF_COPY(jb, 0);
|
PG_FREE_IF_COPY(jb, 0);
|
||||||
PG_FREE_IF_COPY(jp, 1);
|
PG_FREE_IF_COPY(jp, 1);
|
||||||
@ -411,8 +338,7 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
|
|||||||
silent = PG_GETARG_BOOL(3);
|
silent = PG_GETARG_BOOL(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
|
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
|
||||||
jb, !silent, &found, tz);
|
|
||||||
|
|
||||||
PG_FREE_IF_COPY(jb, 0);
|
PG_FREE_IF_COPY(jb, 0);
|
||||||
PG_FREE_IF_COPY(jp, 1);
|
PG_FREE_IF_COPY(jp, 1);
|
||||||
@ -490,8 +416,7 @@ jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
|
|||||||
vars = PG_GETARG_JSONB_P_COPY(2);
|
vars = PG_GETARG_JSONB_P_COPY(2);
|
||||||
silent = PG_GETARG_BOOL(3);
|
silent = PG_GETARG_BOOL(3);
|
||||||
|
|
||||||
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
|
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
|
||||||
jb, !silent, &found, tz);
|
|
||||||
|
|
||||||
funcctx->user_fctx = JsonValueListGetList(&found);
|
funcctx->user_fctx = JsonValueListGetList(&found);
|
||||||
|
|
||||||
@ -538,8 +463,7 @@ jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
|
|||||||
Jsonb *vars = PG_GETARG_JSONB_P(2);
|
Jsonb *vars = PG_GETARG_JSONB_P(2);
|
||||||
bool silent = PG_GETARG_BOOL(3);
|
bool silent = PG_GETARG_BOOL(3);
|
||||||
|
|
||||||
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
|
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
|
||||||
jb, !silent, &found, tz);
|
|
||||||
|
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
|
||||||
}
|
}
|
||||||
@ -570,8 +494,7 @@ jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
|
|||||||
Jsonb *vars = PG_GETARG_JSONB_P(2);
|
Jsonb *vars = PG_GETARG_JSONB_P(2);
|
||||||
bool silent = PG_GETARG_BOOL(3);
|
bool silent = PG_GETARG_BOOL(3);
|
||||||
|
|
||||||
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
|
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
|
||||||
jb, !silent, &found, tz);
|
|
||||||
|
|
||||||
if (JsonValueListLength(&found) >= 1)
|
if (JsonValueListLength(&found) >= 1)
|
||||||
PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
|
PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
|
||||||
@ -613,9 +536,8 @@ jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
|
|||||||
* In other case it tries to find all the satisfied result items.
|
* In other case it tries to find all the satisfied result items.
|
||||||
*/
|
*/
|
||||||
static JsonPathExecResult
|
static JsonPathExecResult
|
||||||
executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
|
executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
|
||||||
Jsonb *json, bool throwErrors, JsonValueList *result,
|
JsonValueList *result, bool useTz)
|
||||||
bool useTz)
|
|
||||||
{
|
{
|
||||||
JsonPathExecContext cxt;
|
JsonPathExecContext cxt;
|
||||||
JsonPathExecResult res;
|
JsonPathExecResult res;
|
||||||
@ -627,16 +549,22 @@ executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
|
|||||||
if (!JsonbExtractScalar(&json->root, &jbv))
|
if (!JsonbExtractScalar(&json->root, &jbv))
|
||||||
JsonbInitBinary(&jbv, json);
|
JsonbInitBinary(&jbv, json);
|
||||||
|
|
||||||
|
if (vars && !JsonContainerIsObject(&vars->root))
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("\"vars\" argument is not an object"),
|
||||||
|
errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
|
||||||
|
}
|
||||||
|
|
||||||
cxt.vars = vars;
|
cxt.vars = vars;
|
||||||
cxt.getVar = getVar;
|
|
||||||
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
|
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
|
||||||
cxt.ignoreStructuralErrors = cxt.laxMode;
|
cxt.ignoreStructuralErrors = cxt.laxMode;
|
||||||
cxt.root = &jbv;
|
cxt.root = &jbv;
|
||||||
cxt.current = &jbv;
|
cxt.current = &jbv;
|
||||||
cxt.baseObject.jbc = NULL;
|
cxt.baseObject.jbc = NULL;
|
||||||
cxt.baseObject.id = 0;
|
cxt.baseObject.id = 0;
|
||||||
/* 1 + number of base objects in vars */
|
cxt.lastGeneratedObjectId = vars ? 2 : 1;
|
||||||
cxt.lastGeneratedObjectId = 1 + getVar(vars, NULL, 0, NULL, NULL);
|
|
||||||
cxt.innermostArraySize = -1;
|
cxt.innermostArraySize = -1;
|
||||||
cxt.throwErrors = throwErrors;
|
cxt.throwErrors = throwErrors;
|
||||||
cxt.useTz = useTz;
|
cxt.useTz = useTz;
|
||||||
@ -2165,7 +2093,7 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
|
|||||||
&value->val.string.len);
|
&value->val.string.len);
|
||||||
break;
|
break;
|
||||||
case jpiVariable:
|
case jpiVariable:
|
||||||
getJsonPathVariable(cxt, item, value);
|
getJsonPathVariable(cxt, item, cxt->vars, value);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unexpected jsonpath item type");
|
elog(ERROR, "unexpected jsonpath item type");
|
||||||
@ -2177,63 +2105,42 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
|
getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
|
||||||
JsonbValue *value)
|
Jsonb *vars, JsonbValue *value)
|
||||||
{
|
{
|
||||||
char *varName;
|
char *varName;
|
||||||
int varNameLength;
|
int varNameLength;
|
||||||
JsonbValue baseObject;
|
|
||||||
int baseObjectId;
|
|
||||||
|
|
||||||
Assert(variable->type == jpiVariable);
|
|
||||||
varName = jspGetString(variable, &varNameLength);
|
|
||||||
|
|
||||||
if (!cxt->vars ||
|
|
||||||
(baseObjectId = cxt->getVar(cxt->vars, varName, varNameLength, value,
|
|
||||||
&baseObject)) < 0)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
||||||
errmsg("could not find jsonpath variable \"%s\"",
|
|
||||||
pnstrdup(varName, varNameLength))));
|
|
||||||
|
|
||||||
if (baseObjectId > 0)
|
|
||||||
setBaseObject(cxt, &baseObject, baseObjectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
|
|
||||||
JsonbValue *value, JsonbValue *baseObject)
|
|
||||||
{
|
|
||||||
Jsonb *vars = varsJsonb;
|
|
||||||
JsonbValue tmp;
|
JsonbValue tmp;
|
||||||
JsonbValue *v;
|
JsonbValue *v;
|
||||||
|
|
||||||
if (!varName)
|
if (!vars)
|
||||||
{
|
{
|
||||||
if (vars && !JsonContainerIsObject(&vars->root))
|
value->type = jbvNull;
|
||||||
{
|
return;
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("\"vars\" argument is not an object"),
|
|
||||||
errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
return vars ? 1 : 0; /* count of base objects */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Assert(variable->type == jpiVariable);
|
||||||
|
varName = jspGetString(variable, &varNameLength);
|
||||||
tmp.type = jbvString;
|
tmp.type = jbvString;
|
||||||
tmp.val.string.val = varName;
|
tmp.val.string.val = varName;
|
||||||
tmp.val.string.len = varNameLength;
|
tmp.val.string.len = varNameLength;
|
||||||
|
|
||||||
v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
|
v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
|
||||||
|
|
||||||
if (!v)
|
if (v)
|
||||||
return -1;
|
{
|
||||||
|
|
||||||
*value = *v;
|
*value = *v;
|
||||||
pfree(v);
|
pfree(v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("could not find jsonpath variable \"%s\"",
|
||||||
|
pnstrdup(varName, varNameLength))));
|
||||||
|
}
|
||||||
|
|
||||||
JsonbInitBinary(baseObject, vars);
|
JsonbInitBinary(&tmp, vars);
|
||||||
return 1;
|
setBaseObject(cxt, &tmp, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************** Support functions for JsonPath execution *****************/
|
/**************** Support functions for JsonPath execution *****************/
|
||||||
@ -2522,13 +2429,6 @@ setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
|
|||||||
return baseObject;
|
return baseObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
JsonValueListClear(JsonValueList *jvl)
|
|
||||||
{
|
|
||||||
jvl->singleton = NULL;
|
|
||||||
jvl->list = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
|
JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
|
||||||
{
|
{
|
||||||
@ -2897,667 +2797,3 @@ compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
|
|||||||
|
|
||||||
return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
|
return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************Interface to pgsql's executor***************************/
|
|
||||||
|
|
||||||
bool
|
|
||||||
JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool *error)
|
|
||||||
{
|
|
||||||
JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar,
|
|
||||||
DatumGetJsonbP(jb), !error, NULL,
|
|
||||||
true);
|
|
||||||
|
|
||||||
Assert(error || !jperIsError(res));
|
|
||||||
|
|
||||||
if (error && jperIsError(res))
|
|
||||||
*error = true;
|
|
||||||
|
|
||||||
return res == jperOk;
|
|
||||||
}
|
|
||||||
|
|
||||||
Datum
|
|
||||||
JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
|
|
||||||
bool *error, List *vars)
|
|
||||||
{
|
|
||||||
JsonbValue *first;
|
|
||||||
bool wrap;
|
|
||||||
JsonValueList found = {0};
|
|
||||||
JsonPathExecResult res PG_USED_FOR_ASSERTS_ONLY;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
res = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
|
|
||||||
&found, true);
|
|
||||||
|
|
||||||
Assert(error || !jperIsError(res));
|
|
||||||
|
|
||||||
if (error && jperIsError(res))
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
*empty = false;
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = JsonValueListLength(&found);
|
|
||||||
|
|
||||||
first = count ? JsonValueListHead(&found) : NULL;
|
|
||||||
|
|
||||||
if (!first)
|
|
||||||
wrap = false;
|
|
||||||
else if (wrapper == JSW_NONE)
|
|
||||||
wrap = false;
|
|
||||||
else if (wrapper == JSW_UNCONDITIONAL)
|
|
||||||
wrap = true;
|
|
||||||
else if (wrapper == JSW_CONDITIONAL)
|
|
||||||
wrap = count > 1 ||
|
|
||||||
IsAJsonbScalar(first) ||
|
|
||||||
(first->type == jbvBinary &&
|
|
||||||
JsonContainerIsScalar(first->val.binary.data));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
elog(ERROR, "unrecognized json wrapper %d", wrapper);
|
|
||||||
wrap = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wrap)
|
|
||||||
return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
|
|
||||||
|
|
||||||
if (count > 1)
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
return (Datum) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
|
|
||||||
errmsg("JSON path expression in JSON_QUERY should return "
|
|
||||||
"singleton item without wrapper"),
|
|
||||||
errhint("Use WITH WRAPPER clause to wrap SQL/JSON item "
|
|
||||||
"sequence into array.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
return JsonbPGetDatum(JsonbValueToJsonb(first));
|
|
||||||
|
|
||||||
*empty = true;
|
|
||||||
return PointerGetDatum(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonbValue *
|
|
||||||
JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
|
|
||||||
{
|
|
||||||
JsonbValue *res;
|
|
||||||
JsonValueList found = {0};
|
|
||||||
JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
jper = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
|
|
||||||
&found, true);
|
|
||||||
|
|
||||||
Assert(error || !jperIsError(jper));
|
|
||||||
|
|
||||||
if (error && jperIsError(jper))
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
*empty = false;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
count = JsonValueListLength(&found);
|
|
||||||
|
|
||||||
*empty = !count;
|
|
||||||
|
|
||||||
if (*empty)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (count > 1)
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
|
|
||||||
errmsg("JSON path expression in JSON_VALUE should return "
|
|
||||||
"singleton scalar item")));
|
|
||||||
}
|
|
||||||
|
|
||||||
res = JsonValueListHead(&found);
|
|
||||||
|
|
||||||
if (res->type == jbvBinary &&
|
|
||||||
JsonContainerIsScalar(res->val.binary.data))
|
|
||||||
JsonbExtractScalar(res->val.binary.data, res);
|
|
||||||
|
|
||||||
if (!IsAJsonbScalar(res))
|
|
||||||
{
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
*error = true;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
|
|
||||||
errmsg("JSON path expression in JSON_VALUE should return "
|
|
||||||
"singleton scalar item")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res->type == jbvNull)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
|
|
||||||
{
|
|
||||||
jbv->type = jbvNumeric;
|
|
||||||
jbv->val.numeric = DatumGetNumeric(num);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
|
|
||||||
{
|
|
||||||
switch (typid)
|
|
||||||
{
|
|
||||||
case BOOLOID:
|
|
||||||
res->type = jbvBool;
|
|
||||||
res->val.boolean = DatumGetBool(val);
|
|
||||||
break;
|
|
||||||
case NUMERICOID:
|
|
||||||
JsonbValueInitNumericDatum(res, val);
|
|
||||||
break;
|
|
||||||
case INT2OID:
|
|
||||||
JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
|
|
||||||
break;
|
|
||||||
case INT4OID:
|
|
||||||
JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
|
|
||||||
break;
|
|
||||||
case INT8OID:
|
|
||||||
JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
|
|
||||||
break;
|
|
||||||
case FLOAT4OID:
|
|
||||||
JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
|
|
||||||
break;
|
|
||||||
case FLOAT8OID:
|
|
||||||
JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
|
|
||||||
break;
|
|
||||||
case TEXTOID:
|
|
||||||
case VARCHAROID:
|
|
||||||
res->type = jbvString;
|
|
||||||
res->val.string.val = VARDATA_ANY(val);
|
|
||||||
res->val.string.len = VARSIZE_ANY_EXHDR(val);
|
|
||||||
break;
|
|
||||||
case DATEOID:
|
|
||||||
case TIMEOID:
|
|
||||||
case TIMETZOID:
|
|
||||||
case TIMESTAMPOID:
|
|
||||||
case TIMESTAMPTZOID:
|
|
||||||
res->type = jbvDatetime;
|
|
||||||
res->val.datetime.value = val;
|
|
||||||
res->val.datetime.typid = typid;
|
|
||||||
res->val.datetime.typmod = typmod;
|
|
||||||
res->val.datetime.tz = 0;
|
|
||||||
break;
|
|
||||||
case JSONBOID:
|
|
||||||
{
|
|
||||||
JsonbValue *jbv = res;
|
|
||||||
Jsonb *jb = DatumGetJsonbP(val);
|
|
||||||
|
|
||||||
if (JsonContainerIsScalar(&jb->root))
|
|
||||||
{
|
|
||||||
bool result PG_USED_FOR_ASSERTS_ONLY;
|
|
||||||
|
|
||||||
result = JsonbExtractScalar(&jb->root, jbv);
|
|
||||||
Assert(result);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
JsonbInitBinary(jbv, jb);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case JSONOID:
|
|
||||||
{
|
|
||||||
text *txt = DatumGetTextP(val);
|
|
||||||
char *str = text_to_cstring(txt);
|
|
||||||
Jsonb *jb =
|
|
||||||
DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
|
|
||||||
CStringGetDatum(str)));
|
|
||||||
|
|
||||||
pfree(str);
|
|
||||||
|
|
||||||
JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
||||||
errmsg("only bool, numeric, and text types could be "
|
|
||||||
"casted to supported jsonpath types.")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************ JSON_TABLE functions ***************************/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns private data from executor state. Ensure validity by check with
|
|
||||||
* MAGIC number.
|
|
||||||
*/
|
|
||||||
static inline JsonTableContext *
|
|
||||||
GetJsonTableContext(TableFuncScanState *state, const char *fname)
|
|
||||||
{
|
|
||||||
JsonTableContext *result;
|
|
||||||
|
|
||||||
if (!IsA(state, TableFuncScanState))
|
|
||||||
elog(ERROR, "%s called with invalid TableFuncScanState", fname);
|
|
||||||
result = (JsonTableContext *) state->opaque;
|
|
||||||
if (result->magic != JSON_TABLE_CONTEXT_MAGIC)
|
|
||||||
elog(ERROR, "%s called with invalid TableFuncScanState", fname);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively initialize JSON_TABLE scan state */
|
|
||||||
static void
|
|
||||||
JsonTableInitScanState(JsonTableContext *cxt, JsonTableScanState *scan,
|
|
||||||
JsonTableParent *node, JsonTableScanState *parent,
|
|
||||||
List *args, MemoryContext mcxt)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
scan->parent = parent;
|
|
||||||
scan->outerJoin = node->outerJoin;
|
|
||||||
scan->errorOnError = node->errorOnError;
|
|
||||||
scan->path = DatumGetJsonPathP(node->path->constvalue);
|
|
||||||
scan->args = args;
|
|
||||||
scan->mcxt = AllocSetContextCreate(mcxt, "JsonTableContext",
|
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
|
||||||
scan->nested = node->child ?
|
|
||||||
JsonTableInitPlanState(cxt, node->child, scan) : NULL;
|
|
||||||
scan->current = PointerGetDatum(NULL);
|
|
||||||
scan->currentIsNull = true;
|
|
||||||
|
|
||||||
for (i = node->colMin; i <= node->colMax; i++)
|
|
||||||
cxt->colexprs[i].scan = scan;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively initialize JSON_TABLE scan state */
|
|
||||||
static JsonTableJoinState *
|
|
||||||
JsonTableInitPlanState(JsonTableContext *cxt, Node *plan,
|
|
||||||
JsonTableScanState *parent)
|
|
||||||
{
|
|
||||||
JsonTableJoinState *state = palloc0(sizeof(*state));
|
|
||||||
|
|
||||||
if (IsA(plan, JsonTableSibling))
|
|
||||||
{
|
|
||||||
JsonTableSibling *join = castNode(JsonTableSibling, plan);
|
|
||||||
|
|
||||||
state->is_join = true;
|
|
||||||
state->u.join.cross = join->cross;
|
|
||||||
state->u.join.left = JsonTableInitPlanState(cxt, join->larg, parent);
|
|
||||||
state->u.join.right = JsonTableInitPlanState(cxt, join->rarg, parent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonTableParent *node = castNode(JsonTableParent, plan);
|
|
||||||
|
|
||||||
state->is_join = false;
|
|
||||||
|
|
||||||
JsonTableInitScanState(cxt, &state->u.scan, node, parent,
|
|
||||||
parent->args, parent->mcxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableInitOpaque
|
|
||||||
* Fill in TableFuncScanState->opaque for JsonTable processor
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
JsonTableInitOpaque(TableFuncScanState *state, int natts)
|
|
||||||
{
|
|
||||||
JsonTableContext *cxt;
|
|
||||||
PlanState *ps = &state->ss.ps;
|
|
||||||
TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
|
|
||||||
TableFunc *tf = tfs->tablefunc;
|
|
||||||
JsonExpr *ci = castNode(JsonExpr, tf->docexpr);
|
|
||||||
JsonTableParent *root = castNode(JsonTableParent, tf->plan);
|
|
||||||
List *args = NIL;
|
|
||||||
ListCell *lc;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
cxt = palloc0(sizeof(JsonTableContext));
|
|
||||||
cxt->magic = JSON_TABLE_CONTEXT_MAGIC;
|
|
||||||
|
|
||||||
if (ci->passing_values)
|
|
||||||
{
|
|
||||||
ListCell *exprlc;
|
|
||||||
ListCell *namelc;
|
|
||||||
|
|
||||||
forboth(exprlc, ci->passing_values,
|
|
||||||
namelc, ci->passing_names)
|
|
||||||
{
|
|
||||||
Expr *expr = (Expr *) lfirst(exprlc);
|
|
||||||
String *name = lfirst_node(String, namelc);
|
|
||||||
JsonPathVariableEvalContext *var = palloc(sizeof(*var));
|
|
||||||
|
|
||||||
var->name = pstrdup(name->sval);
|
|
||||||
var->typid = exprType((Node *) expr);
|
|
||||||
var->typmod = exprTypmod((Node *) expr);
|
|
||||||
var->estate = ExecInitExpr(expr, ps);
|
|
||||||
var->econtext = ps->ps_ExprContext;
|
|
||||||
var->mcxt = CurrentMemoryContext;
|
|
||||||
var->evaluated = false;
|
|
||||||
var->value = (Datum) 0;
|
|
||||||
var->isnull = true;
|
|
||||||
|
|
||||||
args = lappend(args, var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cxt->colexprs = palloc(sizeof(*cxt->colexprs) *
|
|
||||||
list_length(tf->colvalexprs));
|
|
||||||
|
|
||||||
JsonTableInitScanState(cxt, &cxt->root, root, NULL, args,
|
|
||||||
CurrentMemoryContext);
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
foreach(lc, tf->colvalexprs)
|
|
||||||
{
|
|
||||||
Expr *expr = lfirst(lc);
|
|
||||||
|
|
||||||
cxt->colexprs[i].expr =
|
|
||||||
ExecInitExprWithCaseValue(expr, ps,
|
|
||||||
&cxt->colexprs[i].scan->current,
|
|
||||||
&cxt->colexprs[i].scan->currentIsNull);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->opaque = cxt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset scan iterator to the beginning of the item list */
|
|
||||||
static void
|
|
||||||
JsonTableRescan(JsonTableScanState *scan)
|
|
||||||
{
|
|
||||||
JsonValueListInitIterator(&scan->found, &scan->iter);
|
|
||||||
scan->current = PointerGetDatum(NULL);
|
|
||||||
scan->currentIsNull = true;
|
|
||||||
scan->advanceNested = false;
|
|
||||||
scan->ordinal = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset context item of a scan, execute JSON path and reset a scan */
|
|
||||||
static void
|
|
||||||
JsonTableResetContextItem(JsonTableScanState *scan, Datum item)
|
|
||||||
{
|
|
||||||
MemoryContext oldcxt;
|
|
||||||
JsonPathExecResult res;
|
|
||||||
Jsonb *js = (Jsonb *) DatumGetJsonbP(item);
|
|
||||||
|
|
||||||
JsonValueListClear(&scan->found);
|
|
||||||
|
|
||||||
MemoryContextResetOnly(scan->mcxt);
|
|
||||||
|
|
||||||
oldcxt = MemoryContextSwitchTo(scan->mcxt);
|
|
||||||
|
|
||||||
res = executeJsonPath(scan->path, scan->args, EvalJsonPathVar, js,
|
|
||||||
scan->errorOnError, &scan->found, false /* FIXME */ );
|
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
if (jperIsError(res))
|
|
||||||
{
|
|
||||||
Assert(!scan->errorOnError);
|
|
||||||
JsonValueListClear(&scan->found); /* EMPTY ON ERROR case */
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonTableRescan(scan);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableSetDocument
|
|
||||||
* Install the input document
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
JsonTableSetDocument(TableFuncScanState *state, Datum value)
|
|
||||||
{
|
|
||||||
JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableSetDocument");
|
|
||||||
|
|
||||||
JsonTableResetContextItem(&cxt->root, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively reset scan and its child nodes */
|
|
||||||
static void
|
|
||||||
JsonTableRescanRecursive(JsonTableJoinState *state)
|
|
||||||
{
|
|
||||||
if (state->is_join)
|
|
||||||
{
|
|
||||||
JsonTableRescanRecursive(state->u.join.left);
|
|
||||||
JsonTableRescanRecursive(state->u.join.right);
|
|
||||||
state->u.join.advanceRight = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonTableRescan(&state->u.scan);
|
|
||||||
if (state->u.scan.nested)
|
|
||||||
JsonTableRescanRecursive(state->u.scan.nested);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch next row from a cross/union joined scan.
|
|
||||||
*
|
|
||||||
* Returns false at the end of a scan, true otherwise.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
JsonTableNextJoinRow(JsonTableJoinState *state)
|
|
||||||
{
|
|
||||||
if (!state->is_join)
|
|
||||||
return JsonTableNextRow(&state->u.scan);
|
|
||||||
|
|
||||||
if (state->u.join.advanceRight)
|
|
||||||
{
|
|
||||||
/* fetch next inner row */
|
|
||||||
if (JsonTableNextJoinRow(state->u.join.right))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* inner rows are exhausted */
|
|
||||||
if (state->u.join.cross)
|
|
||||||
state->u.join.advanceRight = false; /* next outer row */
|
|
||||||
else
|
|
||||||
return false; /* end of scan */
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!state->u.join.advanceRight)
|
|
||||||
{
|
|
||||||
/* fetch next outer row */
|
|
||||||
bool left = JsonTableNextJoinRow(state->u.join.left);
|
|
||||||
|
|
||||||
if (state->u.join.cross)
|
|
||||||
{
|
|
||||||
if (!left)
|
|
||||||
return false; /* end of scan */
|
|
||||||
|
|
||||||
JsonTableRescanRecursive(state->u.join.right);
|
|
||||||
|
|
||||||
if (!JsonTableNextJoinRow(state->u.join.right))
|
|
||||||
continue; /* next outer row */
|
|
||||||
|
|
||||||
state->u.join.advanceRight = true; /* next inner row */
|
|
||||||
}
|
|
||||||
else if (!left)
|
|
||||||
{
|
|
||||||
if (!JsonTableNextJoinRow(state->u.join.right))
|
|
||||||
return false; /* end of scan */
|
|
||||||
|
|
||||||
state->u.join.advanceRight = true; /* next inner row */
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Recursively set 'reset' flag of scan and its child nodes */
|
|
||||||
static void
|
|
||||||
JsonTableJoinReset(JsonTableJoinState *state)
|
|
||||||
{
|
|
||||||
if (state->is_join)
|
|
||||||
{
|
|
||||||
JsonTableJoinReset(state->u.join.left);
|
|
||||||
JsonTableJoinReset(state->u.join.right);
|
|
||||||
state->u.join.advanceRight = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
state->u.scan.reset = true;
|
|
||||||
state->u.scan.advanceNested = false;
|
|
||||||
|
|
||||||
if (state->u.scan.nested)
|
|
||||||
JsonTableJoinReset(state->u.scan.nested);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fetch next row from a simple scan with outer/inner joined nested subscans.
|
|
||||||
*
|
|
||||||
* Returns false at the end of a scan, true otherwise.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
JsonTableNextRow(JsonTableScanState *scan)
|
|
||||||
{
|
|
||||||
/* reset context item if requested */
|
|
||||||
if (scan->reset)
|
|
||||||
{
|
|
||||||
Assert(!scan->parent->currentIsNull);
|
|
||||||
JsonTableResetContextItem(scan, scan->parent->current);
|
|
||||||
scan->reset = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scan->advanceNested)
|
|
||||||
{
|
|
||||||
/* fetch next nested row */
|
|
||||||
scan->advanceNested = JsonTableNextJoinRow(scan->nested);
|
|
||||||
|
|
||||||
if (scan->advanceNested)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
/* fetch next row */
|
|
||||||
JsonbValue *jbv = JsonValueListNext(&scan->found, &scan->iter);
|
|
||||||
MemoryContext oldcxt;
|
|
||||||
|
|
||||||
if (!jbv)
|
|
||||||
{
|
|
||||||
scan->current = PointerGetDatum(NULL);
|
|
||||||
scan->currentIsNull = true;
|
|
||||||
return false; /* end of scan */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set current row item */
|
|
||||||
oldcxt = MemoryContextSwitchTo(scan->mcxt);
|
|
||||||
scan->current = JsonbPGetDatum(JsonbValueToJsonb(jbv));
|
|
||||||
scan->currentIsNull = false;
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
|
||||||
|
|
||||||
scan->ordinal++;
|
|
||||||
|
|
||||||
if (!scan->nested)
|
|
||||||
break;
|
|
||||||
|
|
||||||
JsonTableJoinReset(scan->nested);
|
|
||||||
|
|
||||||
scan->advanceNested = JsonTableNextJoinRow(scan->nested);
|
|
||||||
|
|
||||||
if (scan->advanceNested || scan->outerJoin)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableFetchRow
|
|
||||||
* Prepare the next "current" tuple for upcoming GetValue calls.
|
|
||||||
* Returns FALSE if the row-filter expression returned no more rows.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
JsonTableFetchRow(TableFuncScanState *state)
|
|
||||||
{
|
|
||||||
JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableFetchRow");
|
|
||||||
|
|
||||||
if (cxt->empty)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return JsonTableNextRow(&cxt->root);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableGetValue
|
|
||||||
* Return the value for column number 'colnum' for the current row.
|
|
||||||
*
|
|
||||||
* This leaks memory, so be sure to reset often the context in which it's
|
|
||||||
* called.
|
|
||||||
*/
|
|
||||||
static Datum
|
|
||||||
JsonTableGetValue(TableFuncScanState *state, int colnum,
|
|
||||||
Oid typid, int32 typmod, bool *isnull)
|
|
||||||
{
|
|
||||||
JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableGetValue");
|
|
||||||
ExprContext *econtext = state->ss.ps.ps_ExprContext;
|
|
||||||
ExprState *estate = cxt->colexprs[colnum].expr;
|
|
||||||
JsonTableScanState *scan = cxt->colexprs[colnum].scan;
|
|
||||||
Datum result;
|
|
||||||
|
|
||||||
if (scan->currentIsNull) /* NULL from outer/union join */
|
|
||||||
{
|
|
||||||
result = (Datum) 0;
|
|
||||||
*isnull = true;
|
|
||||||
}
|
|
||||||
else if (estate) /* regular column */
|
|
||||||
{
|
|
||||||
result = ExecEvalExpr(estate, econtext, isnull);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = Int32GetDatum(scan->ordinal); /* ordinality column */
|
|
||||||
*isnull = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableDestroyOpaque
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
JsonTableDestroyOpaque(TableFuncScanState *state)
|
|
||||||
{
|
|
||||||
JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableDestroyOpaque");
|
|
||||||
|
|
||||||
/* not valid anymore */
|
|
||||||
cxt->magic = 0;
|
|
||||||
|
|
||||||
state->opaque = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TableFuncRoutine JsonbTableRoutine =
|
|
||||||
{
|
|
||||||
JsonTableInitOpaque,
|
|
||||||
JsonTableSetDocument,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
JsonTableFetchRow,
|
|
||||||
JsonTableGetValue,
|
|
||||||
JsonTableDestroyOpaque
|
|
||||||
};
|
|
||||||
|
@ -466,12 +466,6 @@ static void get_coercion_expr(Node *arg, deparse_context *context,
|
|||||||
Node *parentNode);
|
Node *parentNode);
|
||||||
static void get_const_expr(Const *constval, deparse_context *context,
|
static void get_const_expr(Const *constval, deparse_context *context,
|
||||||
int showtype);
|
int showtype);
|
||||||
static void get_json_constructor(JsonConstructorExpr *ctor,
|
|
||||||
deparse_context *context, bool showimplicit);
|
|
||||||
static void get_json_agg_constructor(JsonConstructorExpr *ctor,
|
|
||||||
deparse_context *context,
|
|
||||||
const char *funcname,
|
|
||||||
bool is_json_objectagg);
|
|
||||||
static void get_const_collation(Const *constval, deparse_context *context);
|
static void get_const_collation(Const *constval, deparse_context *context);
|
||||||
static void simple_quote_literal(StringInfo buf, const char *val);
|
static void simple_quote_literal(StringInfo buf, const char *val);
|
||||||
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
|
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
|
||||||
@ -505,10 +499,6 @@ static char *generate_qualified_type_name(Oid typid);
|
|||||||
static text *string_to_text(char *str);
|
static text *string_to_text(char *str);
|
||||||
static char *flatten_reloptions(Oid relid);
|
static char *flatten_reloptions(Oid relid);
|
||||||
static void get_reloptions(StringInfo buf, Datum reloptions);
|
static void get_reloptions(StringInfo buf, Datum reloptions);
|
||||||
static void get_json_path_spec(Node *path_spec, deparse_context *context,
|
|
||||||
bool showimplicit);
|
|
||||||
static void get_json_table_columns(TableFunc *tf, JsonTableParent *node,
|
|
||||||
deparse_context *context, bool showimplicit);
|
|
||||||
|
|
||||||
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
|
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
|
||||||
|
|
||||||
@ -6338,8 +6328,7 @@ get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
|
|||||||
bool need_paren = (PRETTY_PAREN(context)
|
bool need_paren = (PRETTY_PAREN(context)
|
||||||
|| IsA(expr, FuncExpr)
|
|| IsA(expr, FuncExpr)
|
||||||
|| IsA(expr, Aggref)
|
|| IsA(expr, Aggref)
|
||||||
|| IsA(expr, WindowFunc)
|
|| IsA(expr, WindowFunc));
|
||||||
|| IsA(expr, JsonConstructorExpr));
|
|
||||||
|
|
||||||
if (need_paren)
|
if (need_paren)
|
||||||
appendStringInfoChar(context->buf, '(');
|
appendStringInfoChar(context->buf, '(');
|
||||||
@ -8198,8 +8187,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
|||||||
case T_GroupingFunc:
|
case T_GroupingFunc:
|
||||||
case T_WindowFunc:
|
case T_WindowFunc:
|
||||||
case T_FuncExpr:
|
case T_FuncExpr:
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
case T_JsonExpr:
|
|
||||||
/* function-like: name(..) or name[..] */
|
/* function-like: name(..) or name[..] */
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -8293,7 +8280,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
|||||||
case T_NullTest:
|
case T_NullTest:
|
||||||
case T_BooleanTest:
|
case T_BooleanTest:
|
||||||
case T_DistinctExpr:
|
case T_DistinctExpr:
|
||||||
case T_JsonIsPredicate:
|
|
||||||
switch (nodeTag(parentNode))
|
switch (nodeTag(parentNode))
|
||||||
{
|
{
|
||||||
case T_FuncExpr:
|
case T_FuncExpr:
|
||||||
@ -8318,7 +8304,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
|||||||
case T_GroupingFunc: /* own parentheses */
|
case T_GroupingFunc: /* own parentheses */
|
||||||
case T_WindowFunc: /* own parentheses */
|
case T_WindowFunc: /* own parentheses */
|
||||||
case T_CaseExpr: /* other separators */
|
case T_CaseExpr: /* other separators */
|
||||||
case T_JsonExpr: /* own parentheses */
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@ -8375,11 +8360,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
/* maybe simple, check args */
|
|
||||||
return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
|
|
||||||
node, prettyFlags);
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -8486,122 +8466,6 @@ get_rule_expr_paren(Node *node, deparse_context *context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_path_spec - Parse back a JSON path specification
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
|
|
||||||
{
|
|
||||||
if (IsA(path_spec, Const))
|
|
||||||
get_const_expr((Const *) path_spec, context, -1);
|
|
||||||
else
|
|
||||||
get_rule_expr(path_spec, context, showimplicit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_format - Parse back a JsonFormat node
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_format(JsonFormat *format, StringInfo buf)
|
|
||||||
{
|
|
||||||
if (format->format_type == JS_FORMAT_DEFAULT)
|
|
||||||
return;
|
|
||||||
|
|
||||||
appendStringInfoString(buf,
|
|
||||||
format->format_type == JS_FORMAT_JSONB ?
|
|
||||||
" FORMAT JSONB" : " FORMAT JSON");
|
|
||||||
|
|
||||||
if (format->encoding != JS_ENC_DEFAULT)
|
|
||||||
{
|
|
||||||
const char *encoding =
|
|
||||||
format->encoding == JS_ENC_UTF16 ? "UTF16" :
|
|
||||||
format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
|
|
||||||
|
|
||||||
appendStringInfo(buf, " ENCODING %s", encoding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_returning - Parse back a JsonReturning structure
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_returning(JsonReturning *returning, StringInfo buf,
|
|
||||||
bool json_format_by_default)
|
|
||||||
{
|
|
||||||
if (!OidIsValid(returning->typid))
|
|
||||||
return;
|
|
||||||
|
|
||||||
appendStringInfo(buf, " RETURNING %s",
|
|
||||||
format_type_with_typemod(returning->typid,
|
|
||||||
returning->typmod));
|
|
||||||
|
|
||||||
if (!json_format_by_default ||
|
|
||||||
returning->format->format_type !=
|
|
||||||
(returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
|
|
||||||
get_json_format(returning->format, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
get_json_behavior(JsonBehavior *behavior, deparse_context *context,
|
|
||||||
const char *on)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The order of array elements must correspond to the order of
|
|
||||||
* JsonBehaviorType members.
|
|
||||||
*/
|
|
||||||
const char *behavior_names[] =
|
|
||||||
{
|
|
||||||
" NULL",
|
|
||||||
" ERROR",
|
|
||||||
" EMPTY",
|
|
||||||
" TRUE",
|
|
||||||
" FALSE",
|
|
||||||
" UNKNOWN",
|
|
||||||
" EMPTY ARRAY",
|
|
||||||
" EMPTY OBJECT",
|
|
||||||
" DEFAULT "
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
|
|
||||||
elog(ERROR, "invalid json behavior type: %d", behavior->btype);
|
|
||||||
|
|
||||||
appendStringInfoString(context->buf, behavior_names[behavior->btype]);
|
|
||||||
|
|
||||||
if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
|
|
||||||
get_rule_expr(behavior->default_expr, context, false);
|
|
||||||
|
|
||||||
appendStringInfo(context->buf, " ON %s", on);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_expr_options
|
|
||||||
*
|
|
||||||
* Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
|
|
||||||
* JSON_TABLE columns.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
|
|
||||||
JsonBehaviorType default_behavior)
|
|
||||||
{
|
|
||||||
if (jsexpr->op == JSON_QUERY_OP)
|
|
||||||
{
|
|
||||||
if (jsexpr->wrapper == JSW_CONDITIONAL)
|
|
||||||
appendStringInfo(context->buf, " WITH CONDITIONAL WRAPPER");
|
|
||||||
else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
|
|
||||||
appendStringInfo(context->buf, " WITH UNCONDITIONAL WRAPPER");
|
|
||||||
|
|
||||||
if (jsexpr->omit_quotes)
|
|
||||||
appendStringInfo(context->buf, " OMIT QUOTES");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsexpr->op != JSON_EXISTS_OP &&
|
|
||||||
jsexpr->on_empty->btype != default_behavior)
|
|
||||||
get_json_behavior(jsexpr->on_empty, context, "EMPTY");
|
|
||||||
|
|
||||||
if (jsexpr->on_error->btype != default_behavior)
|
|
||||||
get_json_behavior(jsexpr->on_error, context, "ERROR");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* get_rule_expr - Parse back an expression
|
* get_rule_expr - Parse back an expression
|
||||||
*
|
*
|
||||||
@ -9760,116 +9624,6 @@ get_rule_expr(Node *node, deparse_context *context,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *jve = (JsonValueExpr *) node;
|
|
||||||
|
|
||||||
get_rule_expr((Node *) jve->raw_expr, context, false);
|
|
||||||
get_json_format(jve->format, context->buf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
get_json_constructor((JsonConstructorExpr *) node, context, false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
{
|
|
||||||
JsonIsPredicate *pred = (JsonIsPredicate *) node;
|
|
||||||
|
|
||||||
if (!PRETTY_PAREN(context))
|
|
||||||
appendStringInfoChar(context->buf, '(');
|
|
||||||
|
|
||||||
get_rule_expr_paren(pred->expr, context, true, node);
|
|
||||||
|
|
||||||
appendStringInfoString(context->buf, " IS JSON");
|
|
||||||
|
|
||||||
/* TODO: handle FORMAT clause */
|
|
||||||
|
|
||||||
switch (pred->item_type)
|
|
||||||
{
|
|
||||||
case JS_TYPE_SCALAR:
|
|
||||||
appendStringInfoString(context->buf, " SCALAR");
|
|
||||||
break;
|
|
||||||
case JS_TYPE_ARRAY:
|
|
||||||
appendStringInfoString(context->buf, " ARRAY");
|
|
||||||
break;
|
|
||||||
case JS_TYPE_OBJECT:
|
|
||||||
appendStringInfoString(context->buf, " OBJECT");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pred->unique_keys)
|
|
||||||
appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
|
|
||||||
|
|
||||||
if (!PRETTY_PAREN(context))
|
|
||||||
appendStringInfoChar(context->buf, ')');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = (JsonExpr *) node;
|
|
||||||
|
|
||||||
switch (jexpr->op)
|
|
||||||
{
|
|
||||||
case JSON_QUERY_OP:
|
|
||||||
appendStringInfoString(buf, "JSON_QUERY(");
|
|
||||||
break;
|
|
||||||
case JSON_VALUE_OP:
|
|
||||||
appendStringInfoString(buf, "JSON_VALUE(");
|
|
||||||
break;
|
|
||||||
case JSON_EXISTS_OP:
|
|
||||||
appendStringInfoString(buf, "JSON_EXISTS(");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "unexpected JsonExpr type: %d", jexpr->op);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_rule_expr(jexpr->formatted_expr, context, showimplicit);
|
|
||||||
|
|
||||||
appendStringInfoString(buf, ", ");
|
|
||||||
|
|
||||||
get_json_path_spec(jexpr->path_spec, context, showimplicit);
|
|
||||||
|
|
||||||
if (jexpr->passing_values)
|
|
||||||
{
|
|
||||||
ListCell *lc1,
|
|
||||||
*lc2;
|
|
||||||
bool needcomma = false;
|
|
||||||
|
|
||||||
appendStringInfoString(buf, " PASSING ");
|
|
||||||
|
|
||||||
forboth(lc1, jexpr->passing_names,
|
|
||||||
lc2, jexpr->passing_values)
|
|
||||||
{
|
|
||||||
if (needcomma)
|
|
||||||
appendStringInfoString(buf, ", ");
|
|
||||||
needcomma = true;
|
|
||||||
|
|
||||||
get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
|
|
||||||
appendStringInfo(buf, " AS %s",
|
|
||||||
((String *) lfirst_node(String, lc1))->sval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jexpr->op != JSON_EXISTS_OP ||
|
|
||||||
jexpr->returning->typid != BOOLOID)
|
|
||||||
get_json_returning(jexpr->returning, context->buf,
|
|
||||||
jexpr->op == JSON_QUERY_OP);
|
|
||||||
|
|
||||||
get_json_expr_options(jexpr, context,
|
|
||||||
jexpr->op == JSON_EXISTS_OP ?
|
|
||||||
JSON_BEHAVIOR_FALSE : JSON_BEHAVIOR_NULL);
|
|
||||||
|
|
||||||
appendStringInfoString(buf, ")");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case T_List:
|
case T_List:
|
||||||
{
|
{
|
||||||
char *sep;
|
char *sep;
|
||||||
@ -9993,7 +9747,6 @@ looks_like_function(Node *node)
|
|||||||
case T_MinMaxExpr:
|
case T_MinMaxExpr:
|
||||||
case T_SQLValueFunction:
|
case T_SQLValueFunction:
|
||||||
case T_XmlExpr:
|
case T_XmlExpr:
|
||||||
case T_JsonExpr:
|
|
||||||
/* these are all accepted by func_expr_common_subexpr */
|
/* these are all accepted by func_expr_common_subexpr */
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
@ -10139,103 +9892,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
|
|||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
|
|
||||||
{
|
|
||||||
if (ctor->absent_on_null)
|
|
||||||
{
|
|
||||||
if (ctor->type == JSCTOR_JSON_OBJECT ||
|
|
||||||
ctor->type == JSCTOR_JSON_OBJECTAGG)
|
|
||||||
appendStringInfoString(buf, " ABSENT ON NULL");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ctor->type == JSCTOR_JSON_ARRAY ||
|
|
||||||
ctor->type == JSCTOR_JSON_ARRAYAGG)
|
|
||||||
appendStringInfoString(buf, " NULL ON NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor->unique)
|
|
||||||
appendStringInfoString(buf, " WITH UNIQUE KEYS");
|
|
||||||
|
|
||||||
if (!((ctor->type == JSCTOR_JSON_PARSE ||
|
|
||||||
ctor->type == JSCTOR_JSON_SCALAR) &&
|
|
||||||
ctor->returning->typid == JSONOID))
|
|
||||||
get_json_returning(ctor->returning, buf, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
|
|
||||||
bool showimplicit)
|
|
||||||
{
|
|
||||||
StringInfo buf = context->buf;
|
|
||||||
const char *funcname;
|
|
||||||
int nargs;
|
|
||||||
ListCell *lc;
|
|
||||||
|
|
||||||
switch (ctor->type)
|
|
||||||
{
|
|
||||||
case JSCTOR_JSON_PARSE:
|
|
||||||
funcname = "JSON";
|
|
||||||
break;
|
|
||||||
case JSCTOR_JSON_SCALAR:
|
|
||||||
funcname = "JSON_SCALAR";
|
|
||||||
break;
|
|
||||||
case JSCTOR_JSON_SERIALIZE:
|
|
||||||
funcname = "JSON_SERIALIZE";
|
|
||||||
break;
|
|
||||||
case JSCTOR_JSON_OBJECT:
|
|
||||||
funcname = "JSON_OBJECT";
|
|
||||||
break;
|
|
||||||
case JSCTOR_JSON_ARRAY:
|
|
||||||
funcname = "JSON_ARRAY";
|
|
||||||
break;
|
|
||||||
case JSCTOR_JSON_OBJECTAGG:
|
|
||||||
get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
|
|
||||||
return;
|
|
||||||
case JSCTOR_JSON_ARRAYAGG:
|
|
||||||
get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
elog(ERROR, "invalid JsonConstructorExprType %d", ctor->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
appendStringInfo(buf, "%s(", funcname);
|
|
||||||
|
|
||||||
nargs = 0;
|
|
||||||
foreach(lc, ctor->args)
|
|
||||||
{
|
|
||||||
if (nargs > 0)
|
|
||||||
{
|
|
||||||
const char *sep = ctor->type == JSCTOR_JSON_OBJECT &&
|
|
||||||
(nargs % 2) != 0 ? " : " : ", ";
|
|
||||||
|
|
||||||
appendStringInfoString(buf, sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_rule_expr((Node *) lfirst(lc), context, true);
|
|
||||||
|
|
||||||
nargs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_json_constructor_options(ctor, buf);
|
|
||||||
|
|
||||||
appendStringInfo(buf, ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_agg_expr_helper - Parse back an Aggref node
|
* get_agg_expr - Parse back an Aggref node
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
get_agg_expr_helper(Aggref *aggref, deparse_context *context,
|
get_agg_expr(Aggref *aggref, deparse_context *context,
|
||||||
Aggref *original_aggref, const char *funcname,
|
Aggref *original_aggref)
|
||||||
const char *options, bool is_json_objectagg)
|
|
||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
Oid argtypes[FUNC_MAX_ARGS];
|
Oid argtypes[FUNC_MAX_ARGS];
|
||||||
int nargs;
|
int nargs;
|
||||||
bool use_variadic = false;
|
bool use_variadic;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For a combining aggregate, we look up and deparse the corresponding
|
* For a combining aggregate, we look up and deparse the corresponding
|
||||||
@ -10265,14 +9932,13 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context,
|
|||||||
/* Extract the argument types as seen by the parser */
|
/* Extract the argument types as seen by the parser */
|
||||||
nargs = get_aggregate_argtypes(aggref, argtypes);
|
nargs = get_aggregate_argtypes(aggref, argtypes);
|
||||||
|
|
||||||
if (!funcname)
|
|
||||||
funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
|
|
||||||
argtypes, aggref->aggvariadic,
|
|
||||||
&use_variadic,
|
|
||||||
context->special_exprkind);
|
|
||||||
|
|
||||||
/* Print the aggregate name, schema-qualified if needed */
|
/* Print the aggregate name, schema-qualified if needed */
|
||||||
appendStringInfo(buf, "%s(%s", funcname,
|
appendStringInfo(buf, "%s(%s",
|
||||||
|
generate_function_name(aggref->aggfnoid, nargs,
|
||||||
|
NIL, argtypes,
|
||||||
|
aggref->aggvariadic,
|
||||||
|
&use_variadic,
|
||||||
|
context->special_exprkind),
|
||||||
(aggref->aggdistinct != NIL) ? "DISTINCT " : "");
|
(aggref->aggdistinct != NIL) ? "DISTINCT " : "");
|
||||||
|
|
||||||
if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
|
if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
|
||||||
@ -10308,18 +9974,7 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context,
|
|||||||
if (tle->resjunk)
|
if (tle->resjunk)
|
||||||
continue;
|
continue;
|
||||||
if (i++ > 0)
|
if (i++ > 0)
|
||||||
{
|
|
||||||
if (is_json_objectagg)
|
|
||||||
{
|
|
||||||
if (i > 2)
|
|
||||||
break; /* skip ABSENT ON NULL and WITH UNIQUE
|
|
||||||
* args */
|
|
||||||
|
|
||||||
appendStringInfoString(buf, " : ");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
appendStringInfoString(buf, ", ");
|
appendStringInfoString(buf, ", ");
|
||||||
}
|
|
||||||
if (use_variadic && i == nargs)
|
if (use_variadic && i == nargs)
|
||||||
appendStringInfoString(buf, "VARIADIC ");
|
appendStringInfoString(buf, "VARIADIC ");
|
||||||
get_rule_expr(arg, context, true);
|
get_rule_expr(arg, context, true);
|
||||||
@ -10333,9 +9988,6 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options)
|
|
||||||
appendStringInfoString(buf, options);
|
|
||||||
|
|
||||||
if (aggref->aggfilter != NULL)
|
if (aggref->aggfilter != NULL)
|
||||||
{
|
{
|
||||||
appendStringInfoString(buf, ") FILTER (WHERE ");
|
appendStringInfoString(buf, ") FILTER (WHERE ");
|
||||||
@ -10345,16 +9997,6 @@ get_agg_expr_helper(Aggref *aggref, deparse_context *context,
|
|||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* get_agg_expr - Parse back an Aggref node
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_agg_expr(Aggref *aggref, deparse_context *context, Aggref *original_aggref)
|
|
||||||
{
|
|
||||||
get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a helper function for get_agg_expr(). It's used when we deparse
|
* This is a helper function for get_agg_expr(). It's used when we deparse
|
||||||
* a combining Aggref; resolve_special_varno locates the corresponding partial
|
* a combining Aggref; resolve_special_varno locates the corresponding partial
|
||||||
@ -10374,12 +10016,10 @@ get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_windowfunc_expr_helper - Parse back a WindowFunc node
|
* get_windowfunc_expr - Parse back a WindowFunc node
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
|
get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
|
||||||
const char *funcname, const char *options,
|
|
||||||
bool is_json_objectagg)
|
|
||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
Oid argtypes[FUNC_MAX_ARGS];
|
Oid argtypes[FUNC_MAX_ARGS];
|
||||||
@ -10403,30 +10043,16 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
|
|||||||
nargs++;
|
nargs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!funcname)
|
appendStringInfo(buf, "%s(",
|
||||||
funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
|
generate_function_name(wfunc->winfnoid, nargs,
|
||||||
argtypes, false, NULL,
|
argnames, argtypes,
|
||||||
context->special_exprkind);
|
false, NULL,
|
||||||
|
context->special_exprkind));
|
||||||
appendStringInfo(buf, "%s(", funcname);
|
|
||||||
|
|
||||||
/* winstar can be set only in zero-argument aggregates */
|
/* winstar can be set only in zero-argument aggregates */
|
||||||
if (wfunc->winstar)
|
if (wfunc->winstar)
|
||||||
appendStringInfoChar(buf, '*');
|
appendStringInfoChar(buf, '*');
|
||||||
else
|
|
||||||
{
|
|
||||||
if (is_json_objectagg)
|
|
||||||
{
|
|
||||||
get_rule_expr((Node *) linitial(wfunc->args), context, false);
|
|
||||||
appendStringInfoString(buf, " : ");
|
|
||||||
get_rule_expr((Node *) lsecond(wfunc->args), context, false);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
get_rule_expr((Node *) wfunc->args, context, true);
|
get_rule_expr((Node *) wfunc->args, context, true);
|
||||||
}
|
|
||||||
|
|
||||||
if (options)
|
|
||||||
appendStringInfoString(buf, options);
|
|
||||||
|
|
||||||
if (wfunc->aggfilter != NULL)
|
if (wfunc->aggfilter != NULL)
|
||||||
{
|
{
|
||||||
@ -10463,15 +10089,6 @@ get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* get_windowfunc_expr - Parse back a WindowFunc node
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
|
|
||||||
{
|
|
||||||
get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_func_sql_syntax - Parse back a SQL-syntax function call
|
* get_func_sql_syntax - Parse back a SQL-syntax function call
|
||||||
*
|
*
|
||||||
@ -10712,31 +10329,6 @@ get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
|
|
||||||
const char *funcname, bool is_json_objectagg)
|
|
||||||
{
|
|
||||||
StringInfoData options;
|
|
||||||
|
|
||||||
initStringInfo(&options);
|
|
||||||
get_json_constructor_options(ctor, &options);
|
|
||||||
|
|
||||||
if (IsA(ctor->func, Aggref))
|
|
||||||
get_agg_expr_helper((Aggref *) ctor->func, context,
|
|
||||||
(Aggref *) ctor->func,
|
|
||||||
funcname, options.data, is_json_objectagg);
|
|
||||||
else if (IsA(ctor->func, WindowFunc))
|
|
||||||
get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
|
|
||||||
funcname, options.data,
|
|
||||||
is_json_objectagg);
|
|
||||||
else
|
|
||||||
elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
|
|
||||||
nodeTag(ctor->func));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* get_coercion_expr
|
* get_coercion_expr
|
||||||
*
|
*
|
||||||
@ -11106,14 +10698,16 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
|
|||||||
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* get_xmltable - Parse back a XMLTABLE function
|
* get_tablefunc - Parse back a table function
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
|
get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
|
||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
|
|
||||||
|
/* XMLTABLE is the only existing implementation. */
|
||||||
|
|
||||||
appendStringInfoString(buf, "XMLTABLE(");
|
appendStringInfoString(buf, "XMLTABLE(");
|
||||||
|
|
||||||
if (tf->ns_uris != NIL)
|
if (tf->ns_uris != NIL)
|
||||||
@ -11204,271 +10798,6 @@ get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
|
|||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_nested_columns - Parse back nested JSON_TABLE columns
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_table_nested_columns(TableFunc *tf, Node *node,
|
|
||||||
deparse_context *context, bool showimplicit,
|
|
||||||
bool needcomma)
|
|
||||||
{
|
|
||||||
if (IsA(node, JsonTableSibling))
|
|
||||||
{
|
|
||||||
JsonTableSibling *n = (JsonTableSibling *) node;
|
|
||||||
|
|
||||||
get_json_table_nested_columns(tf, n->larg, context, showimplicit,
|
|
||||||
needcomma);
|
|
||||||
get_json_table_nested_columns(tf, n->rarg, context, showimplicit, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonTableParent *n = castNode(JsonTableParent, node);
|
|
||||||
|
|
||||||
if (needcomma)
|
|
||||||
appendStringInfoChar(context->buf, ',');
|
|
||||||
|
|
||||||
appendStringInfoChar(context->buf, ' ');
|
|
||||||
appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
|
|
||||||
get_const_expr(n->path, context, -1);
|
|
||||||
appendStringInfo(context->buf, " AS %s", quote_identifier(n->name));
|
|
||||||
get_json_table_columns(tf, n, context, showimplicit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_table_plan - Parse back a JSON_TABLE plan
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_table_plan(TableFunc *tf, Node *node, deparse_context *context,
|
|
||||||
bool parenthesize)
|
|
||||||
{
|
|
||||||
if (parenthesize)
|
|
||||||
appendStringInfoChar(context->buf, '(');
|
|
||||||
|
|
||||||
if (IsA(node, JsonTableSibling))
|
|
||||||
{
|
|
||||||
JsonTableSibling *n = (JsonTableSibling *) node;
|
|
||||||
|
|
||||||
get_json_table_plan(tf, n->larg, context,
|
|
||||||
IsA(n->larg, JsonTableSibling) ||
|
|
||||||
castNode(JsonTableParent, n->larg)->child);
|
|
||||||
|
|
||||||
appendStringInfoString(context->buf, n->cross ? " CROSS " : " UNION ");
|
|
||||||
|
|
||||||
get_json_table_plan(tf, n->rarg, context,
|
|
||||||
IsA(n->rarg, JsonTableSibling) ||
|
|
||||||
castNode(JsonTableParent, n->rarg)->child);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JsonTableParent *n = castNode(JsonTableParent, node);
|
|
||||||
|
|
||||||
appendStringInfoString(context->buf, quote_identifier(n->name));
|
|
||||||
|
|
||||||
if (n->child)
|
|
||||||
{
|
|
||||||
appendStringInfoString(context->buf,
|
|
||||||
n->outerJoin ? " OUTER " : " INNER ");
|
|
||||||
get_json_table_plan(tf, n->child, context,
|
|
||||||
IsA(n->child, JsonTableSibling));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parenthesize)
|
|
||||||
appendStringInfoChar(context->buf, ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get_json_table_columns - Parse back JSON_TABLE columns
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_table_columns(TableFunc *tf, JsonTableParent *node,
|
|
||||||
deparse_context *context, bool showimplicit)
|
|
||||||
{
|
|
||||||
StringInfo buf = context->buf;
|
|
||||||
JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
|
|
||||||
ListCell *lc_colname;
|
|
||||||
ListCell *lc_coltype;
|
|
||||||
ListCell *lc_coltypmod;
|
|
||||||
ListCell *lc_colvarexpr;
|
|
||||||
int colnum = 0;
|
|
||||||
|
|
||||||
appendStringInfoChar(buf, ' ');
|
|
||||||
appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
|
|
||||||
|
|
||||||
if (PRETTY_INDENT(context))
|
|
||||||
context->indentLevel += PRETTYINDENT_VAR;
|
|
||||||
|
|
||||||
forfour(lc_colname, tf->colnames,
|
|
||||||
lc_coltype, tf->coltypes,
|
|
||||||
lc_coltypmod, tf->coltypmods,
|
|
||||||
lc_colvarexpr, tf->colvalexprs)
|
|
||||||
{
|
|
||||||
char *colname = strVal(lfirst(lc_colname));
|
|
||||||
JsonExpr *colexpr;
|
|
||||||
Oid typid;
|
|
||||||
int32 typmod;
|
|
||||||
bool ordinality;
|
|
||||||
JsonBehaviorType default_behavior;
|
|
||||||
|
|
||||||
typid = lfirst_oid(lc_coltype);
|
|
||||||
typmod = lfirst_int(lc_coltypmod);
|
|
||||||
colexpr = castNode(JsonExpr, lfirst(lc_colvarexpr));
|
|
||||||
|
|
||||||
if (colnum < node->colMin)
|
|
||||||
{
|
|
||||||
colnum++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (colnum > node->colMax)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (colnum > node->colMin)
|
|
||||||
appendStringInfoString(buf, ", ");
|
|
||||||
|
|
||||||
colnum++;
|
|
||||||
|
|
||||||
ordinality = !colexpr;
|
|
||||||
|
|
||||||
appendContextKeyword(context, "", 0, 0, 0);
|
|
||||||
|
|
||||||
appendStringInfo(buf, "%s %s", quote_identifier(colname),
|
|
||||||
ordinality ? "FOR ORDINALITY" :
|
|
||||||
format_type_with_typemod(typid, typmod));
|
|
||||||
if (ordinality)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (colexpr->op == JSON_EXISTS_OP)
|
|
||||||
{
|
|
||||||
appendStringInfoString(buf, " EXISTS");
|
|
||||||
default_behavior = JSON_BEHAVIOR_FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (colexpr->op == JSON_QUERY_OP)
|
|
||||||
{
|
|
||||||
char typcategory;
|
|
||||||
bool typispreferred;
|
|
||||||
|
|
||||||
get_type_category_preferred(typid, &typcategory, &typispreferred);
|
|
||||||
|
|
||||||
if (typcategory == TYPCATEGORY_STRING)
|
|
||||||
appendStringInfoString(buf,
|
|
||||||
colexpr->format->format_type == JS_FORMAT_JSONB ?
|
|
||||||
" FORMAT JSONB" : " FORMAT JSON");
|
|
||||||
}
|
|
||||||
|
|
||||||
default_behavior = JSON_BEHAVIOR_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
|
|
||||||
default_behavior = JSON_BEHAVIOR_ERROR;
|
|
||||||
|
|
||||||
appendStringInfoString(buf, " PATH ");
|
|
||||||
|
|
||||||
get_json_path_spec(colexpr->path_spec, context, showimplicit);
|
|
||||||
|
|
||||||
get_json_expr_options(colexpr, context, default_behavior);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node->child)
|
|
||||||
get_json_table_nested_columns(tf, node->child, context, showimplicit,
|
|
||||||
node->colMax >= node->colMin);
|
|
||||||
|
|
||||||
if (PRETTY_INDENT(context))
|
|
||||||
context->indentLevel -= PRETTYINDENT_VAR;
|
|
||||||
|
|
||||||
appendContextKeyword(context, ")", 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* get_json_table - Parse back a JSON_TABLE function
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
|
|
||||||
{
|
|
||||||
StringInfo buf = context->buf;
|
|
||||||
JsonExpr *jexpr = castNode(JsonExpr, tf->docexpr);
|
|
||||||
JsonTableParent *root = castNode(JsonTableParent, tf->plan);
|
|
||||||
|
|
||||||
appendStringInfoString(buf, "JSON_TABLE(");
|
|
||||||
|
|
||||||
if (PRETTY_INDENT(context))
|
|
||||||
context->indentLevel += PRETTYINDENT_VAR;
|
|
||||||
|
|
||||||
appendContextKeyword(context, "", 0, 0, 0);
|
|
||||||
|
|
||||||
get_rule_expr(jexpr->formatted_expr, context, showimplicit);
|
|
||||||
|
|
||||||
appendStringInfoString(buf, ", ");
|
|
||||||
|
|
||||||
get_const_expr(root->path, context, -1);
|
|
||||||
|
|
||||||
appendStringInfo(buf, " AS %s", quote_identifier(root->name));
|
|
||||||
|
|
||||||
if (jexpr->passing_values)
|
|
||||||
{
|
|
||||||
ListCell *lc1,
|
|
||||||
*lc2;
|
|
||||||
bool needcomma = false;
|
|
||||||
|
|
||||||
appendStringInfoChar(buf, ' ');
|
|
||||||
appendContextKeyword(context, "PASSING ", 0, 0, 0);
|
|
||||||
|
|
||||||
if (PRETTY_INDENT(context))
|
|
||||||
context->indentLevel += PRETTYINDENT_VAR;
|
|
||||||
|
|
||||||
forboth(lc1, jexpr->passing_names,
|
|
||||||
lc2, jexpr->passing_values)
|
|
||||||
{
|
|
||||||
if (needcomma)
|
|
||||||
appendStringInfoString(buf, ", ");
|
|
||||||
needcomma = true;
|
|
||||||
|
|
||||||
appendContextKeyword(context, "", 0, 0, 0);
|
|
||||||
|
|
||||||
get_rule_expr((Node *) lfirst(lc2), context, false);
|
|
||||||
appendStringInfo(buf, " AS %s",
|
|
||||||
quote_identifier((lfirst_node(String, lc1))->sval)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PRETTY_INDENT(context))
|
|
||||||
context->indentLevel -= PRETTYINDENT_VAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_json_table_columns(tf, root, context, showimplicit);
|
|
||||||
|
|
||||||
appendStringInfoChar(buf, ' ');
|
|
||||||
appendContextKeyword(context, "PLAN ", 0, 0, 0);
|
|
||||||
get_json_table_plan(tf, (Node *) root, context, true);
|
|
||||||
|
|
||||||
if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY)
|
|
||||||
get_json_behavior(jexpr->on_error, context, "ERROR");
|
|
||||||
|
|
||||||
if (PRETTY_INDENT(context))
|
|
||||||
context->indentLevel -= PRETTYINDENT_VAR;
|
|
||||||
|
|
||||||
appendContextKeyword(context, ")", 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* get_tablefunc - Parse back a table function
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
|
|
||||||
{
|
|
||||||
/* XMLTABLE and JSON_TABLE are the only existing implementations. */
|
|
||||||
|
|
||||||
if (tf->functype == TFT_XMLTABLE)
|
|
||||||
get_xmltable(tf, context, showimplicit);
|
|
||||||
else if (tf->functype == TFT_JSON_TABLE)
|
|
||||||
get_json_table(tf, context, showimplicit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* get_from_clause - Parse back a FROM clause
|
* get_from_clause - Parse back a FROM clause
|
||||||
*
|
*
|
||||||
|
@ -737,76 +737,6 @@ JumbleExpr(JumbleState *jstate, Node *node)
|
|||||||
JumbleExpr(jstate, (Node *) conf->exclRelTlist);
|
JumbleExpr(jstate, (Node *) conf->exclRelTlist);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_JsonFormat:
|
|
||||||
{
|
|
||||||
JsonFormat *format = (JsonFormat *) node;
|
|
||||||
|
|
||||||
APP_JUMB(format->format_type);
|
|
||||||
APP_JUMB(format->encoding);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonReturning:
|
|
||||||
{
|
|
||||||
JsonReturning *returning = (JsonReturning *) node;
|
|
||||||
|
|
||||||
JumbleExpr(jstate, (Node *) returning->format);
|
|
||||||
APP_JUMB(returning->typid);
|
|
||||||
APP_JUMB(returning->typmod);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonValueExpr:
|
|
||||||
{
|
|
||||||
JsonValueExpr *expr = (JsonValueExpr *) node;
|
|
||||||
|
|
||||||
JumbleExpr(jstate, (Node *) expr->raw_expr);
|
|
||||||
JumbleExpr(jstate, (Node *) expr->formatted_expr);
|
|
||||||
JumbleExpr(jstate, (Node *) expr->format);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonConstructorExpr:
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
|
|
||||||
|
|
||||||
APP_JUMB(ctor->type);
|
|
||||||
JumbleExpr(jstate, (Node *) ctor->args);
|
|
||||||
JumbleExpr(jstate, (Node *) ctor->func);
|
|
||||||
JumbleExpr(jstate, (Node *) ctor->coercion);
|
|
||||||
JumbleExpr(jstate, (Node *) ctor->returning);
|
|
||||||
APP_JUMB(ctor->absent_on_null);
|
|
||||||
APP_JUMB(ctor->unique);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonIsPredicate:
|
|
||||||
{
|
|
||||||
JsonIsPredicate *pred = (JsonIsPredicate *) node;
|
|
||||||
|
|
||||||
JumbleExpr(jstate, (Node *) pred->expr);
|
|
||||||
JumbleExpr(jstate, (Node *) pred->format);
|
|
||||||
APP_JUMB(pred->item_type);
|
|
||||||
APP_JUMB(pred->unique_keys);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_JsonExpr:
|
|
||||||
{
|
|
||||||
JsonExpr *jexpr = (JsonExpr *) node;
|
|
||||||
|
|
||||||
APP_JUMB(jexpr->op);
|
|
||||||
JumbleExpr(jstate, jexpr->formatted_expr);
|
|
||||||
JumbleExpr(jstate, jexpr->path_spec);
|
|
||||||
foreach(temp, jexpr->passing_names)
|
|
||||||
{
|
|
||||||
APP_JUMB_STRING(lfirst_node(String, temp)->sval);
|
|
||||||
}
|
|
||||||
JumbleExpr(jstate, (Node *) jexpr->passing_values);
|
|
||||||
if (jexpr->on_empty)
|
|
||||||
{
|
|
||||||
APP_JUMB(jexpr->on_empty->btype);
|
|
||||||
JumbleExpr(jstate, jexpr->on_empty->default_expr);
|
|
||||||
}
|
|
||||||
APP_JUMB(jexpr->on_error->btype);
|
|
||||||
JumbleExpr(jstate, jexpr->on_error->default_expr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case T_List:
|
case T_List:
|
||||||
foreach(temp, (List *) node)
|
foreach(temp, (List *) node)
|
||||||
{
|
{
|
||||||
@ -879,11 +809,9 @@ JumbleExpr(JumbleState *jstate, Node *node)
|
|||||||
{
|
{
|
||||||
TableFunc *tablefunc = (TableFunc *) node;
|
TableFunc *tablefunc = (TableFunc *) node;
|
||||||
|
|
||||||
APP_JUMB(tablefunc->functype);
|
|
||||||
JumbleExpr(jstate, tablefunc->docexpr);
|
JumbleExpr(jstate, tablefunc->docexpr);
|
||||||
JumbleExpr(jstate, tablefunc->rowexpr);
|
JumbleExpr(jstate, tablefunc->rowexpr);
|
||||||
JumbleExpr(jstate, (Node *) tablefunc->colexprs);
|
JumbleExpr(jstate, (Node *) tablefunc->colexprs);
|
||||||
JumbleExpr(jstate, (Node *) tablefunc->colvalexprs);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case T_TableSampleClause:
|
case T_TableSampleClause:
|
||||||
|
@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202208251
|
#define CATALOG_VERSION_NO 202209011
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -571,36 +571,14 @@
|
|||||||
# json
|
# json
|
||||||
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
|
{ aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
|
||||||
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
|
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
|
||||||
{ aggfnoid => 'json_agg_strict', aggtransfn => 'json_agg_strict_transfn',
|
|
||||||
aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
{ aggfnoid => 'json_object_agg', aggtransfn => 'json_object_agg_transfn',
|
{ aggfnoid => 'json_object_agg', aggtransfn => 'json_object_agg_transfn',
|
||||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
||||||
{ aggfnoid => 'json_object_agg_unique',
|
|
||||||
aggtransfn => 'json_object_agg_unique_transfn',
|
|
||||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
{ aggfnoid => 'json_object_agg_strict',
|
|
||||||
aggtransfn => 'json_object_agg_strict_transfn',
|
|
||||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
{ aggfnoid => 'json_object_agg_unique_strict',
|
|
||||||
aggtransfn => 'json_object_agg_unique_strict_transfn',
|
|
||||||
aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
|
|
||||||
# jsonb
|
# jsonb
|
||||||
{ aggfnoid => 'jsonb_agg', aggtransfn => 'jsonb_agg_transfn',
|
{ aggfnoid => 'jsonb_agg', aggtransfn => 'jsonb_agg_transfn',
|
||||||
aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
|
aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
|
||||||
{ aggfnoid => 'jsonb_agg_strict', aggtransfn => 'jsonb_agg_strict_transfn',
|
|
||||||
aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
{ aggfnoid => 'jsonb_object_agg', aggtransfn => 'jsonb_object_agg_transfn',
|
{ aggfnoid => 'jsonb_object_agg', aggtransfn => 'jsonb_object_agg_transfn',
|
||||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
||||||
{ aggfnoid => 'jsonb_object_agg_unique',
|
|
||||||
aggtransfn => 'jsonb_object_agg_unique_transfn',
|
|
||||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
{ aggfnoid => 'jsonb_object_agg_strict',
|
|
||||||
aggtransfn => 'jsonb_object_agg_strict_transfn',
|
|
||||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
{ aggfnoid => 'jsonb_object_agg_unique_strict',
|
|
||||||
aggtransfn => 'jsonb_object_agg_unique_strict_transfn',
|
|
||||||
aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
|
|
||||||
|
|
||||||
# ordered-set and hypothetical-set aggregates
|
# ordered-set and hypothetical-set aggregates
|
||||||
{ aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
|
{ aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
|
||||||
|
@ -8785,10 +8785,6 @@
|
|||||||
proname => 'json_agg_transfn', proisstrict => 'f', provolatile => 's',
|
proname => 'json_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
prorettype => 'internal', proargtypes => 'internal anyelement',
|
||||||
prosrc => 'json_agg_transfn' },
|
prosrc => 'json_agg_transfn' },
|
||||||
{ oid => '6208', descr => 'json aggregate transition function',
|
|
||||||
proname => 'json_agg_strict_transfn', proisstrict => 'f', provolatile => 's',
|
|
||||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
|
||||||
prosrc => 'json_agg_strict_transfn' },
|
|
||||||
{ oid => '3174', descr => 'json aggregate final function',
|
{ oid => '3174', descr => 'json aggregate final function',
|
||||||
proname => 'json_agg_finalfn', proisstrict => 'f', prorettype => 'json',
|
proname => 'json_agg_finalfn', proisstrict => 'f', prorettype => 'json',
|
||||||
proargtypes => 'internal', prosrc => 'json_agg_finalfn' },
|
proargtypes => 'internal', prosrc => 'json_agg_finalfn' },
|
||||||
@ -8796,29 +8792,10 @@
|
|||||||
proname => 'json_agg', prokind => 'a', proisstrict => 'f', provolatile => 's',
|
proname => 'json_agg', prokind => 'a', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'json', proargtypes => 'anyelement',
|
prorettype => 'json', proargtypes => 'anyelement',
|
||||||
prosrc => 'aggregate_dummy' },
|
prosrc => 'aggregate_dummy' },
|
||||||
{ oid => '6209', descr => 'aggregate input into json',
|
|
||||||
proname => 'json_agg_strict', prokind => 'a', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'json', proargtypes => 'anyelement',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '3180', descr => 'json object aggregate transition function',
|
{ oid => '3180', descr => 'json object aggregate transition function',
|
||||||
proname => 'json_object_agg_transfn', proisstrict => 'f', provolatile => 's',
|
proname => 'json_object_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'internal', proargtypes => 'internal any any',
|
prorettype => 'internal', proargtypes => 'internal any any',
|
||||||
prosrc => 'json_object_agg_transfn' },
|
prosrc => 'json_object_agg_transfn' },
|
||||||
{ oid => '6210', descr => 'json object aggregate transition function',
|
|
||||||
proname => 'json_object_agg_strict_transfn', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'internal',
|
|
||||||
proargtypes => 'internal any any',
|
|
||||||
prosrc => 'json_object_agg_strict_transfn' },
|
|
||||||
{ oid => '6211', descr => 'json object aggregate transition function',
|
|
||||||
proname => 'json_object_agg_unique_transfn', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'internal',
|
|
||||||
proargtypes => 'internal any any',
|
|
||||||
prosrc => 'json_object_agg_unique_transfn' },
|
|
||||||
{ oid => '6212', descr => 'json object aggregate transition function',
|
|
||||||
proname => 'json_object_agg_unique_strict_transfn', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'internal',
|
|
||||||
proargtypes => 'internal any any',
|
|
||||||
prosrc => 'json_object_agg_unique_strict_transfn' },
|
|
||||||
{ oid => '3196', descr => 'json object aggregate final function',
|
{ oid => '3196', descr => 'json object aggregate final function',
|
||||||
proname => 'json_object_agg_finalfn', proisstrict => 'f',
|
proname => 'json_object_agg_finalfn', proisstrict => 'f',
|
||||||
prorettype => 'json', proargtypes => 'internal',
|
prorettype => 'json', proargtypes => 'internal',
|
||||||
@ -8827,20 +8804,6 @@
|
|||||||
proname => 'json_object_agg', prokind => 'a', proisstrict => 'f',
|
proname => 'json_object_agg', prokind => 'a', proisstrict => 'f',
|
||||||
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
||||||
prosrc => 'aggregate_dummy' },
|
prosrc => 'aggregate_dummy' },
|
||||||
{ oid => '6213', descr => 'aggregate non-NULL input into a json object',
|
|
||||||
proname => 'json_object_agg_strict', prokind => 'a', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '6214',
|
|
||||||
descr => 'aggregate input into a json object with unique keys',
|
|
||||||
proname => 'json_object_agg_unique', prokind => 'a', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'json', proargtypes => 'any any',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '6215',
|
|
||||||
descr => 'aggregate non-NULL input into a json object with unique keys',
|
|
||||||
proname => 'json_object_agg_unique_strict', prokind => 'a',
|
|
||||||
proisstrict => 'f', provolatile => 's', prorettype => 'json',
|
|
||||||
proargtypes => 'any any', prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '3198', descr => 'build a json array from any inputs',
|
{ oid => '3198', descr => 'build a json array from any inputs',
|
||||||
proname => 'json_build_array', provariadic => 'any', proisstrict => 'f',
|
proname => 'json_build_array', provariadic => 'any', proisstrict => 'f',
|
||||||
provolatile => 's', prorettype => 'json', proargtypes => 'any',
|
provolatile => 's', prorettype => 'json', proargtypes => 'any',
|
||||||
@ -9713,10 +9676,6 @@
|
|||||||
proname => 'jsonb_agg_transfn', proisstrict => 'f', provolatile => 's',
|
proname => 'jsonb_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
prorettype => 'internal', proargtypes => 'internal anyelement',
|
||||||
prosrc => 'jsonb_agg_transfn' },
|
prosrc => 'jsonb_agg_transfn' },
|
||||||
{ oid => '6216', descr => 'jsonb aggregate transition function',
|
|
||||||
proname => 'jsonb_agg_strict_transfn', proisstrict => 'f', provolatile => 's',
|
|
||||||
prorettype => 'internal', proargtypes => 'internal anyelement',
|
|
||||||
prosrc => 'jsonb_agg_strict_transfn' },
|
|
||||||
{ oid => '3266', descr => 'jsonb aggregate final function',
|
{ oid => '3266', descr => 'jsonb aggregate final function',
|
||||||
proname => 'jsonb_agg_finalfn', proisstrict => 'f', provolatile => 's',
|
proname => 'jsonb_agg_finalfn', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'jsonb', proargtypes => 'internal',
|
prorettype => 'jsonb', proargtypes => 'internal',
|
||||||
@ -9725,29 +9684,10 @@
|
|||||||
proname => 'jsonb_agg', prokind => 'a', proisstrict => 'f',
|
proname => 'jsonb_agg', prokind => 'a', proisstrict => 'f',
|
||||||
provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
|
provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
|
||||||
prosrc => 'aggregate_dummy' },
|
prosrc => 'aggregate_dummy' },
|
||||||
{ oid => '6217', descr => 'aggregate input into jsonb skipping nulls',
|
|
||||||
proname => 'jsonb_agg_strict', prokind => 'a', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '3268', descr => 'jsonb object aggregate transition function',
|
{ oid => '3268', descr => 'jsonb object aggregate transition function',
|
||||||
proname => 'jsonb_object_agg_transfn', proisstrict => 'f', provolatile => 's',
|
proname => 'jsonb_object_agg_transfn', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'internal', proargtypes => 'internal any any',
|
prorettype => 'internal', proargtypes => 'internal any any',
|
||||||
prosrc => 'jsonb_object_agg_transfn' },
|
prosrc => 'jsonb_object_agg_transfn' },
|
||||||
{ oid => '6218', descr => 'jsonb object aggregate transition function',
|
|
||||||
proname => 'jsonb_object_agg_strict_transfn', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'internal',
|
|
||||||
proargtypes => 'internal any any',
|
|
||||||
prosrc => 'jsonb_object_agg_strict_transfn' },
|
|
||||||
{ oid => '6219', descr => 'jsonb object aggregate transition function',
|
|
||||||
proname => 'jsonb_object_agg_unique_transfn', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'internal',
|
|
||||||
proargtypes => 'internal any any',
|
|
||||||
prosrc => 'jsonb_object_agg_unique_transfn' },
|
|
||||||
{ oid => '6220', descr => 'jsonb object aggregate transition function',
|
|
||||||
proname => 'jsonb_object_agg_unique_strict_transfn', proisstrict => 'f',
|
|
||||||
provolatile => 's', prorettype => 'internal',
|
|
||||||
proargtypes => 'internal any any',
|
|
||||||
prosrc => 'jsonb_object_agg_unique_strict_transfn' },
|
|
||||||
{ oid => '3269', descr => 'jsonb object aggregate final function',
|
{ oid => '3269', descr => 'jsonb object aggregate final function',
|
||||||
proname => 'jsonb_object_agg_finalfn', proisstrict => 'f', provolatile => 's',
|
proname => 'jsonb_object_agg_finalfn', proisstrict => 'f', provolatile => 's',
|
||||||
prorettype => 'jsonb', proargtypes => 'internal',
|
prorettype => 'jsonb', proargtypes => 'internal',
|
||||||
@ -9756,20 +9696,6 @@
|
|||||||
proname => 'jsonb_object_agg', prokind => 'a', proisstrict => 'f',
|
proname => 'jsonb_object_agg', prokind => 'a', proisstrict => 'f',
|
||||||
prorettype => 'jsonb', proargtypes => 'any any',
|
prorettype => 'jsonb', proargtypes => 'any any',
|
||||||
prosrc => 'aggregate_dummy' },
|
prosrc => 'aggregate_dummy' },
|
||||||
{ oid => '6221', descr => 'aggregate non-NULL inputs into jsonb object',
|
|
||||||
proname => 'jsonb_object_agg_strict', prokind => 'a', proisstrict => 'f',
|
|
||||||
prorettype => 'jsonb', proargtypes => 'any any',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '6222',
|
|
||||||
descr => 'aggregate inputs into jsonb object checking key uniqueness',
|
|
||||||
proname => 'jsonb_object_agg_unique', prokind => 'a', proisstrict => 'f',
|
|
||||||
prorettype => 'jsonb', proargtypes => 'any any',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '6223',
|
|
||||||
descr => 'aggregate non-NULL inputs into jsonb object checking key uniqueness',
|
|
||||||
proname => 'jsonb_object_agg_unique_strict', prokind => 'a',
|
|
||||||
proisstrict => 'f', prorettype => 'jsonb', proargtypes => 'any any',
|
|
||||||
prosrc => 'aggregate_dummy' },
|
|
||||||
{ oid => '3271', descr => 'build a jsonb array from any inputs',
|
{ oid => '3271', descr => 'build a jsonb array from any inputs',
|
||||||
proname => 'jsonb_build_array', provariadic => 'any', proisstrict => 'f',
|
proname => 'jsonb_build_array', provariadic => 'any', proisstrict => 'f',
|
||||||
provolatile => 's', prorettype => 'jsonb', proargtypes => 'any',
|
provolatile => 's', prorettype => 'jsonb', proargtypes => 'any',
|
||||||
|
@ -21,9 +21,6 @@
|
|||||||
struct ExprEvalStep;
|
struct ExprEvalStep;
|
||||||
struct SubscriptingRefState;
|
struct SubscriptingRefState;
|
||||||
struct ScalarArrayOpExprHashTable;
|
struct ScalarArrayOpExprHashTable;
|
||||||
struct JsonbValue;
|
|
||||||
struct JsonExprState;
|
|
||||||
struct JsonConstructorExprState;
|
|
||||||
|
|
||||||
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
|
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
|
||||||
/* expression's interpreter has been initialized */
|
/* expression's interpreter has been initialized */
|
||||||
@ -242,9 +239,6 @@ typedef enum ExprEvalOp
|
|||||||
EEOP_GROUPING_FUNC,
|
EEOP_GROUPING_FUNC,
|
||||||
EEOP_WINDOW_FUNC,
|
EEOP_WINDOW_FUNC,
|
||||||
EEOP_SUBPLAN,
|
EEOP_SUBPLAN,
|
||||||
EEOP_JSON_CONSTRUCTOR,
|
|
||||||
EEOP_IS_JSON,
|
|
||||||
EEOP_JSONEXPR,
|
|
||||||
|
|
||||||
/* aggregation related nodes */
|
/* aggregation related nodes */
|
||||||
EEOP_AGG_STRICT_DESERIALIZE,
|
EEOP_AGG_STRICT_DESERIALIZE,
|
||||||
@ -679,25 +673,6 @@ typedef struct ExprEvalStep
|
|||||||
int transno;
|
int transno;
|
||||||
int setoff;
|
int setoff;
|
||||||
} agg_trans;
|
} agg_trans;
|
||||||
|
|
||||||
/* for EEOP_JSON_CONSTRUCTOR */
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
struct JsonConstructorExprState *jcstate;
|
|
||||||
} json_constructor;
|
|
||||||
|
|
||||||
/* for EEOP_IS_JSON */
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
JsonIsPredicate *pred; /* original expression node */
|
|
||||||
} is_json;
|
|
||||||
|
|
||||||
/* for EEOP_JSONEXPR */
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
struct JsonExprState *jsestate;
|
|
||||||
} jsonexpr;
|
|
||||||
|
|
||||||
} d;
|
} d;
|
||||||
} ExprEvalStep;
|
} ExprEvalStep;
|
||||||
|
|
||||||
@ -742,64 +717,6 @@ typedef struct SubscriptExecSteps
|
|||||||
ExecEvalSubroutine sbs_fetch_old; /* fetch old value for assignment */
|
ExecEvalSubroutine sbs_fetch_old; /* fetch old value for assignment */
|
||||||
} SubscriptExecSteps;
|
} SubscriptExecSteps;
|
||||||
|
|
||||||
/* EEOP_JSON_CONSTRUCTOR state, too big to inline */
|
|
||||||
typedef struct JsonConstructorExprState
|
|
||||||
{
|
|
||||||
JsonConstructorExpr *constructor;
|
|
||||||
Datum *arg_values;
|
|
||||||
bool *arg_nulls;
|
|
||||||
Oid *arg_types;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
int category;
|
|
||||||
Oid outfuncid;
|
|
||||||
} *arg_type_cache; /* cache for datum_to_json[b]() */
|
|
||||||
int nargs;
|
|
||||||
} JsonConstructorExprState;
|
|
||||||
|
|
||||||
/* EEOP_JSONEXPR state, too big to inline */
|
|
||||||
typedef struct JsonExprState
|
|
||||||
{
|
|
||||||
JsonExpr *jsexpr; /* original expression node */
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
FmgrInfo func; /* typinput function for output type */
|
|
||||||
Oid typioparam;
|
|
||||||
} input; /* I/O info for output type */
|
|
||||||
|
|
||||||
NullableDatum
|
|
||||||
*formatted_expr, /* formatted context item value */
|
|
||||||
*res_expr, /* result item */
|
|
||||||
*coercion_expr, /* input for JSON item coercion */
|
|
||||||
*pathspec; /* path specification value */
|
|
||||||
|
|
||||||
ExprState *result_expr; /* coerced to output type */
|
|
||||||
ExprState *default_on_empty; /* ON EMPTY DEFAULT expression */
|
|
||||||
ExprState *default_on_error; /* ON ERROR DEFAULT expression */
|
|
||||||
List *args; /* passing arguments */
|
|
||||||
|
|
||||||
void *cache; /* cache for json_populate_type() */
|
|
||||||
|
|
||||||
struct JsonCoercionsState
|
|
||||||
{
|
|
||||||
struct JsonCoercionState
|
|
||||||
{
|
|
||||||
JsonCoercion *coercion; /* coercion expression */
|
|
||||||
ExprState *estate; /* coercion expression state */
|
|
||||||
} null,
|
|
||||||
string,
|
|
||||||
numeric ,
|
|
||||||
boolean,
|
|
||||||
date,
|
|
||||||
time,
|
|
||||||
timetz,
|
|
||||||
timestamp,
|
|
||||||
timestamptz,
|
|
||||||
composite;
|
|
||||||
} coercions; /* states for coercion from SQL/JSON item
|
|
||||||
* types directly to the output type */
|
|
||||||
} JsonExprState;
|
|
||||||
|
|
||||||
/* functions in execExpr.c */
|
/* functions in execExpr.c */
|
||||||
extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
|
extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
|
||||||
@ -850,7 +767,6 @@ extern void ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op,
|
|||||||
extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalJsonIsPredicate(ExprState *state, ExprEvalStep *op);
|
|
||||||
extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
|
extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
|
||||||
extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op,
|
extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op,
|
||||||
ExprContext *econtext);
|
ExprContext *econtext);
|
||||||
@ -858,20 +774,6 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
|
|||||||
ExprContext *econtext);
|
ExprContext *econtext);
|
||||||
extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
|
extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
|
||||||
ExprContext *econtext, TupleTableSlot *slot);
|
ExprContext *econtext, TupleTableSlot *slot);
|
||||||
extern void ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
|
|
||||||
ExprContext *econtext);
|
|
||||||
extern void ExecEvalJson(ExprState *state, ExprEvalStep *op,
|
|
||||||
ExprContext *econtext);
|
|
||||||
extern Datum ExecPrepareJsonItemCoercion(struct JsonbValue *item,
|
|
||||||
JsonReturning *returning,
|
|
||||||
struct JsonCoercionsState *coercions,
|
|
||||||
struct JsonCoercionState **pjcstate);
|
|
||||||
extern bool ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr,
|
|
||||||
struct JsonCoercionsState *);
|
|
||||||
extern Datum ExecEvalExprPassingCaseValue(ExprState *estate,
|
|
||||||
ExprContext *econtext, bool *isnull,
|
|
||||||
Datum caseval_datum,
|
|
||||||
bool caseval_isnull);
|
|
||||||
|
|
||||||
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
|
extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
|
||||||
ExprContext *aggcontext);
|
ExprContext *aggcontext);
|
||||||
|
@ -265,8 +265,6 @@ ExecProcNode(PlanState *node)
|
|||||||
*/
|
*/
|
||||||
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
|
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
|
||||||
extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
|
extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
|
||||||
extern ExprState *ExecInitExprWithCaseValue(Expr *node, PlanState *parent,
|
|
||||||
Datum *caseval, bool *casenull);
|
|
||||||
extern ExprState *ExecInitQual(List *qual, PlanState *parent);
|
extern ExprState *ExecInitQual(List *qual, PlanState *parent);
|
||||||
extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
|
extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
|
||||||
extern List *ExecInitExprList(List *nodes, PlanState *parent);
|
extern List *ExecInitExprList(List *nodes, PlanState *parent);
|
||||||
|
@ -106,16 +106,4 @@ extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int loc
|
|||||||
|
|
||||||
extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
|
extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
|
||||||
|
|
||||||
extern JsonFormat *makeJsonFormat(JsonFormatType type, JsonEncoding encoding,
|
|
||||||
int location);
|
|
||||||
extern JsonValueExpr *makeJsonValueExpr(Expr *expr, JsonFormat *format);
|
|
||||||
extern JsonBehavior *makeJsonBehavior(JsonBehaviorType type, Node *expr);
|
|
||||||
extern Node *makeJsonTableJoinedPlan(JsonTablePlanJoinType type,
|
|
||||||
Node *plan1, Node *plan2, int location);
|
|
||||||
extern Node *makeJsonKeyValue(Node *key, Node *value);
|
|
||||||
extern Node *makeJsonIsPredicate(Node *expr, JsonFormat *format,
|
|
||||||
JsonValueType item_type, bool unique_keys,
|
|
||||||
int location);
|
|
||||||
extern JsonEncoding makeJsonEncoding(char *name);
|
|
||||||
|
|
||||||
#endif /* MAKEFUNC_H */
|
#endif /* MAKEFUNC_H */
|
||||||
|
@ -1606,293 +1606,6 @@ typedef struct TriggerTransition
|
|||||||
bool isTable;
|
bool isTable;
|
||||||
} TriggerTransition;
|
} TriggerTransition;
|
||||||
|
|
||||||
/* Nodes for SQL/JSON support */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonQuotes -
|
|
||||||
* representation of [KEEP|OMIT] QUOTES clause for JSON_QUERY()
|
|
||||||
*/
|
|
||||||
typedef enum JsonQuotes
|
|
||||||
{
|
|
||||||
JS_QUOTES_UNSPEC, /* unspecified */
|
|
||||||
JS_QUOTES_KEEP, /* KEEP QUOTES */
|
|
||||||
JS_QUOTES_OMIT /* OMIT QUOTES */
|
|
||||||
} JsonQuotes;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableColumnType -
|
|
||||||
* enumeration of JSON_TABLE column types
|
|
||||||
*/
|
|
||||||
typedef enum JsonTableColumnType
|
|
||||||
{
|
|
||||||
JTC_FOR_ORDINALITY,
|
|
||||||
JTC_REGULAR,
|
|
||||||
JTC_EXISTS,
|
|
||||||
JTC_FORMATTED,
|
|
||||||
JTC_NESTED,
|
|
||||||
} JsonTableColumnType;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonOutput -
|
|
||||||
* representation of JSON output clause (RETURNING type [FORMAT format])
|
|
||||||
*/
|
|
||||||
typedef struct JsonOutput
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
TypeName *typeName; /* RETURNING type name, if specified */
|
|
||||||
JsonReturning *returning; /* RETURNING FORMAT clause and type Oids */
|
|
||||||
} JsonOutput;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonArgument -
|
|
||||||
* representation of argument from JSON PASSING clause
|
|
||||||
*/
|
|
||||||
typedef struct JsonArgument
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonValueExpr *val; /* argument value expression */
|
|
||||||
char *name; /* argument name */
|
|
||||||
} JsonArgument;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonCommon -
|
|
||||||
* representation of common syntax of functions using JSON path
|
|
||||||
*/
|
|
||||||
typedef struct JsonCommon
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonValueExpr *expr; /* context item expression */
|
|
||||||
Node *pathspec; /* JSON path specification expression */
|
|
||||||
char *pathname; /* path name, if any */
|
|
||||||
List *passing; /* list of PASSING clause arguments, if any */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonCommon;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonFuncExpr -
|
|
||||||
* untransformed representation of JSON function expressions
|
|
||||||
*/
|
|
||||||
typedef struct JsonFuncExpr
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonExprOp op; /* expression type */
|
|
||||||
JsonCommon *common; /* common syntax */
|
|
||||||
JsonOutput *output; /* output clause, if specified */
|
|
||||||
JsonBehavior *on_empty; /* ON EMPTY behavior, if specified */
|
|
||||||
JsonBehavior *on_error; /* ON ERROR behavior, if specified */
|
|
||||||
JsonWrapper wrapper; /* array wrapper behavior (JSON_QUERY only) */
|
|
||||||
bool omit_quotes; /* omit or keep quotes? (JSON_QUERY only) */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonFuncExpr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableColumn -
|
|
||||||
* untransformed representation of JSON_TABLE column
|
|
||||||
*/
|
|
||||||
typedef struct JsonTableColumn
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonTableColumnType coltype; /* column type */
|
|
||||||
char *name; /* column name */
|
|
||||||
TypeName *typeName; /* column type name */
|
|
||||||
char *pathspec; /* path specification, if any */
|
|
||||||
char *pathname; /* path name, if any */
|
|
||||||
JsonFormat *format; /* JSON format clause, if specified */
|
|
||||||
JsonWrapper wrapper; /* WRAPPER behavior for formatted columns */
|
|
||||||
bool omit_quotes; /* omit or keep quotes on scalar strings? */
|
|
||||||
List *columns; /* nested columns */
|
|
||||||
JsonBehavior *on_empty; /* ON EMPTY behavior */
|
|
||||||
JsonBehavior *on_error; /* ON ERROR behavior */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonTableColumn;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTablePlanType -
|
|
||||||
* flags for JSON_TABLE plan node types representation
|
|
||||||
*/
|
|
||||||
typedef enum JsonTablePlanType
|
|
||||||
{
|
|
||||||
JSTP_DEFAULT,
|
|
||||||
JSTP_SIMPLE,
|
|
||||||
JSTP_JOINED,
|
|
||||||
} JsonTablePlanType;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTablePlanJoinType -
|
|
||||||
* flags for JSON_TABLE join types representation
|
|
||||||
*/
|
|
||||||
typedef enum JsonTablePlanJoinType
|
|
||||||
{
|
|
||||||
JSTPJ_INNER = 0x01,
|
|
||||||
JSTPJ_OUTER = 0x02,
|
|
||||||
JSTPJ_CROSS = 0x04,
|
|
||||||
JSTPJ_UNION = 0x08,
|
|
||||||
} JsonTablePlanJoinType;
|
|
||||||
|
|
||||||
typedef struct JsonTablePlan JsonTablePlan;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTablePlan -
|
|
||||||
* untransformed representation of JSON_TABLE plan node
|
|
||||||
*/
|
|
||||||
struct JsonTablePlan
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonTablePlanType plan_type; /* plan type */
|
|
||||||
JsonTablePlanJoinType join_type; /* join type (for joined plan only) */
|
|
||||||
JsonTablePlan *plan1; /* first joined plan */
|
|
||||||
JsonTablePlan *plan2; /* second joined plan */
|
|
||||||
char *pathname; /* path name (for simple plan only) */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTable -
|
|
||||||
* untransformed representation of JSON_TABLE
|
|
||||||
*/
|
|
||||||
typedef struct JsonTable
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonCommon *common; /* common JSON path syntax fields */
|
|
||||||
List *columns; /* list of JsonTableColumn */
|
|
||||||
JsonTablePlan *plan; /* join plan, if specified */
|
|
||||||
JsonBehavior *on_error; /* ON ERROR behavior, if specified */
|
|
||||||
Alias *alias; /* table alias in FROM clause */
|
|
||||||
bool lateral; /* does it have LATERAL prefix? */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonTable;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonKeyValue -
|
|
||||||
* untransformed representation of JSON object key-value pair for
|
|
||||||
* JSON_OBJECT() and JSON_OBJECTAGG()
|
|
||||||
*/
|
|
||||||
typedef struct JsonKeyValue
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Expr *key; /* key expression */
|
|
||||||
JsonValueExpr *value; /* JSON value expression */
|
|
||||||
} JsonKeyValue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonParseExpr -
|
|
||||||
* untransformed representation of JSON()
|
|
||||||
*/
|
|
||||||
typedef struct JsonParseExpr
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonValueExpr *expr; /* string expression */
|
|
||||||
JsonOutput *output; /* RETURNING clause, if specified */
|
|
||||||
bool unique_keys; /* WITH UNIQUE KEYS? */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonParseExpr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonScalarExpr -
|
|
||||||
* untransformed representation of JSON_SCALAR()
|
|
||||||
*/
|
|
||||||
typedef struct JsonScalarExpr
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Expr *expr; /* scalar expression */
|
|
||||||
JsonOutput *output; /* RETURNING clause, if specified */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonScalarExpr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonSerializeExpr -
|
|
||||||
* untransformed representation of JSON_SERIALIZE() function
|
|
||||||
*/
|
|
||||||
typedef struct JsonSerializeExpr
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonValueExpr *expr; /* json value expression */
|
|
||||||
JsonOutput *output; /* RETURNING clause, if specified */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonSerializeExpr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonObjectConstructor -
|
|
||||||
* untransformed representation of JSON_OBJECT() constructor
|
|
||||||
*/
|
|
||||||
typedef struct JsonObjectConstructor
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
List *exprs; /* list of JsonKeyValue pairs */
|
|
||||||
JsonOutput *output; /* RETURNING clause, if specified */
|
|
||||||
bool absent_on_null; /* skip NULL values? */
|
|
||||||
bool unique; /* check key uniqueness? */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonObjectConstructor;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonArrayConstructor -
|
|
||||||
* untransformed representation of JSON_ARRAY(element,...) constructor
|
|
||||||
*/
|
|
||||||
typedef struct JsonArrayConstructor
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
List *exprs; /* list of JsonValueExpr elements */
|
|
||||||
JsonOutput *output; /* RETURNING clause, if specified */
|
|
||||||
bool absent_on_null; /* skip NULL elements? */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonArrayConstructor;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonArrayQueryConstructor -
|
|
||||||
* untransformed representation of JSON_ARRAY(subquery) constructor
|
|
||||||
*/
|
|
||||||
typedef struct JsonArrayQueryConstructor
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Node *query; /* subquery */
|
|
||||||
JsonOutput *output; /* RETURNING clause, if specified */
|
|
||||||
JsonFormat *format; /* FORMAT clause for subquery, if specified */
|
|
||||||
bool absent_on_null; /* skip NULL elements? */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonArrayQueryConstructor;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonAggConstructor -
|
|
||||||
* common fields of untransformed representation of
|
|
||||||
* JSON_ARRAYAGG() and JSON_OBJECTAGG()
|
|
||||||
*/
|
|
||||||
typedef struct JsonAggConstructor
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonOutput *output; /* RETURNING clause, if any */
|
|
||||||
Node *agg_filter; /* FILTER clause, if any */
|
|
||||||
List *agg_order; /* ORDER BY clause, if any */
|
|
||||||
struct WindowDef *over; /* OVER clause, if any */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonAggConstructor;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonObjectAgg -
|
|
||||||
* untransformed representation of JSON_OBJECTAGG()
|
|
||||||
*/
|
|
||||||
typedef struct JsonObjectAgg
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonAggConstructor *constructor; /* common fields */
|
|
||||||
JsonKeyValue *arg; /* object key-value pair */
|
|
||||||
bool absent_on_null; /* skip NULL values? */
|
|
||||||
bool unique; /* check key uniqueness? */
|
|
||||||
} JsonObjectAgg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonArrayAgg -
|
|
||||||
* untransformed representation of JSON_ARRRAYAGG()
|
|
||||||
*/
|
|
||||||
typedef struct JsonArrayAgg
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonAggConstructor *constructor; /* common fields */
|
|
||||||
JsonValueExpr *arg; /* array element expression */
|
|
||||||
bool absent_on_null; /* skip NULL elements? */
|
|
||||||
} JsonArrayAgg;
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* Raw Grammar Output Statements
|
* Raw Grammar Output Statements
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@ -89,14 +89,8 @@ typedef struct RangeVar
|
|||||||
int location;
|
int location;
|
||||||
} RangeVar;
|
} RangeVar;
|
||||||
|
|
||||||
typedef enum TableFuncType
|
|
||||||
{
|
|
||||||
TFT_XMLTABLE,
|
|
||||||
TFT_JSON_TABLE
|
|
||||||
} TableFuncType;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TableFunc - node for a table function, such as XMLTABLE or JSON_TABLE.
|
* TableFunc - node for a table function, such as XMLTABLE.
|
||||||
*
|
*
|
||||||
* Entries in the ns_names list are either String nodes containing
|
* Entries in the ns_names list are either String nodes containing
|
||||||
* literal namespace names, or NULL pointers to represent DEFAULT.
|
* literal namespace names, or NULL pointers to represent DEFAULT.
|
||||||
@ -104,7 +98,6 @@ typedef enum TableFuncType
|
|||||||
typedef struct TableFunc
|
typedef struct TableFunc
|
||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
TableFuncType functype; /* XMLTABLE or JSON_TABLE */
|
|
||||||
List *ns_uris; /* list of namespace URI expressions */
|
List *ns_uris; /* list of namespace URI expressions */
|
||||||
List *ns_names; /* list of namespace names or NULL */
|
List *ns_names; /* list of namespace names or NULL */
|
||||||
Node *docexpr; /* input document expression */
|
Node *docexpr; /* input document expression */
|
||||||
@ -115,9 +108,7 @@ typedef struct TableFunc
|
|||||||
List *colcollations; /* OID list of column collation OIDs */
|
List *colcollations; /* OID list of column collation OIDs */
|
||||||
List *colexprs; /* list of column filter expressions */
|
List *colexprs; /* list of column filter expressions */
|
||||||
List *coldefexprs; /* list of column default expressions */
|
List *coldefexprs; /* list of column default expressions */
|
||||||
List *colvalexprs; /* list of column value expressions */
|
|
||||||
Bitmapset *notnulls; /* nullability flag for each output column */
|
Bitmapset *notnulls; /* nullability flag for each output column */
|
||||||
Node *plan; /* JSON_TABLE plan */
|
|
||||||
int ordinalitycol; /* counts from 0; -1 if none specified */
|
int ordinalitycol; /* counts from 0; -1 if none specified */
|
||||||
int location; /* token location, or -1 if unknown */
|
int location; /* token location, or -1 if unknown */
|
||||||
} TableFunc;
|
} TableFunc;
|
||||||
@ -1383,260 +1374,6 @@ typedef struct XmlExpr
|
|||||||
int location; /* token location, or -1 if unknown */
|
int location; /* token location, or -1 if unknown */
|
||||||
} XmlExpr;
|
} XmlExpr;
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonExprOp -
|
|
||||||
* enumeration of JSON functions using JSON path
|
|
||||||
*/
|
|
||||||
typedef enum JsonExprOp
|
|
||||||
{
|
|
||||||
JSON_VALUE_OP, /* JSON_VALUE() */
|
|
||||||
JSON_QUERY_OP, /* JSON_QUERY() */
|
|
||||||
JSON_EXISTS_OP, /* JSON_EXISTS() */
|
|
||||||
JSON_TABLE_OP /* JSON_TABLE() */
|
|
||||||
} JsonExprOp;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonEncoding -
|
|
||||||
* representation of JSON ENCODING clause
|
|
||||||
*/
|
|
||||||
typedef enum JsonEncoding
|
|
||||||
{
|
|
||||||
JS_ENC_DEFAULT, /* unspecified */
|
|
||||||
JS_ENC_UTF8,
|
|
||||||
JS_ENC_UTF16,
|
|
||||||
JS_ENC_UTF32,
|
|
||||||
} JsonEncoding;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonFormatType -
|
|
||||||
* enumeration of JSON formats used in JSON FORMAT clause
|
|
||||||
*/
|
|
||||||
typedef enum JsonFormatType
|
|
||||||
{
|
|
||||||
JS_FORMAT_DEFAULT, /* unspecified */
|
|
||||||
JS_FORMAT_JSON, /* FORMAT JSON [ENCODING ...] */
|
|
||||||
JS_FORMAT_JSONB /* implicit internal format for RETURNING
|
|
||||||
* jsonb */
|
|
||||||
} JsonFormatType;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonBehaviorType -
|
|
||||||
* enumeration of behavior types used in JSON ON ... BEHAVIOR clause
|
|
||||||
*
|
|
||||||
* If enum members are reordered, get_json_behavior() from ruleutils.c
|
|
||||||
* must be updated accordingly.
|
|
||||||
*/
|
|
||||||
typedef enum JsonBehaviorType
|
|
||||||
{
|
|
||||||
JSON_BEHAVIOR_NULL = 0,
|
|
||||||
JSON_BEHAVIOR_ERROR,
|
|
||||||
JSON_BEHAVIOR_EMPTY,
|
|
||||||
JSON_BEHAVIOR_TRUE,
|
|
||||||
JSON_BEHAVIOR_FALSE,
|
|
||||||
JSON_BEHAVIOR_UNKNOWN,
|
|
||||||
JSON_BEHAVIOR_EMPTY_ARRAY,
|
|
||||||
JSON_BEHAVIOR_EMPTY_OBJECT,
|
|
||||||
JSON_BEHAVIOR_DEFAULT
|
|
||||||
} JsonBehaviorType;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonWrapper -
|
|
||||||
* representation of WRAPPER clause for JSON_QUERY()
|
|
||||||
*/
|
|
||||||
typedef enum JsonWrapper
|
|
||||||
{
|
|
||||||
JSW_NONE,
|
|
||||||
JSW_CONDITIONAL,
|
|
||||||
JSW_UNCONDITIONAL,
|
|
||||||
} JsonWrapper;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonFormat -
|
|
||||||
* representation of JSON FORMAT clause
|
|
||||||
*/
|
|
||||||
typedef struct JsonFormat
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonFormatType format_type; /* format type */
|
|
||||||
JsonEncoding encoding; /* JSON encoding */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonFormat;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonReturning -
|
|
||||||
* transformed representation of JSON RETURNING clause
|
|
||||||
*/
|
|
||||||
typedef struct JsonReturning
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonFormat *format; /* output JSON format */
|
|
||||||
Oid typid; /* target type Oid */
|
|
||||||
int32 typmod; /* target type modifier */
|
|
||||||
} JsonReturning;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonValueExpr -
|
|
||||||
* representation of JSON value expression (expr [FORMAT json_format])
|
|
||||||
*/
|
|
||||||
typedef struct JsonValueExpr
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Expr *raw_expr; /* raw expression */
|
|
||||||
Expr *formatted_expr; /* formatted expression or NULL */
|
|
||||||
JsonFormat *format; /* FORMAT clause, if specified */
|
|
||||||
} JsonValueExpr;
|
|
||||||
|
|
||||||
typedef enum JsonConstructorType
|
|
||||||
{
|
|
||||||
JSCTOR_JSON_OBJECT = 1,
|
|
||||||
JSCTOR_JSON_ARRAY = 2,
|
|
||||||
JSCTOR_JSON_OBJECTAGG = 3,
|
|
||||||
JSCTOR_JSON_ARRAYAGG = 4,
|
|
||||||
JSCTOR_JSON_SCALAR = 5,
|
|
||||||
JSCTOR_JSON_SERIALIZE = 6,
|
|
||||||
JSCTOR_JSON_PARSE = 7
|
|
||||||
} JsonConstructorType;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonConstructorExpr -
|
|
||||||
* wrapper over FuncExpr/Aggref/WindowFunc for SQL/JSON constructors
|
|
||||||
*/
|
|
||||||
typedef struct JsonConstructorExpr
|
|
||||||
{
|
|
||||||
Expr xpr;
|
|
||||||
JsonConstructorType type; /* constructor type */
|
|
||||||
List *args;
|
|
||||||
Expr *func; /* underlying json[b]_xxx() function call */
|
|
||||||
Expr *coercion; /* coercion to RETURNING type */
|
|
||||||
JsonReturning *returning; /* RETURNING clause */
|
|
||||||
bool absent_on_null; /* ABSENT ON NULL? */
|
|
||||||
bool unique; /* WITH UNIQUE KEYS? (JSON_OBJECT[AGG] only) */
|
|
||||||
int location;
|
|
||||||
} JsonConstructorExpr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonValueType -
|
|
||||||
* representation of JSON item type in IS JSON predicate
|
|
||||||
*/
|
|
||||||
typedef enum JsonValueType
|
|
||||||
{
|
|
||||||
JS_TYPE_ANY, /* IS JSON [VALUE] */
|
|
||||||
JS_TYPE_OBJECT, /* IS JSON OBJECT */
|
|
||||||
JS_TYPE_ARRAY, /* IS JSON ARRAY */
|
|
||||||
JS_TYPE_SCALAR /* IS JSON SCALAR */
|
|
||||||
} JsonValueType;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonIsPredicate -
|
|
||||||
* representation of IS JSON predicate
|
|
||||||
*/
|
|
||||||
typedef struct JsonIsPredicate
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Node *expr; /* subject expression */
|
|
||||||
JsonFormat *format; /* FORMAT clause, if specified */
|
|
||||||
JsonValueType item_type; /* JSON item type */
|
|
||||||
bool unique_keys; /* check key uniqueness? */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonIsPredicate;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonBehavior -
|
|
||||||
* representation of JSON ON ... BEHAVIOR clause
|
|
||||||
*/
|
|
||||||
typedef struct JsonBehavior
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonBehaviorType btype; /* behavior type */
|
|
||||||
Node *default_expr; /* default expression, if any */
|
|
||||||
} JsonBehavior;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonCoercion -
|
|
||||||
* coercion from SQL/JSON item types to SQL types
|
|
||||||
*/
|
|
||||||
typedef struct JsonCoercion
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Node *expr; /* resulting expression coerced to target type */
|
|
||||||
bool via_populate; /* coerce result using json_populate_type()? */
|
|
||||||
bool via_io; /* coerce result using type input function? */
|
|
||||||
Oid collation; /* collation for coercion via I/O or populate */
|
|
||||||
} JsonCoercion;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonItemCoercions -
|
|
||||||
* expressions for coercion from SQL/JSON item types directly to the
|
|
||||||
* output SQL type
|
|
||||||
*/
|
|
||||||
typedef struct JsonItemCoercions
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
JsonCoercion *null;
|
|
||||||
JsonCoercion *string;
|
|
||||||
JsonCoercion *numeric;
|
|
||||||
JsonCoercion *boolean;
|
|
||||||
JsonCoercion *date;
|
|
||||||
JsonCoercion *time;
|
|
||||||
JsonCoercion *timetz;
|
|
||||||
JsonCoercion *timestamp;
|
|
||||||
JsonCoercion *timestamptz;
|
|
||||||
JsonCoercion *composite; /* arrays and objects */
|
|
||||||
} JsonItemCoercions;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonExpr -
|
|
||||||
* transformed representation of JSON_VALUE(), JSON_QUERY(), JSON_EXISTS()
|
|
||||||
*/
|
|
||||||
typedef struct JsonExpr
|
|
||||||
{
|
|
||||||
Expr xpr;
|
|
||||||
JsonExprOp op; /* json function ID */
|
|
||||||
Node *formatted_expr; /* formatted context item expression */
|
|
||||||
JsonCoercion *result_coercion; /* resulting coercion to RETURNING type */
|
|
||||||
JsonFormat *format; /* context item format (JSON/JSONB) */
|
|
||||||
Node *path_spec; /* JSON path specification expression */
|
|
||||||
List *passing_names; /* PASSING argument names */
|
|
||||||
List *passing_values; /* PASSING argument values */
|
|
||||||
JsonReturning *returning; /* RETURNING clause type/format info */
|
|
||||||
JsonBehavior *on_empty; /* ON EMPTY behavior */
|
|
||||||
JsonBehavior *on_error; /* ON ERROR behavior */
|
|
||||||
JsonItemCoercions *coercions; /* coercions for JSON_VALUE */
|
|
||||||
JsonWrapper wrapper; /* WRAPPER for JSON_QUERY */
|
|
||||||
bool omit_quotes; /* KEEP/OMIT QUOTES for JSON_QUERY */
|
|
||||||
int location; /* token location, or -1 if unknown */
|
|
||||||
} JsonExpr;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableParent -
|
|
||||||
* transformed representation of parent JSON_TABLE plan node
|
|
||||||
*/
|
|
||||||
typedef struct JsonTableParent
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Const *path; /* jsonpath constant */
|
|
||||||
char *name; /* path name */
|
|
||||||
Node *child; /* nested columns, if any */
|
|
||||||
bool outerJoin; /* outer or inner join for nested columns? */
|
|
||||||
int colMin; /* min column index in the resulting column
|
|
||||||
* list */
|
|
||||||
int colMax; /* max column index in the resulting column
|
|
||||||
* list */
|
|
||||||
bool errorOnError; /* ERROR/EMPTY ON ERROR behavior */
|
|
||||||
} JsonTableParent;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JsonTableSibling -
|
|
||||||
* transformed representation of joined sibling JSON_TABLE plan node
|
|
||||||
*/
|
|
||||||
typedef struct JsonTableSibling
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
Node *larg; /* left join node */
|
|
||||||
Node *rarg; /* right join node */
|
|
||||||
bool cross; /* cross or union join? */
|
|
||||||
} JsonTableSibling;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* NullTest
|
* NullTest
|
||||||
*
|
*
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
/* name, value, category, is-bare-label */
|
/* name, value, category, is-bare-label */
|
||||||
PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("absent", ABSENT, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
@ -93,7 +92,6 @@ PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("conditional", CONDITIONAL, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
@ -148,13 +146,11 @@ PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("empty", EMPTY_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("end", END_P, RESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("end", END_P, RESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("error", ERROR_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD, AS_LABEL)
|
PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD, AS_LABEL)
|
||||||
@ -179,7 +175,6 @@ PG_KEYWORD("following", FOLLOWING, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("for", FOR, RESERVED_KEYWORD, AS_LABEL)
|
PG_KEYWORD("for", FOR, RESERVED_KEYWORD, AS_LABEL)
|
||||||
PG_KEYWORD("force", FORCE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("force", FORCE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("foreign", FOREIGN, RESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("foreign", FOREIGN, RESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("format", FORMAT, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("forward", FORWARD, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("forward", FORWARD, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("freeze", FREEZE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("freeze", FREEZE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("from", FROM, RESERVED_KEYWORD, AS_LABEL)
|
PG_KEYWORD("from", FROM, RESERVED_KEYWORD, AS_LABEL)
|
||||||
@ -232,20 +227,7 @@ PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
|
PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
|
||||||
PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("json", JSON, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_array", JSON_ARRAY, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_arrayagg", JSON_ARRAYAGG, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_exists", JSON_EXISTS, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_object", JSON_OBJECT, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_objectagg", JSON_OBJECTAGG, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_query", JSON_QUERY, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_scalar", JSON_SCALAR, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_serialize", JSON_SERIALIZE, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_table", JSON_TABLE, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("json_value", JSON_VALUE, COL_NAME_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("keep", KEEP, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("keys", KEYS, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
@ -284,7 +266,6 @@ PG_KEYWORD("names", NAMES, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("national", NATIONAL, COL_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("national", NATIONAL, COL_NAME_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("natural", NATURAL, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("natural", NATURAL, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("nchar", NCHAR, COL_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("nchar", NCHAR, COL_NAME_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("nested", NESTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("new", NEW, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("new", NEW, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("next", NEXT, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("next", NEXT, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("nfc", NFC, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("nfc", NFC, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
@ -310,7 +291,6 @@ PG_KEYWORD("off", OFF, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("offset", OFFSET, RESERVED_KEYWORD, AS_LABEL)
|
PG_KEYWORD("offset", OFFSET, RESERVED_KEYWORD, AS_LABEL)
|
||||||
PG_KEYWORD("oids", OIDS, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("oids", OIDS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("old", OLD, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("old", OLD, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("omit", OMIT, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("on", ON, RESERVED_KEYWORD, AS_LABEL)
|
PG_KEYWORD("on", ON, RESERVED_KEYWORD, AS_LABEL)
|
||||||
PG_KEYWORD("only", ONLY, RESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("only", ONLY, RESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("operator", OPERATOR, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("operator", OPERATOR, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
@ -335,9 +315,7 @@ PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("path", PATH, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("plan", PLAN, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("policy", POLICY, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("policy", POLICY, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("position", POSITION, COL_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("position", POSITION, COL_NAME_KEYWORD, BARE_LABEL)
|
||||||
@ -355,7 +333,6 @@ PG_KEYWORD("procedures", PROCEDURES, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("program", PROGRAM, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("program", PROGRAM, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("publication", PUBLICATION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("publication", PUBLICATION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("quotes", QUOTES, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("range", RANGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("range", RANGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("read", READ, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("read", READ, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("real", REAL, COL_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("real", REAL, COL_NAME_KEYWORD, BARE_LABEL)
|
||||||
@ -390,7 +367,6 @@ PG_KEYWORD("row", ROW, COL_NAME_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("scalar", SCALAR, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
@ -426,7 +402,6 @@ PG_KEYWORD("stdout", STDOUT, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("stored", STORED, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("stored", STORED, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("string", STRING, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD, BARE_LABEL)
|
||||||
@ -461,7 +436,6 @@ PG_KEYWORD("types", TYPES_P, UNRESERVED_KEYWORD, BARE_LABEL)
|
|||||||
PG_KEYWORD("uescape", UESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("uescape", UESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("unbounded", UNBOUNDED, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("unbounded", UNBOUNDED, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("uncommitted", UNCOMMITTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("uncommitted", UNCOMMITTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("unconditional", UNCONDITIONAL, UNRESERVED_KEYWORD, BARE_LABEL)
|
|
||||||
PG_KEYWORD("unencrypted", UNENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("unencrypted", UNENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
|
||||||
PG_KEYWORD("union", UNION, RESERVED_KEYWORD, AS_LABEL)
|
PG_KEYWORD("union", UNION, RESERVED_KEYWORD, AS_LABEL)
|
||||||
PG_KEYWORD("unique", UNIQUE, RESERVED_KEYWORD, BARE_LABEL)
|
PG_KEYWORD("unique", UNIQUE, RESERVED_KEYWORD, BARE_LABEL)
|
||||||
|
@ -51,7 +51,4 @@ extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
|
|||||||
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
|
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
|
||||||
extern bool targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList);
|
extern bool targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList);
|
||||||
|
|
||||||
/* functions in parse_jsontable.c */
|
|
||||||
extern ParseNamespaceItem *transformJsonTable(ParseState *pstate, JsonTable *jt);
|
|
||||||
|
|
||||||
#endif /* PARSE_CLAUSE_H */
|
#endif /* PARSE_CLAUSE_H */
|
||||||
|
@ -17,9 +17,6 @@
|
|||||||
#ifndef _FORMATTING_H_
|
#ifndef _FORMATTING_H_
|
||||||
#define _FORMATTING_H_
|
#define _FORMATTING_H_
|
||||||
|
|
||||||
#define DCH_DATED 0x01
|
|
||||||
#define DCH_TIMED 0x02
|
|
||||||
#define DCH_ZONED 0x04
|
|
||||||
|
|
||||||
extern char *str_tolower(const char *buff, size_t nbytes, Oid collid);
|
extern char *str_tolower(const char *buff, size_t nbytes, Oid collid);
|
||||||
extern char *str_toupper(const char *buff, size_t nbytes, Oid collid);
|
extern char *str_toupper(const char *buff, size_t nbytes, Oid collid);
|
||||||
@ -32,6 +29,5 @@ extern char *asc_initcap(const char *buff, size_t nbytes);
|
|||||||
extern Datum parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
|
extern Datum parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict,
|
||||||
Oid *typid, int32 *typmod, int *tz,
|
Oid *typid, int32 *typmod, int *tz,
|
||||||
bool *have_error);
|
bool *have_error);
|
||||||
extern int datetime_format_flags(const char *fmt_str, bool *have_error);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -16,35 +16,9 @@
|
|||||||
|
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
|
|
||||||
typedef enum /* type categories for datum_to_json */
|
|
||||||
{
|
|
||||||
JSONTYPE_NULL, /* null, so we didn't bother to identify */
|
|
||||||
JSONTYPE_BOOL, /* boolean (built-in types only) */
|
|
||||||
JSONTYPE_NUMERIC, /* numeric (ditto) */
|
|
||||||
JSONTYPE_DATE, /* we use special formatting for datetimes */
|
|
||||||
JSONTYPE_TIMESTAMP,
|
|
||||||
JSONTYPE_TIMESTAMPTZ,
|
|
||||||
JSONTYPE_JSON, /* JSON itself (and JSONB) */
|
|
||||||
JSONTYPE_ARRAY, /* array */
|
|
||||||
JSONTYPE_COMPOSITE, /* composite */
|
|
||||||
JSONTYPE_CAST, /* something with an explicit cast to JSON */
|
|
||||||
JSONTYPE_OTHER /* all else */
|
|
||||||
} JsonTypeCategory;
|
|
||||||
|
|
||||||
/* functions in json.c */
|
/* functions in json.c */
|
||||||
extern void escape_json(StringInfo buf, const char *str);
|
extern void escape_json(StringInfo buf, const char *str);
|
||||||
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
|
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
|
||||||
const int *tzp);
|
const int *tzp);
|
||||||
extern bool to_json_is_immutable(Oid typoid);
|
|
||||||
extern void json_categorize_type(Oid typoid, JsonTypeCategory *tcategory,
|
|
||||||
Oid *outfuncoid);
|
|
||||||
extern Datum to_json_worker(Datum val, JsonTypeCategory tcategory,
|
|
||||||
Oid outfuncoid);
|
|
||||||
extern Datum json_build_object_worker(int nargs, Datum *args, bool *nulls,
|
|
||||||
Oid *types, bool absent_on_null,
|
|
||||||
bool unique_keys);
|
|
||||||
extern Datum json_build_array_worker(int nargs, Datum *args, bool *nulls,
|
|
||||||
Oid *types, bool absent_on_null);
|
|
||||||
extern bool json_validate(text *json, bool check_unique_keys, bool throw_error);
|
|
||||||
|
|
||||||
#endif /* JSON_H */
|
#endif /* JSON_H */
|
||||||
|
@ -329,8 +329,6 @@ typedef struct JsonbParseState
|
|||||||
JsonbValue contVal;
|
JsonbValue contVal;
|
||||||
Size size;
|
Size size;
|
||||||
struct JsonbParseState *next;
|
struct JsonbParseState *next;
|
||||||
bool unique_keys; /* Check object key uniqueness */
|
|
||||||
bool skip_nulls; /* Skip null object fields */
|
|
||||||
} JsonbParseState;
|
} JsonbParseState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -376,22 +374,6 @@ typedef struct JsonbIterator
|
|||||||
struct JsonbIterator *parent;
|
struct JsonbIterator *parent;
|
||||||
} JsonbIterator;
|
} JsonbIterator;
|
||||||
|
|
||||||
/* unlike with json categories, we need to treat json and jsonb differently */
|
|
||||||
typedef enum /* type categories for datum_to_jsonb */
|
|
||||||
{
|
|
||||||
JSONBTYPE_NULL, /* null, so we didn't bother to identify */
|
|
||||||
JSONBTYPE_BOOL, /* boolean (built-in types only) */
|
|
||||||
JSONBTYPE_NUMERIC, /* numeric (ditto) */
|
|
||||||
JSONBTYPE_DATE, /* we use special formatting for datetimes */
|
|
||||||
JSONBTYPE_TIMESTAMP, /* we use special formatting for timestamp */
|
|
||||||
JSONBTYPE_TIMESTAMPTZ, /* ... and timestamptz */
|
|
||||||
JSONBTYPE_JSON, /* JSON */
|
|
||||||
JSONBTYPE_JSONB, /* JSONB */
|
|
||||||
JSONBTYPE_ARRAY, /* array */
|
|
||||||
JSONBTYPE_COMPOSITE, /* composite */
|
|
||||||
JSONBTYPE_JSONCAST, /* something with an explicit cast to JSON */
|
|
||||||
JSONBTYPE_OTHER /* all else */
|
|
||||||
} JsonbTypeCategory;
|
|
||||||
|
|
||||||
/* Support functions */
|
/* Support functions */
|
||||||
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
|
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
|
||||||
@ -419,14 +401,10 @@ extern void JsonbHashScalarValueExtended(const JsonbValue *scalarVal,
|
|||||||
uint64 *hash, uint64 seed);
|
uint64 *hash, uint64 seed);
|
||||||
|
|
||||||
/* jsonb.c support functions */
|
/* jsonb.c support functions */
|
||||||
extern Datum jsonb_from_text(text *js, bool unique_keys);
|
|
||||||
extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
|
extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
|
||||||
int estimated_len);
|
int estimated_len);
|
||||||
extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
|
extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
|
||||||
int estimated_len);
|
int estimated_len);
|
||||||
extern Jsonb *JsonbMakeEmptyArray(void);
|
|
||||||
extern Jsonb *JsonbMakeEmptyObject(void);
|
|
||||||
extern char *JsonbUnquote(Jsonb *jb);
|
|
||||||
extern bool JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res);
|
extern bool JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res);
|
||||||
extern const char *JsonbTypeName(JsonbValue *jb);
|
extern const char *JsonbTypeName(JsonbValue *jb);
|
||||||
|
|
||||||
@ -434,15 +412,4 @@ extern Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len,
|
|||||||
JsonbValue *newval);
|
JsonbValue *newval);
|
||||||
extern Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
|
extern Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
|
||||||
bool *isnull, bool as_text);
|
bool *isnull, bool as_text);
|
||||||
extern bool to_jsonb_is_immutable(Oid typoid);
|
|
||||||
extern void jsonb_categorize_type(Oid typoid, JsonbTypeCategory *tcategory,
|
|
||||||
Oid *outfuncoid);
|
|
||||||
extern Datum to_jsonb_worker(Datum val, JsonbTypeCategory tcategory,
|
|
||||||
Oid outfuncoid);
|
|
||||||
extern Datum jsonb_build_object_worker(int nargs, Datum *args, bool *nulls,
|
|
||||||
Oid *types, bool absent_on_null,
|
|
||||||
bool unique_keys);
|
|
||||||
extern Datum jsonb_build_array_worker(int nargs, Datum *args, bool *nulls,
|
|
||||||
Oid *types, bool absent_on_null);
|
|
||||||
|
|
||||||
#endif /* __JSONB_H__ */
|
#endif /* __JSONB_H__ */
|
||||||
|
@ -45,9 +45,6 @@ extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
|
|||||||
/* report an error during json lexing or parsing */
|
/* report an error during json lexing or parsing */
|
||||||
extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
|
extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
|
||||||
|
|
||||||
/* get first JSON token */
|
|
||||||
extern JsonTokenType json_get_first_token(text *json, bool throw_error);
|
|
||||||
|
|
||||||
extern uint32 parse_jsonb_index_flags(Jsonb *jb);
|
extern uint32 parse_jsonb_index_flags(Jsonb *jb);
|
||||||
extern void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
|
extern void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
|
||||||
JsonIterateStringValuesAction action);
|
JsonIterateStringValuesAction action);
|
||||||
@ -58,8 +55,4 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
|
|||||||
extern text *transform_json_string_values(text *json, void *action_state,
|
extern text *transform_json_string_values(text *json, void *action_state,
|
||||||
JsonTransformStringValuesAction transform_action);
|
JsonTransformStringValuesAction transform_action);
|
||||||
|
|
||||||
extern Datum json_populate_type(Datum json_val, Oid json_type,
|
|
||||||
Oid typid, int32 typmod,
|
|
||||||
void **cache, MemoryContext mcxt, bool *isnull);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -15,11 +15,8 @@
|
|||||||
#define JSONPATH_H
|
#define JSONPATH_H
|
||||||
|
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "executor/tablefunc.h"
|
|
||||||
#include "nodes/pg_list.h"
|
#include "nodes/pg_list.h"
|
||||||
#include "nodes/primnodes.h"
|
|
||||||
#include "utils/jsonb.h"
|
#include "utils/jsonb.h"
|
||||||
#include "utils/jsonfuncs.h"
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -177,7 +174,6 @@ extern bool jspGetBool(JsonPathItem *v);
|
|||||||
extern char *jspGetString(JsonPathItem *v, int32 *len);
|
extern char *jspGetString(JsonPathItem *v, int32 *len);
|
||||||
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
|
extern bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from,
|
||||||
JsonPathItem *to, int i);
|
JsonPathItem *to, int i);
|
||||||
extern bool jspIsMutable(JsonPath *path, List *varnames, List *varexprs);
|
|
||||||
|
|
||||||
extern const char *jspOperationName(JsonPathItemType type);
|
extern const char *jspOperationName(JsonPathItemType type);
|
||||||
|
|
||||||
@ -252,37 +248,4 @@ extern JsonPathParseResult *parsejsonpath(const char *str, int len);
|
|||||||
|
|
||||||
extern int jspConvertRegexFlags(uint32 xflags);
|
extern int jspConvertRegexFlags(uint32 xflags);
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluation of jsonpath
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* External variable passed into jsonpath. */
|
|
||||||
typedef struct JsonPathVariableEvalContext
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
Oid typid;
|
|
||||||
int32 typmod;
|
|
||||||
struct ExprContext *econtext;
|
|
||||||
struct ExprState *estate;
|
|
||||||
MemoryContext mcxt; /* memory context for cached value */
|
|
||||||
Datum value;
|
|
||||||
bool isnull;
|
|
||||||
bool evaluated;
|
|
||||||
} JsonPathVariableEvalContext;
|
|
||||||
|
|
||||||
/* SQL/JSON item */
|
|
||||||
extern void JsonItemFromDatum(Datum val, Oid typid, int32 typmod,
|
|
||||||
JsonbValue *res);
|
|
||||||
|
|
||||||
extern bool JsonPathExists(Datum jb, JsonPath *path, List *vars, bool *error);
|
|
||||||
extern Datum JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
|
|
||||||
bool *empty, bool *error, List *vars);
|
|
||||||
extern JsonbValue *JsonPathValue(Datum jb, JsonPath *jp, bool *empty,
|
|
||||||
bool *error, List *vars);
|
|
||||||
|
|
||||||
extern int EvalJsonPathVar(void *vars, char *varName, int varNameLen,
|
|
||||||
JsonbValue *val, JsonbValue *baseObject);
|
|
||||||
|
|
||||||
extern PGDLLIMPORT const TableFuncRoutine JsonbTableRoutine;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -657,34 +657,6 @@ var_type: simple_type
|
|||||||
$$.type_index = mm_strdup("-1");
|
$$.type_index = mm_strdup("-1");
|
||||||
$$.type_sizeof = NULL;
|
$$.type_sizeof = NULL;
|
||||||
}
|
}
|
||||||
| STRING
|
|
||||||
{
|
|
||||||
if (INFORMIX_MODE)
|
|
||||||
{
|
|
||||||
/* In Informix mode, "string" is automatically a typedef */
|
|
||||||
$$.type_enum = ECPGt_string;
|
|
||||||
$$.type_str = mm_strdup("char");
|
|
||||||
$$.type_dimension = mm_strdup("-1");
|
|
||||||
$$.type_index = mm_strdup("-1");
|
|
||||||
$$.type_sizeof = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Otherwise, legal only if user typedef'ed it */
|
|
||||||
struct typedefs *this = get_typedef("string", false);
|
|
||||||
|
|
||||||
$$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
|
|
||||||
$$.type_enum = this->type->type_enum;
|
|
||||||
$$.type_dimension = this->type->type_dimension;
|
|
||||||
$$.type_index = this->type->type_index;
|
|
||||||
if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
|
|
||||||
$$.type_sizeof = this->type->type_sizeof;
|
|
||||||
else
|
|
||||||
$$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
|
|
||||||
|
|
||||||
struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
| IDENT ecpg_interval
|
| IDENT ecpg_interval
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -58,8 +58,6 @@ my %replace_string = (
|
|||||||
'NOT_LA' => 'not',
|
'NOT_LA' => 'not',
|
||||||
'NULLS_LA' => 'nulls',
|
'NULLS_LA' => 'nulls',
|
||||||
'WITH_LA' => 'with',
|
'WITH_LA' => 'with',
|
||||||
'WITH_LA_UNIQUE' => 'with',
|
|
||||||
'WITHOUT_LA' => 'without',
|
|
||||||
'TYPECAST' => '::',
|
'TYPECAST' => '::',
|
||||||
'DOT_DOT' => '..',
|
'DOT_DOT' => '..',
|
||||||
'COLON_EQUALS' => ':=',
|
'COLON_EQUALS' => ':=',
|
||||||
|
@ -83,7 +83,6 @@ filtered_base_yylex(void)
|
|||||||
case WITH:
|
case WITH:
|
||||||
case UIDENT:
|
case UIDENT:
|
||||||
case USCONST:
|
case USCONST:
|
||||||
case WITHOUT:
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return cur_token;
|
return cur_token;
|
||||||
@ -144,19 +143,6 @@ filtered_base_yylex(void)
|
|||||||
case ORDINALITY:
|
case ORDINALITY:
|
||||||
cur_token = WITH_LA;
|
cur_token = WITH_LA;
|
||||||
break;
|
break;
|
||||||
case UNIQUE:
|
|
||||||
cur_token = WITH_LA_UNIQUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WITHOUT:
|
|
||||||
/* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
|
|
||||||
switch (next_token)
|
|
||||||
{
|
|
||||||
case TIME:
|
|
||||||
cur_token = WITHOUT_LA;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIDENT:
|
case UIDENT:
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
-- JSON_EXISTS
|
|
||||||
SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
|
|
||||||
ERROR: JSON_EXISTS() is not yet implemented for the json type
|
|
||||||
LINE 1: SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
|
|
||||||
^
|
|
||||||
HINT: Try casting the argument to jsonb
|
|
||||||
-- JSON_VALUE
|
|
||||||
SELECT JSON_VALUE(NULL FORMAT JSON, '$');
|
|
||||||
ERROR: JSON_VALUE() is not yet implemented for the json type
|
|
||||||
LINE 1: SELECT JSON_VALUE(NULL FORMAT JSON, '$');
|
|
||||||
^
|
|
||||||
HINT: Try casting the argument to jsonb
|
|
||||||
-- JSON_QUERY
|
|
||||||
SELECT JSON_QUERY(NULL FORMAT JSON, '$');
|
|
||||||
ERROR: JSON_QUERY() is not yet implemented for the json type
|
|
||||||
LINE 1: SELECT JSON_QUERY(NULL FORMAT JSON, '$');
|
|
||||||
^
|
|
||||||
HINT: Try casting the argument to jsonb
|
|
||||||
-- JSON_TABLE
|
|
||||||
SELECT * FROM JSON_TABLE(NULL FORMAT JSON, '$' COLUMNS (foo text));
|
|
||||||
ERROR: JSON_TABLE() is not yet implemented for the json type
|
|
||||||
LINE 1: SELECT * FROM JSON_TABLE(NULL FORMAT JSON, '$' COLUMNS (foo ...
|
|
||||||
^
|
|
||||||
HINT: Try casting the argument to jsonb
|
|
File diff suppressed because it is too large
Load Diff
@ -1474,10 +1474,8 @@ WHERE a.aggfnoid = p.oid AND
|
|||||||
NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
|
NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
|
||||||
OR (p.pronargs > 2 AND
|
OR (p.pronargs > 2 AND
|
||||||
NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
|
NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
|
||||||
OR (p.pronargs > 3 AND
|
-- we could carry the check further, but 3 args is enough for now
|
||||||
NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4]))
|
OR (p.pronargs > 3)
|
||||||
-- we could carry the check further, but 4 args is enough for now
|
|
||||||
OR (p.pronargs > 4)
|
|
||||||
);
|
);
|
||||||
aggfnoid | proname | oid | proname
|
aggfnoid | proname | oid | proname
|
||||||
----------+---------+-----+---------
|
----------+---------+-----+---------
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -111,7 +111,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
|
|||||||
# ----------
|
# ----------
|
||||||
# Another group of parallel tests (JSON related)
|
# Another group of parallel tests (JSON related)
|
||||||
# ----------
|
# ----------
|
||||||
test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath sqljson json_sqljson jsonb_sqljson
|
test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath
|
||||||
|
|
||||||
# ----------
|
# ----------
|
||||||
# Another group of parallel tests
|
# Another group of parallel tests
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
-- JSON_EXISTS
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
|
|
||||||
|
|
||||||
-- JSON_VALUE
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(NULL FORMAT JSON, '$');
|
|
||||||
|
|
||||||
-- JSON_QUERY
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(NULL FORMAT JSON, '$');
|
|
||||||
|
|
||||||
-- JSON_TABLE
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(NULL FORMAT JSON, '$' COLUMNS (foo text));
|
|
@ -1,977 +0,0 @@
|
|||||||
-- JSON_EXISTS
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(NULL::jsonb, '$');
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(jsonb '[]', '$');
|
|
||||||
SELECT JSON_EXISTS(JSON_OBJECT(RETURNING jsonb), '$');
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$');
|
|
||||||
SELECT JSON_EXISTS(jsonb 'null', '$');
|
|
||||||
SELECT JSON_EXISTS(jsonb '[]', '$');
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', 'strict $.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', 'strict $.a' ERROR ON ERROR);
|
|
||||||
SELECT JSON_EXISTS(jsonb 'null', '$.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '[]', '$.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '[1, "aaa", {"a": 1}]', 'strict $.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '[1, "aaa", {"a": 1}]', 'lax $.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '{}', '$.a');
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"b": 1, "a": 2}', '$.a');
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$.a.b');
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": {"b": 1}}', '$.a.b');
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.a.b');
|
|
||||||
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING 1 AS x);
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING '1' AS x);
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 2 AS y);
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 1 AS y);
|
|
||||||
|
|
||||||
-- extension: boolean expressions
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$ > 2');
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
|
|
||||||
|
|
||||||
-- extension: RETURNING clause
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING bool);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING bool);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING int);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING int);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING text);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[1]' RETURNING text);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', 'strict $[1]' RETURNING text FALSE ON ERROR);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING jsonb);
|
|
||||||
SELECT JSON_EXISTS(jsonb '1', '$[0]' RETURNING float4);
|
|
||||||
|
|
||||||
|
|
||||||
-- JSON_VALUE
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(NULL::jsonb, '$');
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$' RETURNING int);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb 'true', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb 'true', '$' RETURNING bool);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '123', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb '123', '$' RETURNING int) + 234;
|
|
||||||
SELECT JSON_VALUE(jsonb '123', '$' RETURNING text);
|
|
||||||
/* jsonb bytea ??? */
|
|
||||||
SELECT JSON_VALUE(jsonb '123', '$' RETURNING bytea ERROR ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '1.23', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
|
|
||||||
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
|
|
||||||
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING int ERROR ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING text);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING char(5));
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING char(2));
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING json);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING jsonb);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING json ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING jsonb ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '"\"aaa\""', '$' RETURNING json);
|
|
||||||
SELECT JSON_VALUE(jsonb '"\"aaa\""', '$' RETURNING jsonb);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int DEFAULT 111 ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '"123"', '$' RETURNING int) + 234;
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '"2017-02-20"', '$' RETURNING date) + 9;
|
|
||||||
|
|
||||||
-- Test NULL checks execution in domain types
|
|
||||||
CREATE DOMAIN sqljsonb_int_not_null AS int NOT NULL;
|
|
||||||
SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null NULL ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null DEFAULT NULL ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '[]', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb '[]', '$' ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '{}', '$');
|
|
||||||
SELECT JSON_VALUE(jsonb '{}', '$' ERROR ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '1', '$.a');
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'strict $.a' ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 'error' ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 2 ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT 2 ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' NULL ON EMPTY DEFAULT '2' ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON EMPTY DEFAULT '3' ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY DEFAULT '3' ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' DEFAULT '0' ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int ERROR ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
|
|
||||||
SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
x,
|
|
||||||
JSON_VALUE(
|
|
||||||
jsonb '{"a": 1, "b": 2}',
|
|
||||||
'$.* ? (@ > $x)' PASSING x AS x
|
|
||||||
RETURNING int
|
|
||||||
DEFAULT -1 ON EMPTY
|
|
||||||
DEFAULT -2 ON ERROR
|
|
||||||
) y
|
|
||||||
FROM
|
|
||||||
generate_series(0, 2) x;
|
|
||||||
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$a' PASSING point ' (1, 2 )' AS a);
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$a' PASSING point ' (1, 2 )' AS a RETURNING point);
|
|
||||||
|
|
||||||
-- Test timestamptz passing and output
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamptz);
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamp);
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
|
|
||||||
SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
|
|
||||||
|
|
||||||
-- JSON_QUERY
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
JSON_QUERY(js, '$'),
|
|
||||||
JSON_QUERY(js, '$' WITHOUT WRAPPER),
|
|
||||||
JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
|
|
||||||
JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
|
|
||||||
JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
|
|
||||||
FROM
|
|
||||||
(VALUES
|
|
||||||
(jsonb 'null'),
|
|
||||||
('12.3'),
|
|
||||||
('true'),
|
|
||||||
('"aaa"'),
|
|
||||||
('[1, null, "2"]'),
|
|
||||||
('{"a": 1, "b": [2]}')
|
|
||||||
) foo(js);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
JSON_QUERY(js, 'strict $[*]') AS "unspec",
|
|
||||||
JSON_QUERY(js, 'strict $[*]' WITHOUT WRAPPER) AS "without",
|
|
||||||
JSON_QUERY(js, 'strict $[*]' WITH CONDITIONAL WRAPPER) AS "with cond",
|
|
||||||
JSON_QUERY(js, 'strict $[*]' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
|
|
||||||
JSON_QUERY(js, 'strict $[*]' WITH ARRAY WRAPPER) AS "with"
|
|
||||||
FROM
|
|
||||||
(VALUES
|
|
||||||
(jsonb '1'),
|
|
||||||
('[]'),
|
|
||||||
('[null]'),
|
|
||||||
('[12.3]'),
|
|
||||||
('[true]'),
|
|
||||||
('["aaa"]'),
|
|
||||||
('[[1, 2, 3]]'),
|
|
||||||
('[{"a": 1, "b": [2]}]'),
|
|
||||||
('[1, "2", null, [3]]')
|
|
||||||
) foo(js);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text KEEP QUOTES);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text KEEP QUOTES ON SCALAR STRING);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text OMIT QUOTES);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text OMIT QUOTES ON SCALAR STRING);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' OMIT QUOTES ERROR ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING json OMIT QUOTES ERROR ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING bytea FORMAT JSON OMIT QUOTES ERROR ON ERROR);
|
|
||||||
|
|
||||||
-- QUOTES behavior should not be specified when WITH WRAPPER used:
|
|
||||||
-- Should fail
|
|
||||||
SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER OMIT QUOTES);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER KEEP QUOTES);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTES);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTES);
|
|
||||||
-- Should succeed
|
|
||||||
SELECT JSON_QUERY(jsonb '[1]', '$' WITHOUT WRAPPER OMIT QUOTES);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1]', '$' WITHOUT WRAPPER KEEP QUOTES);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]');
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' NULL ON EMPTY);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY ON EMPTY);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY ARRAY ON EMPTY);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY OBJECT ON EMPTY);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' DEFAULT '"empty"' ON EMPTY);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY NULL ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY ARRAY ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY OBJECT ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY ERROR ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' DEFAULT '"empty"' ON ERROR);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING json);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING json FORMAT JSON);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING jsonb);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING jsonb FORMAT JSON);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING text);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING char(10));
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING char(3));
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING text FORMAT JSON);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea FORMAT JSON);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea EMPTY OBJECT ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea FORMAT JSON EMPTY OBJECT ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING json EMPTY OBJECT ON ERROR);
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING jsonb EMPTY OBJECT ON ERROR);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
x, y,
|
|
||||||
JSON_QUERY(
|
|
||||||
jsonb '[1,2,3,4,5,null]',
|
|
||||||
'$[*] ? (@ >= $x && @ <= $y)'
|
|
||||||
PASSING x AS x, y AS y
|
|
||||||
WITH CONDITIONAL WRAPPER
|
|
||||||
EMPTY ARRAY ON EMPTY
|
|
||||||
) list
|
|
||||||
FROM
|
|
||||||
generate_series(0, 4) x,
|
|
||||||
generate_series(0, 4) y;
|
|
||||||
|
|
||||||
-- Extension: record types returning
|
|
||||||
CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
|
|
||||||
CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
|
|
||||||
|
|
||||||
SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}}, {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
|
|
||||||
SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa": [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
|
|
||||||
SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
|
|
||||||
|
|
||||||
-- Extension: array types returning
|
|
||||||
SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
|
|
||||||
SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
|
|
||||||
|
|
||||||
-- Extension: domain types returning
|
|
||||||
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
|
|
||||||
SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
|
|
||||||
|
|
||||||
-- Test timestamptz passing and output
|
|
||||||
SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
|
|
||||||
SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
|
|
||||||
SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
|
|
||||||
|
|
||||||
-- Test constraints
|
|
||||||
|
|
||||||
CREATE TABLE test_jsonb_constraints (
|
|
||||||
js text,
|
|
||||||
i int,
|
|
||||||
x jsonb DEFAULT JSON_QUERY(jsonb '[1,2]', '$[*]' WITH WRAPPER)
|
|
||||||
CONSTRAINT test_jsonb_constraint1
|
|
||||||
CHECK (js IS JSON)
|
|
||||||
CONSTRAINT test_jsonb_constraint2
|
|
||||||
CHECK (JSON_EXISTS(js::jsonb, '$.a' PASSING i + 5 AS int, i::text AS txt, array[1,2,3] as arr))
|
|
||||||
CONSTRAINT test_jsonb_constraint3
|
|
||||||
CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT ('12' || i)::int ON EMPTY ERROR ON ERROR) > i)
|
|
||||||
CONSTRAINT test_jsonb_constraint4
|
|
||||||
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
|
|
||||||
CONSTRAINT test_jsonb_constraint5
|
|
||||||
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
|
|
||||||
CONSTRAINT test_jsonb_constraint6
|
|
||||||
CHECK (JSON_EXISTS(js::jsonb, 'strict $.a' RETURNING int TRUE ON ERROR) < 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
\d test_jsonb_constraints
|
|
||||||
|
|
||||||
SELECT check_clause
|
|
||||||
FROM information_schema.check_constraints
|
|
||||||
WHERE constraint_name LIKE 'test_jsonb_constraint%'
|
|
||||||
ORDER BY 1;
|
|
||||||
|
|
||||||
SELECT pg_get_expr(adbin, adrelid)
|
|
||||||
FROM pg_attrdef
|
|
||||||
WHERE adrelid = 'test_jsonb_constraints'::regclass
|
|
||||||
ORDER BY 1;
|
|
||||||
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('', 1);
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('1', 1);
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('[]');
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1);
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
|
|
||||||
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
|
|
||||||
|
|
||||||
DROP TABLE test_jsonb_constraints;
|
|
||||||
|
|
||||||
-- Test mutabilily od query functions
|
|
||||||
CREATE TABLE test_jsonb_mutability(js jsonb);
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a[0]'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime()'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a ? (@ < $.datetime())'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a ? (@.datetime() < $.datetime())'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a ? (@.datetime() < $.datetime("HH:MI TZH"))'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a ? (@.datetime("HH:MI TZH") < $.datetime("HH:MI TZH"))'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a ? (@.datetime("HH:MI") < $.datetime("YY-MM-DD HH:MI"))'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.a ? (@.datetime("HH:MI TZH") < $.datetime("YY-MM-DD HH:MI"))'));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime("HH:MI TZH") < $x' PASSING '12:34'::timetz AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime("HH:MI TZH") < $y' PASSING '12:34'::timetz AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime() < $x' PASSING '12:34'::timetz AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime() < $x' PASSING '1234'::int AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime() ? (@ == $x)' PASSING '12:34'::time AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$.datetime("YY-MM-DD") ? (@ == $x)' PASSING '2020-07-14'::date AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$[1, $.a ? (@.datetime() == $x)]' PASSING '12:34'::time AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$[1, 0 to $.a ? (@.datetime() == $x)]' PASSING '12:34'::time AS x));
|
|
||||||
CREATE INDEX ON test_jsonb_mutability (JSON_QUERY(js, '$[1, $.a ? (@.datetime("HH:MI") == $x)]' PASSING '12:34'::time AS x));
|
|
||||||
DROP TABLE test_jsonb_mutability;
|
|
||||||
|
|
||||||
-- JSON_TABLE
|
|
||||||
|
|
||||||
-- Should fail (JSON_TABLE can be used only in FROM clause)
|
|
||||||
SELECT JSON_TABLE('[]', '$');
|
|
||||||
|
|
||||||
-- Should fail (no columns)
|
|
||||||
SELECT * FROM JSON_TABLE(NULL, '$' COLUMNS ());
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE (NULL::jsonb, '$' COLUMNS (v1 timestamp)) AS f (v1, v2);
|
|
||||||
|
|
||||||
-- NULL => empty table
|
|
||||||
SELECT * FROM JSON_TABLE(NULL::jsonb, '$' COLUMNS (foo int)) bar;
|
|
||||||
|
|
||||||
--
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '123', '$'
|
|
||||||
COLUMNS (item int PATH '$', foo int)) bar;
|
|
||||||
|
|
||||||
-- JSON_TABLE: basic functionality
|
|
||||||
CREATE DOMAIN jsonb_test_domain AS text CHECK (value <> 'foo');
|
|
||||||
|
|
||||||
SELECT *
|
|
||||||
FROM
|
|
||||||
(VALUES
|
|
||||||
('1'),
|
|
||||||
('[]'),
|
|
||||||
('{}'),
|
|
||||||
('[1, 1.23, "2", "aaaaaaa", "foo", null, false, true, {"aaa": 123}, "[1,2]", "\"str\""]')
|
|
||||||
) vals(js)
|
|
||||||
LEFT OUTER JOIN
|
|
||||||
-- JSON_TABLE is implicitly lateral
|
|
||||||
JSON_TABLE(
|
|
||||||
vals.js::jsonb, 'lax $[*]'
|
|
||||||
COLUMNS (
|
|
||||||
id FOR ORDINALITY,
|
|
||||||
id2 FOR ORDINALITY, -- allowed additional ordinality columns
|
|
||||||
"int" int PATH '$',
|
|
||||||
"text" text PATH '$',
|
|
||||||
"char(4)" char(4) PATH '$',
|
|
||||||
"bool" bool PATH '$',
|
|
||||||
"numeric" numeric PATH '$',
|
|
||||||
"domain" jsonb_test_domain PATH '$',
|
|
||||||
js json PATH '$',
|
|
||||||
jb jsonb PATH '$',
|
|
||||||
jst text FORMAT JSON PATH '$',
|
|
||||||
jsc char(4) FORMAT JSON PATH '$',
|
|
||||||
jsv varchar(4) FORMAT JSON PATH '$',
|
|
||||||
jsb jsonb FORMAT JSON PATH '$',
|
|
||||||
jsbq jsonb FORMAT JSON PATH '$' OMIT QUOTES,
|
|
||||||
aaa int, -- implicit path '$."aaa"',
|
|
||||||
aaa1 int PATH '$.aaa',
|
|
||||||
exists1 bool EXISTS PATH '$.aaa',
|
|
||||||
exists2 int EXISTS PATH '$.aaa',
|
|
||||||
exists3 int EXISTS PATH 'strict $.aaa' UNKNOWN ON ERROR,
|
|
||||||
exists4 text EXISTS PATH 'strict $.aaa' FALSE ON ERROR,
|
|
||||||
|
|
||||||
js2 json PATH '$',
|
|
||||||
jsb2w jsonb PATH '$' WITH WRAPPER,
|
|
||||||
jsb2q jsonb PATH '$' OMIT QUOTES,
|
|
||||||
ia int[] PATH '$',
|
|
||||||
ta text[] PATH '$',
|
|
||||||
jba jsonb[] PATH '$'
|
|
||||||
)
|
|
||||||
) jt
|
|
||||||
ON true;
|
|
||||||
|
|
||||||
-- JSON_TABLE: Test backward parsing
|
|
||||||
|
|
||||||
CREATE VIEW jsonb_table_view AS
|
|
||||||
SELECT * FROM
|
|
||||||
JSON_TABLE(
|
|
||||||
jsonb 'null', 'lax $[*]' PASSING 1 + 2 AS a, json '"foo"' AS "b c"
|
|
||||||
COLUMNS (
|
|
||||||
id FOR ORDINALITY,
|
|
||||||
id2 FOR ORDINALITY, -- allowed additional ordinality columns
|
|
||||||
"int" int PATH '$',
|
|
||||||
"text" text PATH '$',
|
|
||||||
"char(4)" char(4) PATH '$',
|
|
||||||
"bool" bool PATH '$',
|
|
||||||
"numeric" numeric PATH '$',
|
|
||||||
"domain" jsonb_test_domain PATH '$',
|
|
||||||
js json PATH '$',
|
|
||||||
jb jsonb PATH '$',
|
|
||||||
jst text FORMAT JSON PATH '$',
|
|
||||||
jsc char(4) FORMAT JSON PATH '$',
|
|
||||||
jsv varchar(4) FORMAT JSON PATH '$',
|
|
||||||
jsb jsonb FORMAT JSON PATH '$',
|
|
||||||
jsbq jsonb FORMAT JSON PATH '$' OMIT QUOTES,
|
|
||||||
aaa int, -- implicit path '$."aaa"',
|
|
||||||
aaa1 int PATH '$.aaa',
|
|
||||||
exists1 bool EXISTS PATH '$.aaa',
|
|
||||||
exists2 int EXISTS PATH '$.aaa' TRUE ON ERROR,
|
|
||||||
exists3 text EXISTS PATH 'strict $.aaa' UNKNOWN ON ERROR,
|
|
||||||
|
|
||||||
js2 json PATH '$',
|
|
||||||
jsb2w jsonb PATH '$' WITH WRAPPER,
|
|
||||||
jsb2q jsonb PATH '$' OMIT QUOTES,
|
|
||||||
ia int[] PATH '$',
|
|
||||||
ta text[] PATH '$',
|
|
||||||
jba jsonb[] PATH '$',
|
|
||||||
|
|
||||||
NESTED PATH '$[1]' AS p1 COLUMNS (
|
|
||||||
a1 int,
|
|
||||||
NESTED PATH '$[*]' AS "p1 1" COLUMNS (
|
|
||||||
a11 text
|
|
||||||
),
|
|
||||||
b1 text
|
|
||||||
),
|
|
||||||
NESTED PATH '$[2]' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$[*]' AS "p2:1" COLUMNS (
|
|
||||||
a21 text
|
|
||||||
),
|
|
||||||
NESTED PATH '$[*]' AS p22 COLUMNS (
|
|
||||||
a22 text
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
\sv jsonb_table_view
|
|
||||||
|
|
||||||
EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM jsonb_table_view;
|
|
||||||
|
|
||||||
DROP VIEW jsonb_table_view;
|
|
||||||
DROP DOMAIN jsonb_test_domain;
|
|
||||||
|
|
||||||
-- JSON_TABLE: ON EMPTY/ON ERROR behavior
|
|
||||||
SELECT *
|
|
||||||
FROM
|
|
||||||
(VALUES ('1'), ('"err"')) vals(js),
|
|
||||||
JSON_TABLE(vals.js::jsonb, '$' COLUMNS (a int PATH '$')) jt;
|
|
||||||
|
|
||||||
SELECT *
|
|
||||||
FROM
|
|
||||||
(VALUES ('1'), ('"err"')) vals(js)
|
|
||||||
LEFT OUTER JOIN
|
|
||||||
JSON_TABLE(vals.js::jsonb, '$' COLUMNS (a int PATH '$') ERROR ON ERROR) jt
|
|
||||||
ON true;
|
|
||||||
|
|
||||||
SELECT *
|
|
||||||
FROM
|
|
||||||
(VALUES ('1'), ('"err"')) vals(js)
|
|
||||||
LEFT OUTER JOIN
|
|
||||||
JSON_TABLE(vals.js::jsonb, '$' COLUMNS (a int PATH '$' ERROR ON ERROR)) jt
|
|
||||||
ON true;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH '$.a' ERROR ON EMPTY)) jt;
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH 'strict $.a' ERROR ON EMPTY) ERROR ON ERROR) jt;
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '1', '$' COLUMNS (a int PATH 'lax $.a' ERROR ON EMPTY) ERROR ON ERROR) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int PATH '$' DEFAULT 1 ON EMPTY DEFAULT 2 ON ERROR)) jt;
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int PATH 'strict $.a' DEFAULT 1 ON EMPTY DEFAULT 2 ON ERROR)) jt;
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int PATH 'lax $.a' DEFAULT 1 ON EMPTY DEFAULT 2 ON ERROR)) jt;
|
|
||||||
|
|
||||||
-- JSON_TABLE: EXISTS PATH types
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int4 EXISTS PATH '$.a'));
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int2 EXISTS PATH '$.a'));
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a int8 EXISTS PATH '$.a'));
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a float4 EXISTS PATH '$.a'));
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a char(3) EXISTS PATH '$.a'));
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a json EXISTS PATH '$.a'));
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '"a"', '$' COLUMNS (a jsonb EXISTS PATH '$.a'));
|
|
||||||
|
|
||||||
-- JSON_TABLE: nested paths and plans
|
|
||||||
|
|
||||||
-- Should fail (JSON_TABLE columns must contain explicit AS path
|
|
||||||
-- specifications if explicit PLAN clause is used)
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb '[]', '$' -- AS <path name> required here
|
|
||||||
COLUMNS (
|
|
||||||
foo int PATH '$'
|
|
||||||
)
|
|
||||||
PLAN DEFAULT (UNION)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb '[]', '$' AS path1
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' COLUMNS ( -- AS <path name> required here
|
|
||||||
foo int PATH '$'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN DEFAULT (UNION)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- Should fail (column names must be distinct)
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb '[]', '$' AS a
|
|
||||||
COLUMNS (
|
|
||||||
a int
|
|
||||||
)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb '[]', '$' AS a
|
|
||||||
COLUMNS (
|
|
||||||
b int,
|
|
||||||
NESTED PATH '$' AS a
|
|
||||||
COLUMNS (
|
|
||||||
c int
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb '[]', '$'
|
|
||||||
COLUMNS (
|
|
||||||
b int,
|
|
||||||
NESTED PATH '$' AS b
|
|
||||||
COLUMNS (
|
|
||||||
c int
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb '[]', '$'
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS a
|
|
||||||
COLUMNS (
|
|
||||||
b int
|
|
||||||
),
|
|
||||||
NESTED PATH '$'
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS a
|
|
||||||
COLUMNS (
|
|
||||||
c int
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- JSON_TABLE: plan validation
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p1)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER p3)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 UNION p1 UNION p11)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER (p1 CROSS p13))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER (p1 CROSS p2))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER ((p1 UNION p11) CROSS p2))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER ((p1 INNER p11) CROSS p2))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', '$[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER ((p1 INNER (p12 CROSS p11)) CROSS p2))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', 'strict $[*]' AS p0
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN (p0 OUTER ((p1 INNER (p12 CROSS p11)) CROSS (p2 INNER p21)))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
SELECT * FROM JSON_TABLE(
|
|
||||||
jsonb 'null', 'strict $[*]' -- without root path name
|
|
||||||
COLUMNS (
|
|
||||||
NESTED PATH '$' AS p1 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p11 COLUMNS ( foo int ),
|
|
||||||
NESTED PATH '$' AS p12 COLUMNS ( bar int )
|
|
||||||
),
|
|
||||||
NESTED PATH '$' AS p2 COLUMNS (
|
|
||||||
NESTED PATH '$' AS p21 COLUMNS ( baz int )
|
|
||||||
)
|
|
||||||
)
|
|
||||||
PLAN ((p1 INNER (p12 CROSS p11)) CROSS (p2 INNER p21))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- JSON_TABLE: plan execution
|
|
||||||
|
|
||||||
CREATE TEMP TABLE jsonb_table_test (js jsonb);
|
|
||||||
|
|
||||||
INSERT INTO jsonb_table_test
|
|
||||||
VALUES (
|
|
||||||
'[
|
|
||||||
{"a": 1, "b": [], "c": []},
|
|
||||||
{"a": 2, "b": [1, 2, 3], "c": [10, null, 20]},
|
|
||||||
{"a": 3, "b": [1, 2], "c": []},
|
|
||||||
{"x": "4", "b": [1, 2], "c": 123}
|
|
||||||
]'
|
|
||||||
);
|
|
||||||
|
|
||||||
-- unspecified plan (outer, union)
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- default plan (outer, union)
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan default (outer, union)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- specific plan (p outer (pb union pc))
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan (p outer (pb union pc))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- specific plan (p outer (pc union pb))
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan (p outer (pc union pb))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- default plan (inner, union)
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan default (inner)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- specific plan (p inner (pb union pc))
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan (p inner (pb union pc))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- default plan (inner, cross)
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan default (cross, inner)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- specific plan (p inner (pb cross pc))
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan (p inner (pb cross pc))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- default plan (outer, cross)
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan default (outer, cross)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- specific plan (p outer (pb cross pc))
|
|
||||||
select
|
|
||||||
jt.*
|
|
||||||
from
|
|
||||||
jsonb_table_test jtt,
|
|
||||||
json_table (
|
|
||||||
jtt.js,'strict $[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on empty,
|
|
||||||
nested path 'strict $.b[*]' as pb columns ( b int path '$' ),
|
|
||||||
nested path 'strict $.c[*]' as pc columns ( c int path '$' )
|
|
||||||
)
|
|
||||||
plan (p outer (pb cross pc))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
|
|
||||||
select
|
|
||||||
jt.*, b1 + 100 as b
|
|
||||||
from
|
|
||||||
json_table (jsonb
|
|
||||||
'[
|
|
||||||
{"a": 1, "b": [[1, 10], [2], [3, 30, 300]], "c": [1, null, 2]},
|
|
||||||
{"a": 2, "b": [10, 20], "c": [1, null, 2]},
|
|
||||||
{"x": "3", "b": [11, 22, 33, 44]}
|
|
||||||
]',
|
|
||||||
'$[*]' as p
|
|
||||||
columns (
|
|
||||||
n for ordinality,
|
|
||||||
a int path 'lax $.a' default -1 on error,
|
|
||||||
nested path 'strict $.b[*]' as pb columns (
|
|
||||||
b text format json path '$',
|
|
||||||
nested path 'strict $[*]' as pb1 columns (
|
|
||||||
b1 int path '$'
|
|
||||||
)
|
|
||||||
),
|
|
||||||
nested path 'strict $.c[*]' as pc columns (
|
|
||||||
c text format json path '$',
|
|
||||||
nested path 'strict $[*]' as pc1 columns (
|
|
||||||
c1 int path '$'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
--plan default(outer, cross)
|
|
||||||
plan(p outer ((pb inner pb1) cross (pc outer pc1)))
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- Should succeed (JSON arguments are passed to root and nested paths)
|
|
||||||
SELECT *
|
|
||||||
FROM
|
|
||||||
generate_series(1, 4) x,
|
|
||||||
generate_series(1, 3) y,
|
|
||||||
JSON_TABLE(jsonb
|
|
||||||
'[[1,2,3],[2,3,4,5],[3,4,5,6]]',
|
|
||||||
'strict $[*] ? (@[*] < $x)'
|
|
||||||
PASSING x AS x, y AS y
|
|
||||||
COLUMNS (
|
|
||||||
y text FORMAT JSON PATH '$',
|
|
||||||
NESTED PATH 'strict $[*] ? (@ >= $y)'
|
|
||||||
COLUMNS (
|
|
||||||
z int PATH '$'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- Should fail (JSON arguments are not passed to column paths)
|
|
||||||
SELECT *
|
|
||||||
FROM JSON_TABLE(
|
|
||||||
jsonb '[1,2,3]',
|
|
||||||
'$[*] ? (@ < $x)'
|
|
||||||
PASSING 10 AS x
|
|
||||||
COLUMNS (y text FORMAT JSON PATH '$ ? (@ < $x)')
|
|
||||||
) jt;
|
|
||||||
|
|
||||||
-- Extension: non-constant JSON path
|
|
||||||
SELECT JSON_EXISTS(jsonb '{"a": 123}', '$' || '.' || 'a');
|
|
||||||
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a');
|
|
||||||
SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
|
|
||||||
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
|
|
||||||
SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
|
|
||||||
-- Should fail (invalid path)
|
|
||||||
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
|
|
||||||
-- Should fail (not supported)
|
|
||||||
SELECT * FROM JSON_TABLE(jsonb '{"a": 123}', '$' || '.' || 'a' COLUMNS (foo int));
|
|
||||||
|
|
||||||
-- Test parallel JSON_VALUE()
|
|
||||||
|
|
||||||
|
|
||||||
CREATE UNLOGGED TABLE test_parallel_jsonb_value AS
|
|
||||||
SELECT i::text::jsonb AS js
|
|
||||||
FROM generate_series(1, 50000) i;
|
|
||||||
|
|
||||||
|
|
||||||
-- encourage use of parallel plans
|
|
||||||
set parallel_setup_cost=0;
|
|
||||||
set parallel_tuple_cost=0;
|
|
||||||
set min_parallel_table_scan_size=0;
|
|
||||||
set max_parallel_workers_per_gather=4;
|
|
||||||
set parallel_leader_participation = off;
|
|
||||||
|
|
||||||
-- Should be non-parallel due to subtransactions
|
|
||||||
EXPLAIN (COSTS OFF)
|
|
||||||
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
|
|
||||||
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
|
|
||||||
|
|
||||||
-- Should be parallel
|
|
||||||
EXPLAIN (COSTS OFF)
|
|
||||||
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
|
|
||||||
SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
|
|
||||||
|
|
||||||
DROP TABLE test_parallel_jsonb_value;
|
|
@ -854,10 +854,8 @@ WHERE a.aggfnoid = p.oid AND
|
|||||||
NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
|
NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
|
||||||
OR (p.pronargs > 2 AND
|
OR (p.pronargs > 2 AND
|
||||||
NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
|
NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
|
||||||
OR (p.pronargs > 3 AND
|
-- we could carry the check further, but 3 args is enough for now
|
||||||
NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4]))
|
OR (p.pronargs > 3)
|
||||||
-- we could carry the check further, but 4 args is enough for now
|
|
||||||
OR (p.pronargs > 4)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Cross-check finalfn (if present) against its entry in pg_proc.
|
-- Cross-check finalfn (if present) against its entry in pg_proc.
|
||||||
|
@ -1,471 +0,0 @@
|
|||||||
-- JSON()
|
|
||||||
SELECT JSON();
|
|
||||||
SELECT JSON(NULL);
|
|
||||||
SELECT JSON('{ "a" : 1 } ');
|
|
||||||
SELECT JSON('{ "a" : 1 } ' FORMAT JSON);
|
|
||||||
SELECT JSON('{ "a" : 1 } ' FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON('{ "a" : 1 } '::bytea FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT pg_typeof(JSON('{ "a" : 1 } '));
|
|
||||||
|
|
||||||
SELECT JSON(' 1 '::json);
|
|
||||||
SELECT JSON(' 1 '::jsonb);
|
|
||||||
SELECT JSON(' 1 '::json WITH UNIQUE KEYS);
|
|
||||||
SELECT JSON(123);
|
|
||||||
|
|
||||||
SELECT JSON('{"a": 1, "a": 2}');
|
|
||||||
SELECT JSON('{"a": 1, "a": 2}' WITH UNIQUE KEYS);
|
|
||||||
SELECT JSON('{"a": 1, "a": 2}' WITHOUT UNIQUE KEYS);
|
|
||||||
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' FORMAT JSON);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON ENCODING UTF8);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITH UNIQUE KEYS);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITHOUT UNIQUE KEYS);
|
|
||||||
|
|
||||||
SELECT JSON('123' RETURNING text);
|
|
||||||
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb);
|
|
||||||
SELECT pg_typeof(JSON('123'));
|
|
||||||
SELECT pg_typeof(JSON('123' RETURNING json));
|
|
||||||
SELECT pg_typeof(JSON('123' RETURNING jsonb));
|
|
||||||
|
|
||||||
-- JSON_SCALAR()
|
|
||||||
SELECT JSON_SCALAR();
|
|
||||||
SELECT JSON_SCALAR(NULL);
|
|
||||||
SELECT JSON_SCALAR(NULL::int);
|
|
||||||
SELECT JSON_SCALAR(123);
|
|
||||||
SELECT JSON_SCALAR(123.45);
|
|
||||||
SELECT JSON_SCALAR(123.45::numeric);
|
|
||||||
SELECT JSON_SCALAR(true);
|
|
||||||
SELECT JSON_SCALAR(false);
|
|
||||||
SELECT JSON_SCALAR(' 123.45');
|
|
||||||
SELECT JSON_SCALAR('2020-06-07'::date);
|
|
||||||
SELECT JSON_SCALAR('2020-06-07 01:02:03'::timestamp);
|
|
||||||
SELECT JSON_SCALAR('{}'::json);
|
|
||||||
SELECT JSON_SCALAR('{}'::jsonb);
|
|
||||||
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123');
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json);
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb);
|
|
||||||
|
|
||||||
-- JSON_SERIALIZE()
|
|
||||||
SELECT JSON_SERIALIZE();
|
|
||||||
SELECT JSON_SERIALIZE(NULL);
|
|
||||||
SELECT JSON_SERIALIZE(JSON('{ "a" : 1 } '));
|
|
||||||
SELECT JSON_SERIALIZE('{ "a" : 1 } ');
|
|
||||||
SELECT JSON_SERIALIZE('1');
|
|
||||||
SELECT JSON_SERIALIZE('1' FORMAT JSON);
|
|
||||||
SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING bytea);
|
|
||||||
SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING varchar);
|
|
||||||
SELECT pg_typeof(JSON_SERIALIZE(NULL));
|
|
||||||
|
|
||||||
-- only string types or bytea allowed
|
|
||||||
SELECT JSON_SERIALIZE('{ "a" : 1 } ' RETURNING jsonb);
|
|
||||||
|
|
||||||
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SERIALIZE('{}');
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SERIALIZE('{}' RETURNING bytea);
|
|
||||||
|
|
||||||
-- JSON_OBJECT()
|
|
||||||
SELECT JSON_OBJECT();
|
|
||||||
SELECT JSON_OBJECT(RETURNING json);
|
|
||||||
SELECT JSON_OBJECT(RETURNING json FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT(RETURNING jsonb);
|
|
||||||
SELECT JSON_OBJECT(RETURNING jsonb FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT(RETURNING text);
|
|
||||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
|
||||||
SELECT JSON_OBJECT(RETURNING bytea);
|
|
||||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF16);
|
|
||||||
SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF32);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING UTF8);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT(NULL: 1);
|
|
||||||
SELECT JSON_OBJECT('a': 2 + 3);
|
|
||||||
SELECT JSON_OBJECT('a' VALUE 2 + 3);
|
|
||||||
--SELECT JSON_OBJECT(KEY 'a' VALUE 2 + 3);
|
|
||||||
SELECT JSON_OBJECT('a' || 2: 1);
|
|
||||||
SELECT JSON_OBJECT(('a' || 2) VALUE 1);
|
|
||||||
--SELECT JSON_OBJECT('a' || 2 VALUE 1);
|
|
||||||
--SELECT JSON_OBJECT(KEY 'a' || 2 VALUE 1);
|
|
||||||
SELECT JSON_OBJECT('a': 2::text);
|
|
||||||
SELECT JSON_OBJECT('a' VALUE 2::text);
|
|
||||||
--SELECT JSON_OBJECT(KEY 'a' VALUE 2::text);
|
|
||||||
SELECT JSON_OBJECT(1::text: 2);
|
|
||||||
SELECT JSON_OBJECT((1::text) VALUE 2);
|
|
||||||
--SELECT JSON_OBJECT(1::text VALUE 2);
|
|
||||||
--SELECT JSON_OBJECT(KEY 1::text VALUE 2);
|
|
||||||
SELECT JSON_OBJECT(json '[1]': 123);
|
|
||||||
SELECT JSON_OBJECT(ARRAY[1,2,3]: 'aaa');
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT(
|
|
||||||
'a': '123',
|
|
||||||
1.23: 123,
|
|
||||||
'c': json '[ 1,true,{ } ]',
|
|
||||||
'd': jsonb '{ "x" : 123.45 }'
|
|
||||||
);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT(
|
|
||||||
'a': '123',
|
|
||||||
1.23: 123,
|
|
||||||
'c': json '[ 1,true,{ } ]',
|
|
||||||
'd': jsonb '{ "x" : 123.45 }'
|
|
||||||
RETURNING jsonb
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
SELECT JSON_OBJECT(
|
|
||||||
'a': '123',
|
|
||||||
KEY 1.23 VALUE 123,
|
|
||||||
'c' VALUE json '[1, true, {}]'
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa'));
|
|
||||||
SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa' RETURNING jsonb));
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text));
|
|
||||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text) FORMAT JSON);
|
|
||||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea));
|
|
||||||
SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea) FORMAT JSON);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2);
|
|
||||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 NULL ON NULL);
|
|
||||||
SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 ABSENT ON NULL);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '1': NULL NULL ON NULL WITH UNIQUE RETURNING jsonb);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 NULL ON NULL WITH UNIQUE);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb);
|
|
||||||
SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
|
|
||||||
|
|
||||||
|
|
||||||
-- JSON_ARRAY()
|
|
||||||
SELECT JSON_ARRAY();
|
|
||||||
SELECT JSON_ARRAY(RETURNING json);
|
|
||||||
SELECT JSON_ARRAY(RETURNING json FORMAT JSON);
|
|
||||||
SELECT JSON_ARRAY(RETURNING jsonb);
|
|
||||||
SELECT JSON_ARRAY(RETURNING jsonb FORMAT JSON);
|
|
||||||
SELECT JSON_ARRAY(RETURNING text);
|
|
||||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON);
|
|
||||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
|
||||||
SELECT JSON_ARRAY(RETURNING bytea);
|
|
||||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON);
|
|
||||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF8);
|
|
||||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16);
|
|
||||||
SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32);
|
|
||||||
|
|
||||||
SELECT JSON_ARRAY('aaa', 111, true, array[1,2,3], NULL, json '{"a": [1]}', jsonb '["a",3]');
|
|
||||||
|
|
||||||
SELECT JSON_ARRAY('a', NULL, 'b' NULL ON NULL);
|
|
||||||
SELECT JSON_ARRAY('a', NULL, 'b' ABSENT ON NULL);
|
|
||||||
SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL);
|
|
||||||
SELECT JSON_ARRAY('a', NULL, 'b' NULL ON NULL RETURNING jsonb);
|
|
||||||
SELECT JSON_ARRAY('a', NULL, 'b' ABSENT ON NULL RETURNING jsonb);
|
|
||||||
SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL RETURNING jsonb);
|
|
||||||
|
|
||||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' RETURNING text));
|
|
||||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text));
|
|
||||||
SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT JSON);
|
|
||||||
|
|
||||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
|
|
||||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
|
|
||||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
|
|
||||||
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
|
|
||||||
--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
|
|
||||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
|
|
||||||
-- Should fail
|
|
||||||
SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
|
|
||||||
SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
|
|
||||||
SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
|
|
||||||
|
|
||||||
-- JSON_ARRAYAGG()
|
|
||||||
SELECT JSON_ARRAYAGG(i) IS NULL,
|
|
||||||
JSON_ARRAYAGG(i RETURNING jsonb) IS NULL
|
|
||||||
FROM generate_series(1, 0) i;
|
|
||||||
|
|
||||||
SELECT JSON_ARRAYAGG(i),
|
|
||||||
JSON_ARRAYAGG(i RETURNING jsonb)
|
|
||||||
FROM generate_series(1, 5) i;
|
|
||||||
|
|
||||||
SELECT JSON_ARRAYAGG(i ORDER BY i DESC)
|
|
||||||
FROM generate_series(1, 5) i;
|
|
||||||
|
|
||||||
SELECT JSON_ARRAYAGG(i::text::json)
|
|
||||||
FROM generate_series(1, 5) i;
|
|
||||||
|
|
||||||
SELECT JSON_ARRAYAGG(JSON_ARRAY(i, i + 1 RETURNING text) FORMAT JSON)
|
|
||||||
FROM generate_series(1, 5) i;
|
|
||||||
|
|
||||||
SELECT JSON_ARRAYAGG(NULL),
|
|
||||||
JSON_ARRAYAGG(NULL RETURNING jsonb)
|
|
||||||
FROM generate_series(1, 5);
|
|
||||||
|
|
||||||
SELECT JSON_ARRAYAGG(NULL NULL ON NULL),
|
|
||||||
JSON_ARRAYAGG(NULL NULL ON NULL RETURNING jsonb)
|
|
||||||
FROM generate_series(1, 5);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
JSON_ARRAYAGG(bar),
|
|
||||||
JSON_ARRAYAGG(bar RETURNING jsonb),
|
|
||||||
JSON_ARRAYAGG(bar ABSENT ON NULL),
|
|
||||||
JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb),
|
|
||||||
JSON_ARRAYAGG(bar NULL ON NULL),
|
|
||||||
JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb),
|
|
||||||
JSON_ARRAYAGG(foo),
|
|
||||||
JSON_ARRAYAGG(foo RETURNING jsonb),
|
|
||||||
JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2),
|
|
||||||
JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2)
|
|
||||||
FROM
|
|
||||||
(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
bar, JSON_ARRAYAGG(bar) FILTER (WHERE bar > 2) OVER (PARTITION BY foo.bar % 2)
|
|
||||||
FROM
|
|
||||||
(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL), (5), (4)) foo(bar);
|
|
||||||
|
|
||||||
-- JSON_OBJECTAGG()
|
|
||||||
SELECT JSON_OBJECTAGG('key': 1) IS NULL,
|
|
||||||
JSON_OBJECTAGG('key': 1 RETURNING jsonb) IS NULL
|
|
||||||
WHERE FALSE;
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(NULL: 1);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(NULL: 1 RETURNING jsonb);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
JSON_OBJECTAGG(i: i),
|
|
||||||
-- JSON_OBJECTAGG(i VALUE i),
|
|
||||||
-- JSON_OBJECTAGG(KEY i VALUE i),
|
|
||||||
JSON_OBJECTAGG(i: i RETURNING jsonb)
|
|
||||||
FROM
|
|
||||||
generate_series(1, 5) i;
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
JSON_OBJECTAGG(k: v),
|
|
||||||
JSON_OBJECTAGG(k: v NULL ON NULL),
|
|
||||||
JSON_OBJECTAGG(k: v ABSENT ON NULL),
|
|
||||||
JSON_OBJECTAGG(k: v RETURNING jsonb),
|
|
||||||
JSON_OBJECTAGG(k: v NULL ON NULL RETURNING jsonb),
|
|
||||||
JSON_OBJECTAGG(k: v ABSENT ON NULL RETURNING jsonb)
|
|
||||||
FROM
|
|
||||||
(VALUES (1, 1), (1, NULL), (2, NULL), (3, 3)) foo(k, v);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS)
|
|
||||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
|
|
||||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
|
|
||||||
FROM (VALUES (1, 1), (0, NULL), (3, NULL), (2, 2), (4, NULL)) foo(k, v);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS RETURNING jsonb)
|
|
||||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
|
|
||||||
FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
|
|
||||||
|
|
||||||
SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
|
|
||||||
FROM (VALUES (1, 1), (0, NULL),(4, null), (5, null),(6, null),(2, 2)) foo(k, v);
|
|
||||||
|
|
||||||
-- Test JSON_OBJECT deparsing
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
|
|
||||||
|
|
||||||
CREATE VIEW json_object_view AS
|
|
||||||
SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
|
|
||||||
|
|
||||||
\sv json_object_view
|
|
||||||
|
|
||||||
DROP VIEW json_object_view;
|
|
||||||
|
|
||||||
SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v WITH UNIQUE KEYS) OVER (ORDER BY k)
|
|
||||||
FROM (VALUES (1,1), (2,2)) a(k,v);
|
|
||||||
|
|
||||||
SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v WITH UNIQUE KEYS) OVER (ORDER BY k)
|
|
||||||
FROM (VALUES (1,1), (1,2), (2,2)) a(k,v);
|
|
||||||
|
|
||||||
SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL WITH UNIQUE KEYS)
|
|
||||||
OVER (ORDER BY k)
|
|
||||||
FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
|
|
||||||
|
|
||||||
SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL)
|
|
||||||
OVER (ORDER BY k)
|
|
||||||
FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
|
|
||||||
|
|
||||||
SELECT to_json(a) AS a, JSON_OBJECTAGG(k : v ABSENT ON NULL)
|
|
||||||
OVER (ORDER BY k RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
|
|
||||||
FROM (VALUES (1,1), (1,null), (2,2)) a(k,v);
|
|
||||||
|
|
||||||
-- Test JSON_ARRAY deparsing
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
|
|
||||||
|
|
||||||
CREATE VIEW json_array_view AS
|
|
||||||
SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
|
|
||||||
|
|
||||||
\sv json_array_view
|
|
||||||
|
|
||||||
DROP VIEW json_array_view;
|
|
||||||
|
|
||||||
-- Test JSON_OBJECTAGG deparsing
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
|
||||||
FROM generate_series(1,5) i;
|
|
||||||
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
|
|
||||||
FROM generate_series(1,5) i;
|
|
||||||
|
|
||||||
CREATE VIEW json_objectagg_view AS
|
|
||||||
SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
|
|
||||||
FROM generate_series(1,5) i;
|
|
||||||
|
|
||||||
\sv json_objectagg_view
|
|
||||||
|
|
||||||
DROP VIEW json_objectagg_view;
|
|
||||||
|
|
||||||
-- Test JSON_ARRAYAGG deparsing
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
|
||||||
FROM generate_series(1,5) i;
|
|
||||||
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
|
|
||||||
FROM generate_series(1,5) i;
|
|
||||||
|
|
||||||
CREATE VIEW json_arrayagg_view AS
|
|
||||||
SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
|
|
||||||
FROM generate_series(1,5) i;
|
|
||||||
|
|
||||||
\sv json_arrayagg_view
|
|
||||||
|
|
||||||
DROP VIEW json_arrayagg_view;
|
|
||||||
|
|
||||||
-- Test JSON_ARRAY(subquery) deparsing
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
|
|
||||||
|
|
||||||
CREATE VIEW json_array_subquery_view AS
|
|
||||||
SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
|
|
||||||
|
|
||||||
\sv json_array_subquery_view
|
|
||||||
|
|
||||||
DROP VIEW json_array_subquery_view;
|
|
||||||
|
|
||||||
-- IS JSON predicate
|
|
||||||
SELECT NULL IS JSON;
|
|
||||||
SELECT NULL IS NOT JSON;
|
|
||||||
SELECT NULL::json IS JSON;
|
|
||||||
SELECT NULL::jsonb IS JSON;
|
|
||||||
SELECT NULL::text IS JSON;
|
|
||||||
SELECT NULL::bytea IS JSON;
|
|
||||||
SELECT NULL::int IS JSON;
|
|
||||||
|
|
||||||
SELECT '' IS JSON;
|
|
||||||
|
|
||||||
SELECT bytea '\x00' IS JSON;
|
|
||||||
|
|
||||||
CREATE TABLE test_is_json (js text);
|
|
||||||
|
|
||||||
INSERT INTO test_is_json VALUES
|
|
||||||
(NULL),
|
|
||||||
(''),
|
|
||||||
('123'),
|
|
||||||
('"aaa "'),
|
|
||||||
('true'),
|
|
||||||
('null'),
|
|
||||||
('[]'),
|
|
||||||
('[1, "2", {}]'),
|
|
||||||
('{}'),
|
|
||||||
('{ "a": 1, "b": null }'),
|
|
||||||
('{ "a": 1, "a": null }'),
|
|
||||||
('{ "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }'),
|
|
||||||
('{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }'),
|
|
||||||
('aaa'),
|
|
||||||
('{a:1}'),
|
|
||||||
('["a",]');
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
js,
|
|
||||||
js IS JSON "IS JSON",
|
|
||||||
js IS NOT JSON "IS NOT JSON",
|
|
||||||
js IS JSON VALUE "IS VALUE",
|
|
||||||
js IS JSON OBJECT "IS OBJECT",
|
|
||||||
js IS JSON ARRAY "IS ARRAY",
|
|
||||||
js IS JSON SCALAR "IS SCALAR",
|
|
||||||
js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
|
|
||||||
js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
|
|
||||||
FROM
|
|
||||||
test_is_json;
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
js,
|
|
||||||
js IS JSON "IS JSON",
|
|
||||||
js IS NOT JSON "IS NOT JSON",
|
|
||||||
js IS JSON VALUE "IS VALUE",
|
|
||||||
js IS JSON OBJECT "IS OBJECT",
|
|
||||||
js IS JSON ARRAY "IS ARRAY",
|
|
||||||
js IS JSON SCALAR "IS SCALAR",
|
|
||||||
js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
|
|
||||||
js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
|
|
||||||
FROM
|
|
||||||
(SELECT js::json FROM test_is_json WHERE js IS JSON) foo(js);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
js0,
|
|
||||||
js IS JSON "IS JSON",
|
|
||||||
js IS NOT JSON "IS NOT JSON",
|
|
||||||
js IS JSON VALUE "IS VALUE",
|
|
||||||
js IS JSON OBJECT "IS OBJECT",
|
|
||||||
js IS JSON ARRAY "IS ARRAY",
|
|
||||||
js IS JSON SCALAR "IS SCALAR",
|
|
||||||
js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
|
|
||||||
js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
|
|
||||||
FROM
|
|
||||||
(SELECT js, js::bytea FROM test_is_json WHERE js IS JSON) foo(js0, js);
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
js,
|
|
||||||
js IS JSON "IS JSON",
|
|
||||||
js IS NOT JSON "IS NOT JSON",
|
|
||||||
js IS JSON VALUE "IS VALUE",
|
|
||||||
js IS JSON OBJECT "IS OBJECT",
|
|
||||||
js IS JSON ARRAY "IS ARRAY",
|
|
||||||
js IS JSON SCALAR "IS SCALAR",
|
|
||||||
js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
|
|
||||||
js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
|
|
||||||
FROM
|
|
||||||
(SELECT js::jsonb FROM test_is_json WHERE js IS JSON) foo(js);
|
|
||||||
|
|
||||||
-- Test IS JSON deparsing
|
|
||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
|
||||||
SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT JSON ARRAY AS "array", '{}' IS JSON OBJECT WITH UNIQUE AS "object" FROM generate_series(1, 3) i;
|
|
||||||
|
|
||||||
CREATE VIEW is_json_view AS
|
|
||||||
SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT JSON ARRAY AS "array", '{}' IS JSON OBJECT WITH UNIQUE AS "object" FROM generate_series(1, 3) i;
|
|
||||||
|
|
||||||
\sv is_json_view
|
|
||||||
|
|
||||||
DROP VIEW is_json_view;
|
|
@ -1237,12 +1237,10 @@ JsonBehaviorType
|
|||||||
JsonCoercion
|
JsonCoercion
|
||||||
JsonCommon
|
JsonCommon
|
||||||
JsonConstructorExpr
|
JsonConstructorExpr
|
||||||
JsonConstructorExprState
|
|
||||||
JsonConstructorType
|
JsonConstructorType
|
||||||
JsonEncoding
|
JsonEncoding
|
||||||
JsonExpr
|
JsonExpr
|
||||||
JsonExprOp
|
JsonExprOp
|
||||||
JsonExprState
|
|
||||||
JsonFormat
|
JsonFormat
|
||||||
JsonFormatType
|
JsonFormatType
|
||||||
JsonFunc
|
JsonFunc
|
||||||
@ -1291,18 +1289,6 @@ JsonQuotes
|
|||||||
JsonReturning
|
JsonReturning
|
||||||
JsonScalarExpr
|
JsonScalarExpr
|
||||||
JsonSemAction
|
JsonSemAction
|
||||||
JsonSerializeExpr
|
|
||||||
JsonTable
|
|
||||||
JsonTableColumn
|
|
||||||
JsonTableColumnType
|
|
||||||
JsonTableContext
|
|
||||||
JsonTableJoinState
|
|
||||||
JsonTableParent
|
|
||||||
JsonTablePlan
|
|
||||||
JsonTablePlanJoinType
|
|
||||||
JsonTablePlanType
|
|
||||||
JsonTableScanState
|
|
||||||
JsonTableSibling
|
|
||||||
JsonTokenType
|
JsonTokenType
|
||||||
JsonTransformStringValuesAction
|
JsonTransformStringValuesAction
|
||||||
JsonTypeCategory
|
JsonTypeCategory
|
||||||
@ -2724,7 +2710,6 @@ TableFunc
|
|||||||
TableFuncRoutine
|
TableFuncRoutine
|
||||||
TableFuncScan
|
TableFuncScan
|
||||||
TableFuncScanState
|
TableFuncScanState
|
||||||
TableFuncType
|
|
||||||
TableInfo
|
TableInfo
|
||||||
TableLikeClause
|
TableLikeClause
|
||||||
TableSampleClause
|
TableSampleClause
|
||||||
|
Loading…
x
Reference in New Issue
Block a user