diff options
Diffstat (limited to 'src/backend/executor/execExprInterp.c')
-rw-r--r-- | src/backend/executor/execExprInterp.c | 743 |
1 files changed, 0 insertions, 743 deletions
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 636794ca6f1..9b9bbf00a97 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -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; -} |