aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/jsonpath_exec.c
diff options
context:
space:
mode:
authorAndrew Dunstan <andrew@dunslane.net>2022-09-01 17:07:14 -0400
committerAndrew Dunstan <andrew@dunslane.net>2022-09-01 17:07:14 -0400
commit2f2b18bd3f554e96a8cc885b177211be12288e4a (patch)
tree344a5d33738de735f68b98361a54eb5944726f8f /src/backend/utils/adt/jsonpath_exec.c
parent90247e742f849794d061a0444071647728054b45 (diff)
downloadpostgresql-2f2b18bd3f554e96a8cc885b177211be12288e4a.tar.gz
postgresql-2f2b18bd3f554e96a8cc885b177211be12288e4a.zip
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
Diffstat (limited to 'src/backend/utils/adt/jsonpath_exec.c')
-rw-r--r--src/backend/utils/adt/jsonpath_exec.c844
1 files changed, 40 insertions, 804 deletions
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 9c381ae7271..9f4192e0798 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -61,11 +61,9 @@
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
-#include "executor/execExpr.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
-#include "nodes/nodeFuncs.h"
#include "regex/regex.h"
#include "utils/builtins.h"
#include "utils/date.h"
@@ -76,8 +74,6 @@
#include "utils/guc.h"
#include "utils/json.h"
#include "utils/jsonpath.h"
-#include "utils/lsyscache.h"
-#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/varlena.h"
@@ -90,16 +86,12 @@ typedef struct JsonBaseObjectInfo
int id;
} JsonBaseObjectInfo;
-typedef int (*JsonPathVarCallback) (void *vars, char *varName, int varNameLen,
- JsonbValue *val, JsonbValue *baseObject);
-
/*
* Context of jsonpath execution.
*/
typedef struct JsonPathExecContext
{
- void *vars; /* variables to substitute into jsonpath */
- JsonPathVarCallback getVar;
+ Jsonb *vars; /* variables to substitute into jsonpath */
JsonbValue *root; /* for $ evaluation */
JsonbValue *current; /* for @ evaluation */
JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
@@ -159,59 +151,6 @@ typedef struct JsonValueListIterator
ListCell *next;
} 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 */
#define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
#define jspAutoUnwrap(cxt) ((cxt)->laxMode)
@@ -234,8 +173,7 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
void *param);
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
-static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
- JsonPathVarCallback getVar,
+static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
Jsonb *json, bool throwErrors,
JsonValueList *result, bool useTz);
static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
@@ -287,10 +225,7 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
JsonbValue *value);
static void getJsonPathVariable(JsonPathExecContext *cxt,
- JsonPathItem *variable, JsonbValue *value);
-static int getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
- int varNameLen, JsonbValue *val,
- JsonbValue *baseObject);
+ JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
static int JsonbArraySize(JsonbValue *jb);
static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
JsonbValue *rv, void *p);
@@ -302,7 +237,6 @@ static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
JsonPathItem *jsp, JsonbValue *jb, int32 *index);
static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
JsonbValue *jbv, int32 id);
-static void JsonValueListClear(JsonValueList *jvl);
static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
static int JsonValueListLength(const 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,
bool useTz, bool *have_error);
-
-static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt,
- Node *plan, JsonTableScanState *parent);
-static bool JsonTableNextRow(JsonTableScanState *scan);
-
-
/****************** User interface to JsonPath executor ********************/
/*
@@ -355,8 +283,7 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
silent = PG_GETARG_BOOL(3);
}
- res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
- jb, !silent, NULL, tz);
+ res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
PG_FREE_IF_COPY(jb, 0);
PG_FREE_IF_COPY(jp, 1);
@@ -411,8 +338,7 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
silent = PG_GETARG_BOOL(3);
}
- (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
- jb, !silent, &found, tz);
+ (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
PG_FREE_IF_COPY(jb, 0);
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);
silent = PG_GETARG_BOOL(3);
- (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
- jb, !silent, &found, tz);
+ (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
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);
bool silent = PG_GETARG_BOOL(3);
- (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
- jb, !silent, &found, tz);
+ (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
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);
bool silent = PG_GETARG_BOOL(3);
- (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
- jb, !silent, &found, tz);
+ (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
if (JsonValueListLength(&found) >= 1)
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.
*/
static JsonPathExecResult
-executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
- Jsonb *json, bool throwErrors, JsonValueList *result,
- bool useTz)
+executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
+ JsonValueList *result, bool useTz)
{
JsonPathExecContext cxt;
JsonPathExecResult res;
@@ -627,16 +549,22 @@ executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
if (!JsonbExtractScalar(&json->root, &jbv))
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.getVar = getVar;
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
cxt.ignoreStructuralErrors = cxt.laxMode;
cxt.root = &jbv;
cxt.current = &jbv;
cxt.baseObject.jbc = NULL;
cxt.baseObject.id = 0;
- /* 1 + number of base objects in vars */
- cxt.lastGeneratedObjectId = 1 + getVar(vars, NULL, 0, NULL, NULL);
+ cxt.lastGeneratedObjectId = vars ? 2 : 1;
cxt.innermostArraySize = -1;
cxt.throwErrors = throwErrors;
cxt.useTz = useTz;
@@ -2165,7 +2093,7 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
&value->val.string.len);
break;
case jpiVariable:
- getJsonPathVariable(cxt, item, value);
+ getJsonPathVariable(cxt, item, cxt->vars, value);
return;
default:
elog(ERROR, "unexpected jsonpath item type");
@@ -2177,63 +2105,42 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
*/
static void
getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
- JsonbValue *value)
+ Jsonb *vars, JsonbValue *value)
{
char *varName;
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 *v;
- if (!varName)
+ if (!vars)
{
- 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.")));
- }
-
- return vars ? 1 : 0; /* count of base objects */
+ value->type = jbvNull;
+ return;
}
+ Assert(variable->type == jpiVariable);
+ varName = jspGetString(variable, &varNameLength);
tmp.type = jbvString;
tmp.val.string.val = varName;
tmp.val.string.len = varNameLength;
v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
- if (!v)
- return -1;
-
- *value = *v;
- pfree(v);
+ if (v)
+ {
+ *value = *v;
+ pfree(v);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find jsonpath variable \"%s\"",
+ pnstrdup(varName, varNameLength))));
+ }
- JsonbInitBinary(baseObject, vars);
- return 1;
+ JsonbInitBinary(&tmp, vars);
+ setBaseObject(cxt, &tmp, 1);
}
/**************** Support functions for JsonPath execution *****************/
@@ -2523,13 +2430,6 @@ setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
}
static void
-JsonValueListClear(JsonValueList *jvl)
-{
- jvl->singleton = NULL;
- jvl->list = NULL;
-}
-
-static void
JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
{
if (jvl->singleton)
@@ -2897,667 +2797,3 @@ compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
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
-};