mirror of
https://github.com/postgres/postgres.git
synced 2025-07-21 16:02:15 +03:00
Revert SQL/JSON features
The reverts the following and makes some associated cleanups: commitf79b803dc
: Common SQL/JSON clauses commitf4fb45d15
: SQL/JSON constructors commit5f0adec25
: Make STRING an unreserved_keyword. commit33a377608
: IS JSON predicate commit1a36bc9db
: SQL/JSON query functions commit606948b05
: SQL JSON functions commit49082c2cc
: RETURNING clause for JSON() and JSON_SCALAR() commit4e34747c8
: JSON_TABLE commitfadb48b00
: PLAN clauses for JSON_TABLE commit2ef6f11b0
: Reduce running time of jsonb_sqljson test commit14d3f24fa
: Further improve jsonb_sqljson parallel test commita6baa4bad
: Documentation for SQL/JSON features commitb46bcf7a4
: Improve readability of SQL/JSON documentation. commit112fdb352
: Fix finalization for json_objectagg and friends commitfcdb35c32
: Fix transformJsonBehavior commit4cd8717af
: Improve a couple of sql/json error messages commitf7a605f63
: Small cleanups in SQL/JSON code commit9c3d25e17
: Fix JSON_OBJECTAGG uniquefying bug commita79153b7a
: Claim SQL standard compliance for SQL/JSON features commita1e7616d6
: Rework SQL/JSON documentation commit8d9f9634e
: Fix errors in copyfuncs/equalfuncs support for JSON node types. commit3c633f32b
: Only allow returning string types or bytea from json_serialize commit67b26703b
: 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:
@ -47,9 +47,6 @@
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/datum.h"
|
||||
#include "utils/json.h"
|
||||
#include "utils/jsonb.h"
|
||||
#include "utils/jsonpath.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/typcache.h"
|
||||
|
||||
@ -88,40 +85,6 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
|
||||
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
|
||||
*
|
||||
@ -159,7 +122,32 @@ ExecInitExprInternal(Expr *node, PlanState *parent, ParamListInfo ext_params,
|
||||
ExprState *
|
||||
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 *
|
||||
ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
|
||||
{
|
||||
return ExecInitExprInternal(node, NULL, ext_params, NULL, NULL);
|
||||
}
|
||||
ExprState *state;
|
||||
ExprEvalStep scratch = {0};
|
||||
|
||||
/*
|
||||
* ExecInitExprWithCaseValue: prepare an expression tree for execution
|
||||
*
|
||||
* This is the same as ExecInitExpr, except that a pointer to the value for
|
||||
* CasTestExpr is passed here.
|
||||
*/
|
||||
ExprState *
|
||||
ExecInitExprWithCaseValue(Expr *node, PlanState *parent,
|
||||
Datum *caseval, bool *casenull)
|
||||
{
|
||||
return ExecInitExprInternal(node, parent, NULL, caseval, casenull);
|
||||
/* 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 = NULL;
|
||||
state->ext_params = ext_params;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
|
@ -57,31 +57,22 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heaptoast.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/sequence.h"
|
||||
#include "executor/execExpr.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "pgstat.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/date.h"
|
||||
#include "utils/datum.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/memutils.h"
|
||||
#include "utils/resowner.h"
|
||||
#include "utils/timestamp.h"
|
||||
#include "utils/typcache.h"
|
||||
#include "utils/xml.h"
|
||||
@ -488,9 +479,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||
&&CASE_EEOP_GROUPING_FUNC,
|
||||
&&CASE_EEOP_WINDOW_FUNC,
|
||||
&&CASE_EEOP_SUBPLAN,
|
||||
&&CASE_EEOP_JSON_CONSTRUCTOR,
|
||||
&&CASE_EEOP_IS_JSON,
|
||||
&&CASE_EEOP_JSONEXPR,
|
||||
&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
|
||||
&&CASE_EEOP_AGG_DESERIALIZE,
|
||||
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
|
||||
@ -1824,27 +1812,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||
{
|
||||
/* too complex for an inline implementation */
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
*
|
||||
@ -4619,629 +4502,3 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans,
|
||||
|
||||
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 "nodes/execnodes.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/jsonpath.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/xml.h"
|
||||
@ -162,9 +161,8 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
|
||||
scanstate->ss.ps.qual =
|
||||
ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
|
||||
|
||||
/* Only XMLTABLE and JSON_TABLE are supported currently */
|
||||
scanstate->routine =
|
||||
tf->functype == TFT_XMLTABLE ? &XmlTableRoutine : &JsonbTableRoutine;
|
||||
/* Only XMLTABLE is supported currently */
|
||||
scanstate->routine = &XmlTableRoutine;
|
||||
|
||||
scanstate->perTableCxt =
|
||||
AllocSetContextCreate(CurrentMemoryContext,
|
||||
@ -383,17 +381,14 @@ tfuncInitialize(TableFuncScanState *tstate, ExprContext *econtext, Datum doc)
|
||||
routine->SetNamespace(tstate, ns_name, ns_uri);
|
||||
}
|
||||
|
||||
if (routine->SetRowFilter)
|
||||
{
|
||||
/* Install the row filter expression into the table builder context */
|
||||
value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
|
||||
if (isnull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||
errmsg("row filter expression must not be null")));
|
||||
/* Install the row filter expression into the table builder context */
|
||||
value = ExecEvalExpr(tstate->rowexpr, econtext, &isnull);
|
||||
if (isnull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
|
||||
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.
|
||||
|
Reference in New Issue
Block a user