v43-0002-Many-fixes-and-refactoring.patch
application/octet-stream
Filename: v43-0002-Many-fixes-and-refactoring.patch
Type: application/octet-stream
Part: 0
Message:
Re: remaining sql/json patches
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v43-0002
Subject: Many fixes and refactoring
| File | + | − |
|---|---|---|
| doc/src/sgml/func.sgml | 2 | 12 |
| src/backend/executor/execExpr.c | 109 | 150 |
| src/backend/executor/execExprInterp.c | 107 | 148 |
| src/backend/jit/llvm/llvmjit_expr.c | 23 | 59 |
| src/backend/nodes/makefuncs.c | 2 | 4 |
| src/backend/nodes/nodeFuncs.c | 14 | 105 |
| src/backend/parser/gram.y | 2 | 2 |
| src/backend/parser/parse_expr.c | 143 | 251 |
| src/backend/parser/parse_target.c | 3 | 0 |
| src/backend/utils/adt/ruleutils.c | 3 | 0 |
| src/include/executor/execExpr.h | 2 | 3 |
| src/include/nodes/execnodes.h | 7 | 15 |
| src/include/nodes/makefuncs.h | 2 | 2 |
| src/include/nodes/primnodes.h | 15 | 76 |
| src/test/regress/expected/sqljson_queryfuncs.out | 35 | 27 |
| src/test/regress/sql/sqljson_queryfuncs.sql | 12 | 11 |
| src/tools/pgindent/typedefs.list | 0 | 2 |
From 93a0c1e15bf4956c76e8ba1ef20cb79f71073dc2 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Tue, 12 Mar 2024 13:20:34 +0900
Subject: [PATCH v43 2/3] Many fixes and refactoring
---
doc/src/sgml/func.sgml | 14 +-
src/backend/executor/execExpr.c | 259 +++++-------
src/backend/executor/execExprInterp.c | 255 +++++-------
src/backend/jit/llvm/llvmjit_expr.c | 82 +---
src/backend/nodes/makefuncs.c | 6 +-
src/backend/nodes/nodeFuncs.c | 119 +-----
src/backend/parser/gram.y | 4 +-
src/backend/parser/parse_expr.c | 394 +++++++-----------
src/backend/parser/parse_target.c | 3 +
src/backend/utils/adt/ruleutils.c | 3 +
src/include/executor/execExpr.h | 5 +-
src/include/nodes/execnodes.h | 22 +-
src/include/nodes/makefuncs.h | 4 +-
src/include/nodes/primnodes.h | 91 +---
.../regress/expected/sqljson_queryfuncs.out | 62 +--
src/test/regress/sql/sqljson_queryfuncs.sql | 23 +-
src/tools/pgindent/typedefs.list | 2 -
17 files changed, 481 insertions(+), 867 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 70fa1f6c69..754a52fb48 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18605,16 +18605,6 @@ $.* ? (@ like_regex "^\\d+$")
<replaceable>path_expression</replaceable> can contain.
</para>
- <note>
- <para>
- SQL/JSON query functions currently only accept values of the
- <type>jsonb</type> type, because the SQL/JSON path language only
- supports those, so it might be necessary to cast the
- <replaceable>context_item</replaceable> argument of these functions to
- <type>jsonb</type>.
- </para>
- </note>
-
<table id="functions-sqljson-querying">
<title>SQL/JSON Query Functions</title>
<tgroup cols="1">
@@ -18684,7 +18674,7 @@ ERROR: jsonpath array subscript is out of bounds
<optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>EMPTY</literal> { <optional> <literal>ARRAY</literal> </optional> | <literal>OBJECT</literal> } | <literal>DEFAULT</literal> <replaceable>expression</replaceable> } <literal>ON ERROR</literal> </optional>)
</para>
<para>
- Returns the result of applying the
+ Returns the result of applying the SQL/JSON
<replaceable>path_expression</replaceable> to the
<replaceable>context_item</replaceable> using the
<literal>PASSING</literal> <replaceable>value</replaceable>s.
@@ -18762,7 +18752,7 @@ DETAIL: Missing "]" after array dimensions.
<optional> { <literal>ERROR</literal> | <literal>NULL</literal> | <literal>DEFAULT</literal> <replaceable>expression</replaceable> } <literal>ON ERROR</literal> </optional>)
</para>
<para>
- Returns the result of applying the
+ Returns the result of applying the SQL/JSON
<replaceable>path_expression</replaceable> to the
<replaceable>context_item</replaceable> using the
<literal>PASSING</literal> <replaceable>value</replaceable>s.
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e5c92efd61..2269642934 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -88,12 +88,12 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
int transno, int setno, int setoff, bool ishash,
bool nullcheck);
-static void ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
+static void ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
Datum *resv, bool *resnull,
ExprEvalStep *scratch);
-static int ExecInitJsonExprCoercion(ExprState *state, Node *coercion,
- ErrorSaveContext *escontext,
- Datum *resv, bool *resnull);
+static void ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
+ ErrorSaveContext *escontext,
+ Datum *resv, bool *resnull);
/*
@@ -2434,9 +2434,9 @@ ExecInitExprRec(Expr *node, ExprState *state,
case T_JsonExpr:
{
- JsonExpr *jexpr = castNode(JsonExpr, node);
+ JsonExpr *jsexpr = castNode(JsonExpr, node);
- ExecInitJsonExpr(jexpr, state, resv, resnull, &scratch);
+ ExecInitJsonExpr(jsexpr, state, resv, resnull, &scratch);
break;
}
@@ -4213,26 +4213,28 @@ ExecBuildParamSetEqual(TupleDesc desc,
* Push steps to evaluate a JsonExpr and its various subsidiary expressions.
*/
static void
-ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
+ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
Datum *resv, bool *resnull,
ExprEvalStep *scratch)
{
JsonExprState *jsestate = palloc0(sizeof(JsonExprState));
+ ErrorSaveContext *escontext =
+ jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
+ &jsestate->escontext : NULL;
ListCell *argexprlc;
ListCell *argnamelc;
List *jumps_if_skip = NIL;
- List *jumps_to_coerce_finish = NIL;
List *jumps_to_end = NIL;
ListCell *lc;
ExprEvalStep *as;
- jsestate->jsexpr = jexpr;
+ jsestate->jsexpr = jsexpr;
/*
* Evaluate formatted_expr storing the result into
* jsestate->formatted_expr.
*/
- ExecInitExprRec((Expr *) jexpr->formatted_expr, state,
+ ExecInitExprRec((Expr *) jsexpr->formatted_expr, state,
&jsestate->formatted_expr.value,
&jsestate->formatted_expr.isnull);
@@ -4247,7 +4249,7 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
* Evaluate pathspec expression storing the result into
* jsestate->pathspec.
*/
- ExecInitExprRec((Expr *) jexpr->path_spec, state,
+ ExecInitExprRec((Expr *) jsexpr->path_spec, state,
&jsestate->pathspec.value,
&jsestate->pathspec.isnull);
@@ -4260,8 +4262,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
/* Steps to compute PASSING args. */
jsestate->args = NIL;
- forboth(argexprlc, jexpr->passing_values,
- argnamelc, jexpr->passing_names)
+ forboth(argexprlc, jsexpr->passing_values,
+ argnamelc, jsexpr->passing_names)
{
Expr *argexpr = (Expr *) lfirst(argexprlc);
String *argname = lfirst_node(String, argnamelc);
@@ -4300,8 +4302,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
scratch->d.constval.isnull = true;
ExprEvalPushStep(state, scratch);
- /* Jump to coerce the NULL using result_coercion if present. */
- if (jexpr->result_coercion)
+ /* Jump to coerce the NULL using coercion_expr if present. */
+ if (jsexpr->coercion_expr)
{
scratch->opcode = EEOP_JUMP;
scratch->d.jump.jumpdone = state->steps_len + 1;
@@ -4309,81 +4311,80 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
}
/*
- * Steps to coerce the result value computed by EEOP_JSONEXPR_PATH. To
- * handle coercion errors softly, use the following ErrorSaveContext when
- * initializing the coercion expressions, including any JsonCoercion
- * nodes.
+ * To handle coercion errors softly, use the following ErrorSaveContext
+ * to pass to ExecInitExprRec() when initializing the coercion expressions
+ * and in the EEOP_JSONEXPR_COERCION step.
*/
jsestate->escontext.type = T_ErrorSaveContext;
- if (jexpr->result_coercion)
+
+ /* Steps to coerce the result value computed by EEOP_JSONEXPR_PATH. */
+ jsestate->jump_eval_coercion_expr = -1;
+ if (jsexpr->coercion_expr)
{
- jsestate->jump_eval_result_coercion =
- ExecInitJsonExprCoercion(state, jexpr->result_coercion,
- jexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
- &jsestate->escontext : NULL,
- resv, resnull);
- /* Jump to COERCION_FINISH. */
- scratch->opcode = EEOP_JUMP;
- scratch->d.jump.jumpdone = -1; /* set below */
- jumps_to_coerce_finish = lappend_int(jumps_to_coerce_finish,
- state->steps_len);
- ExprEvalPushStep(state, scratch);
- }
- else
- jsestate->jump_eval_result_coercion = -1;
+ Datum *save_innermost_caseval;
+ bool *save_innermost_casenull;
+ ErrorSaveContext *save_escontext;
- /* Steps for coercing JsonItemType values returned by JsonPathValue(). */
- if (jexpr->item_coercions)
+ jsestate->jump_eval_coercion_expr = state->steps_len;
+ save_innermost_caseval = state->innermost_caseval;
+ save_innermost_casenull = state->innermost_casenull;
+ save_escontext = state->escontext;
+
+ state->innermost_caseval = resv;
+ state->innermost_casenull = resnull;
+ state->escontext = escontext;
+
+ ExecInitExprRec((Expr *) jsexpr->coercion_expr, state, resv, resnull);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+ state->escontext = save_escontext;
+ }
+ else if (jsexpr->use_json_coercion)
+ {
+ jsestate->jump_eval_coercion_expr = state->steps_len;
+ ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv, resnull);
+ }
+ else if (jsexpr->use_io_coercion)
{
/*
- * Here we create the steps for each JsonItemType type's coercion
- * expression and also store a flag whether the coercion is a cast
- * expression. ExecPrepareJsonItemCoercion() called by
- * ExecEvalJsonExprPath() will map a given JsonbValue returned by
- * JsonPathValue() to its JsonItemType's expression's step address
- * and the flag by indexing the following arrays with JsonItemType
- * enum value.
+ * Only initialize the FunctionCallInfo for the target type's input
+ * function, which is called by ExecEvalJsonExprPath() itself, so no
+ * additional step is necessary.
*/
- jsestate->num_item_coercions = list_length(jexpr->item_coercions);
- jsestate->eval_item_coercion_jumps = (int *)
- palloc(jsestate->num_item_coercions * sizeof(int));
- jsestate->item_coercion_is_cast = (bool *)
- palloc0(jsestate->num_item_coercions * sizeof(bool));
- foreach(lc, jexpr->item_coercions)
- {
- JsonItemCoercion *item_coercion = lfirst(lc);
- Node *coercion = item_coercion->coercion;
-
- jsestate->item_coercion_is_cast[item_coercion->item_type] =
- (coercion != NULL && !IsA(coercion, JsonCoercion));
- jsestate->eval_item_coercion_jumps[item_coercion->item_type] =
- ExecInitJsonExprCoercion(state, coercion,
- jexpr->on_error->btype != JSON_BEHAVIOR_ERROR ?
- &jsestate->escontext : NULL,
- resv, resnull);
-
- /* Jump to COERCION_FINISH. */
- scratch->opcode = EEOP_JUMP;
- scratch->d.jump.jumpdone = -1; /* set below */
- jumps_to_coerce_finish = lappend_int(jumps_to_coerce_finish,
- state->steps_len);
- ExprEvalPushStep(state, scratch);
- }
+ Oid typinput;
+ Oid typioparam;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+
+ getTypeInputInfo(jsexpr->returning->typid, &typinput, &typioparam);
+ finfo = palloc0(sizeof(FmgrInfo));
+ fcinfo = palloc0(SizeForFunctionCallInfo(3));
+ fmgr_info(typinput, finfo);
+ fmgr_info_set_expr((Node *) jsexpr->returning, finfo);
+ InitFunctionCallInfoData(*fcinfo, finfo, 3, InvalidOid, NULL, NULL);
+
+ /*
+ * We can preload the second and third arguments for the input
+ * function, since they're constants.
+ */
+ fcinfo->args[1].value = ObjectIdGetDatum(typioparam);
+ fcinfo->args[1].isnull = false;
+ fcinfo->args[2].value = Int32GetDatum(jsexpr->returning->typmod);
+ fcinfo->args[2].isnull = false;
+ fcinfo->context = (Node *) escontext;
+
+ jsestate->input_finfo = finfo;
+ jsestate->input_fcinfo = fcinfo;
}
/*
- * Add step to reset the ErrorSaveContext and set JsonExprState.error if
- * the coercion evaluation ran into an error but was not thrown because of
- * the ON ERROR behavior.
+ * Add a special step to check if the coercion evaluation ran into an
+ * error but was not thrown because of the ON ERROR behavior and set
+ * JsonExprState.error if so.
*/
- if (jexpr->result_coercion || jexpr->item_coercions)
+ if (jsestate->jump_eval_coercion_expr >= 0)
{
- foreach(lc, jumps_to_coerce_finish)
- {
- as = &state->steps[lfirst_int(lc)];
- as->d.jump.jumpdone = state->steps_len;
- }
-
scratch->opcode = EEOP_JSONEXPR_COERCION_FINISH;
scratch->d.jsonexpr.jsestate = jsestate;
ExprEvalPushStep(state, scratch);
@@ -4396,8 +4397,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
* occur during EEOP_JSONEXPR_PATH evaluation and subsequent coercion
* evaluation.
*/
- if (jexpr->on_error &&
- jexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
+ if (jsexpr->on_error &&
+ jsexpr->on_error->btype != JSON_BEHAVIOR_ERROR)
{
jsestate->jump_error = state->steps_len;
scratch->opcode = EEOP_JUMP_IF_NOT_TRUE;
@@ -4413,14 +4414,13 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
ExprEvalPushStep(state, scratch);
/* Steps to evaluate the ON ERROR expression */
- ExecInitExprRec((Expr *) jexpr->on_error->expr,
+ ExecInitExprRec((Expr *) jsexpr->on_error->expr,
state, resv, resnull);
/* Steps to coerce the ON ERROR expression if needed */
- (void) ExecInitJsonExprCoercion(state,
- (Node *) jexpr->on_error->coercion,
- &jsestate->escontext,
- resv, resnull);
+ if (jsexpr->on_error->coerce)
+ ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv,
+ resnull);
jumps_to_end = lappend_int(jumps_to_end, state->steps_len);
scratch->opcode = EEOP_JUMP;
@@ -4429,8 +4429,8 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
}
/* Step to handle ON EMPTY behaviors. */
- if (jexpr->on_empty != NULL &&
- jexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
+ if (jsexpr->on_empty != NULL &&
+ jsexpr->on_empty->btype != JSON_BEHAVIOR_ERROR)
{
jsestate->jump_empty = state->steps_len;
@@ -4442,14 +4442,13 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
ExprEvalPushStep(state, scratch);
/* Steps to evaluate the ON EMPTY expression */
- ExecInitExprRec((Expr *) jexpr->on_empty->expr,
+ ExecInitExprRec((Expr *) jsexpr->on_empty->expr,
state, resv, resnull);
/* Steps to coerce the ON EMPTY expression if needed */
- (void) ExecInitJsonExprCoercion(state,
- (Node *) jexpr->on_empty->coercion,
- &jsestate->escontext,
- resv, resnull);
+ if (jsexpr->on_empty->coerce)
+ ExecInitJsonCoercion(state, jsexpr->returning, escontext, resv,
+ resnull);
scratch->opcode = EEOP_JUMP;
scratch->d.jump.jumpdone = -1; /* set below */
@@ -4466,64 +4465,24 @@ ExecInitJsonExpr(JsonExpr *jexpr, ExprState *state,
jsestate->jump_end = state->steps_len;
}
-/* Initialize one JsonCoercion for execution. */
-static int
-ExecInitJsonExprCoercion(ExprState *state, Node *coercion_expr,
- ErrorSaveContext *escontext,
- Datum *resv, bool *resnull)
+/*
+ * Initialize a EEOP_JSONEXPR_COERCION step to coerce the value given in resv
+ * to the given RETURNING type.
+ */
+static void
+ExecInitJsonCoercion(ExprState *state, JsonReturning *returning,
+ ErrorSaveContext *escontext,
+ Datum *resv, bool *resnull)
{
- int jump_eval_coercion;
- Datum *save_innermost_caseval;
- bool *save_innermost_casenull;
- ErrorSaveContext *save_escontext;
-
- if (coercion_expr == NULL)
- return -1;
-
- jump_eval_coercion = state->steps_len;
- if (IsA(coercion_expr, JsonCoercion))
- {
- JsonCoercion *coercion = (JsonCoercion *) coercion_expr;
- ExprEvalStep scratch = {0};
- Oid typinput;
- FmgrInfo *finfo;
- Oid typioparam;
-
- getTypeInputInfo(((JsonCoercion *) coercion)->targettype,
- &typinput, &typioparam);
- finfo = palloc0(sizeof(FmgrInfo));
- fmgr_info(typinput, finfo);
-
- scratch.opcode = EEOP_JSONEXPR_COERCION;
- scratch.resvalue = resv;
- scratch.resnull = resnull;
- scratch.d.jsonexpr_coercion.coercion = coercion;
- scratch.d.jsonexpr_coercion.input_finfo = finfo;
- scratch.d.jsonexpr_coercion.typioparam = typioparam;
- scratch.d.jsonexpr_coercion.json_populate_type_cache = NULL;
- scratch.d.jsonexpr_coercion.escontext = escontext;
- ExprEvalPushStep(state, &scratch);
- /* Initialize the cast expression below, if any. */
- if (coercion->cast_expr != NULL)
- coercion_expr = coercion->cast_expr;
- else
- return jump_eval_coercion;
- }
-
- /* Push step(s) to compute cstate->coercion. */
- save_innermost_caseval = state->innermost_caseval;
- save_innermost_casenull = state->innermost_casenull;
- save_escontext = state->escontext;
-
- state->innermost_caseval = resv;
- state->innermost_casenull = resnull;
- state->escontext = escontext;
-
- ExecInitExprRec((Expr *) coercion_expr, state, resv, resnull);
-
- state->innermost_caseval = save_innermost_caseval;
- state->innermost_casenull = save_innermost_casenull;
- state->escontext = save_escontext;
+ ExprEvalStep scratch = {0};
- return jump_eval_coercion;
+ /* For json_populate_type() */
+ scratch.opcode = EEOP_JSONEXPR_COERCION;
+ scratch.resvalue = resv;
+ scratch.resnull = resnull;
+ scratch.d.jsonexpr_coercion.targettype = returning->typid;
+ scratch.d.jsonexpr_coercion.targettypmod = returning->typmod;
+ scratch.d.jsonexpr_coercion.json_populate_type_cache = NULL;
+ scratch.d.jsonexpr_coercion.escontext = escontext;
+ ExprEvalPushStep(state, &scratch);
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 2d6b11bb34..dea3006dec 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -180,10 +180,7 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
AggStatePerGroup pergroup,
ExprContext *aggcontext,
int setno);
-static void ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
- bool throw_error,
- int *jump_eval_item_coercion,
- Datum *resvalue, bool *resnull);
+static char *ExecGetJsonValueItemString(JsonbValue *item, bool *resnull);
/*
* ScalarArrayOpExprHashEntry
@@ -4269,15 +4266,14 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
ExprContext *econtext)
{
JsonExprState *jsestate = op->d.jsonexpr.jsestate;
- JsonExpr *jexpr = jsestate->jsexpr;
+ JsonExpr *jsexpr = jsestate->jsexpr;
Datum item;
JsonPath *path;
- bool throw_error = jexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
+ bool throw_error = jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR;
bool error = false,
empty = false;
-
- /* Might get overridden for JSON_VALUE_OP by an per-item coercion. */
- int jump_eval_coercion = jsestate->jump_eval_result_coercion;
+ int jump_eval_coercion = jsestate->jump_eval_coercion_expr;
+ char *val_string = NULL;
item = jsestate->formatted_expr.value;
path = DatumGetJsonPathP(jsestate->pathspec.value);
@@ -4285,7 +4281,8 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
/* Set error/empty to false. */
memset(&jsestate->error, 0, sizeof(NullableDatum));
memset(&jsestate->empty, 0, sizeof(NullableDatum));
- switch (jexpr->op)
+ jsestate->escontext.error_occurred = false;
+ switch (jsexpr->op)
{
case JSON_EXISTS_OP:
{
@@ -4302,12 +4299,27 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
break;
case JSON_QUERY_OP:
- *op->resvalue = JsonPathQuery(item, path, jexpr->wrapper, &empty,
+ *op->resvalue = JsonPathQuery(item, path, jsexpr->wrapper, &empty,
!throw_error ? &error : NULL,
jsestate->args);
- if (!error && !empty)
- *op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
+ *op->resnull = (DatumGetPointer(*op->resvalue) == NULL);
+
+ /* Handle OMIT QUOTES. */
+ if (!*op->resnull && jsexpr->omit_quotes)
+ {
+ val_string = JsonbUnquote(DatumGetJsonbP(*op->resvalue));
+
+ /*
+ * Pass the string as a text value to the cast expression
+ * if any. If not, use the input function call below to
+ * do the coercion.
+ */
+ if (jump_eval_coercion >= 0)
+ *op->resvalue =
+ DirectFunctionCall1(textin,
+ PointerGetDatum(val_string));
+ }
break;
case JSON_VALUE_OP:
@@ -4318,47 +4330,69 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
if (jbv == NULL)
{
- /* Will be coerced with result_coercion. */
+ /* Will be coerced with coercion_expr. */
*op->resvalue = (Datum) 0;
*op->resnull = true;
}
else if (!error && !empty)
{
- /*
- * If the requested output type is json(b), use
- * result_coercion to do the coercion.
- */
- if (jexpr->returning->typid == JSONOID ||
- jexpr->returning->typid == JSONBOID)
+ if (jsexpr->returning->typid == JSONOID ||
+ jsexpr->returning->typid == JSONBOID)
{
- *op->resvalue = JsonbPGetDatum(JsonbValueToJsonb(jbv));
- *op->resnull = false;
+ val_string = DatumGetCString(DirectFunctionCall1(jsonb_out,
+ JsonbPGetDatum(JsonbValueToJsonb(jbv))));
}
else
{
+ val_string = ExecGetJsonValueItemString(jbv, op->resnull);
+
/*
- * Else, use one of the item_coercions.
- *
- * Error out if no cast expression exists.
+ * Pass the string as a text value to the cast expression
+ * if any. If not, use the input function call below to
+ * do the coercion.
*/
- ExecPrepareJsonItemCoercion(jbv, jsestate, throw_error,
- &jump_eval_coercion,
- op->resvalue, op->resnull);
+ *op->resvalue = PointerGetDatum(val_string);
+ if (jump_eval_coercion >= 0)
+ *op->resvalue = DirectFunctionCall1(textin, *op->resvalue);
}
}
break;
}
default:
- elog(ERROR, "unrecognized SQL/JSON expression op %d", jexpr->op);
+ elog(ERROR, "unrecognized SQL/JSON expression op %d",
+ (int) jsexpr->op);
return false;
}
+ /*
+ * Coerce the result value by calling the input function coercion.
+ * *op->resvalue must point to C string in this case.
+ */
+ if (!*op->resnull && jsexpr->use_io_coercion)
+ {
+ FunctionCallInfo fcinfo;
+
+ fcinfo = jsestate->input_fcinfo;
+ Assert(fcinfo != NULL);
+ Assert(val_string != NULL);
+ fcinfo->args[0].value = PointerGetDatum(val_string);
+ fcinfo->args[0].isnull = *op->resnull;
+ /* second and third arguments are already set up */
+
+ fcinfo->isnull = false;
+ *op->resvalue = FunctionCallInvoke(fcinfo);
+ if (SOFT_ERROR_OCCURRED(&jsestate->escontext))
+ error = true;
+
+ jump_eval_coercion = -1;
+ }
+
if (empty)
{
- if (jexpr->on_empty)
+ if (jsexpr->on_empty)
{
- if (jexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
+ if (jsexpr->on_empty->btype == JSON_BEHAVIOR_ERROR)
ereport(ERROR,
errcode(ERRCODE_NO_SQL_JSON_ITEM),
errmsg("no SQL/JSON item"));
@@ -4368,7 +4402,7 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
Assert(jsestate->jump_empty >= 0);
return jsestate->jump_empty;
}
- else if (jexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
+ else if (jsexpr->on_error->btype == JSON_BEHAVIOR_ERROR)
ereport(ERROR,
errcode(ERRCODE_NO_SQL_JSON_ITEM),
errmsg("no SQL/JSON item"));
@@ -4396,31 +4430,18 @@ ExecEvalJsonExprPath(ExprState *state, ExprEvalStep *op,
}
/*
- * Selects a coercion for a given JsonbValue based on its type.
- *
- * On return, *resvalue and *resnull are set to the value extracted from the
- * JsonbValue and *jump_eval_item_coercion is set to the step address of the
- * coercion expression.
+ * Convert the a given JsonbValue to its C string representation
*
- * If the found expression is a JsonCoercion node that means the parser
- * didnt' find a cast to do the coercion, so throw an error if the
- * ON ERROR behavior says to do so.
+ * Returns the string as a Datum setting *resnull if the JsonbValue is a
+ * a jbvNull.
*/
-static void
-ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
- bool throw_error,
- int *jump_eval_item_coercion,
- Datum *resvalue, bool *resnull)
+static char *
+ExecGetJsonValueItemString(JsonbValue *item, bool *resnull)
{
- int *eval_item_coercion_jumps = jsestate->eval_item_coercion_jumps;
- bool *item_coercion_is_cast = jsestate->item_coercion_is_cast;
- bool is_cast;
- int jump_to;
- JsonbValue buf;
-
if (item->type == jbvBinary && JsonContainerIsScalar(item->val.binary.data))
{
bool is_scalar PG_USED_FOR_ASSERTS_ONLY;
+ JsonbValue buf;
is_scalar = JsonbExtractScalar(item->val.binary.data, &buf);
item = &buf;
@@ -4433,56 +4454,44 @@ ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
switch (item->type)
{
case jbvNull:
- is_cast = item_coercion_is_cast[JsonItemTypeNull];
- jump_to = eval_item_coercion_jumps[JsonItemTypeNull];
- *resvalue = (Datum) 0;
*resnull = true;
- break;
+ return NULL;
case jbvString:
- is_cast = item_coercion_is_cast[JsonItemTypeString];
- jump_to = eval_item_coercion_jumps[JsonItemTypeString];
- *resvalue =
- PointerGetDatum(cstring_to_text_with_len(item->val.string.val,
- item->val.string.len));
- break;
+ {
+ char *str = palloc(item->val.string.len + 1);
+
+ memcpy(str, item->val.string.val, item->val.string.len);
+ str[item->val.string.len] = '\0';
+ return str;
+ }
case jbvNumeric:
- is_cast = item_coercion_is_cast[JsonItemTypeNumeric];
- jump_to = eval_item_coercion_jumps[JsonItemTypeNumeric];
- *resvalue = NumericGetDatum(item->val.numeric);
- break;
+ return DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(item->val.numeric)));
case jbvBool:
- is_cast = item_coercion_is_cast[JsonItemTypeBoolean];
- jump_to = eval_item_coercion_jumps[JsonItemTypeBoolean];
- *resvalue = BoolGetDatum(item->val.boolean);
- break;
+ return DatumGetCString(DirectFunctionCall1(boolout,
+ BoolGetDatum(item->val.boolean)));
case jbvDatetime:
- *resvalue = item->val.datetime.value;
switch (item->val.datetime.typid)
{
case DATEOID:
- is_cast = item_coercion_is_cast[JsonItemTypeDate];
- jump_to = eval_item_coercion_jumps[JsonItemTypeDate];
- break;
+ return DatumGetCString(DirectFunctionCall1(date_out,
+ item->val.datetime.value));
case TIMEOID:
- is_cast = item_coercion_is_cast[JsonItemTypeTime];
- jump_to = eval_item_coercion_jumps[JsonItemTypeTime];
- break;
+ return DatumGetCString(DirectFunctionCall1(time_out,
+ item->val.datetime.value));
case TIMETZOID:
- is_cast = item_coercion_is_cast[JsonItemTypeTimetz];
- jump_to = eval_item_coercion_jumps[JsonItemTypeTimetz];
- break;
+ return DatumGetCString(DirectFunctionCall1(timetz_out,
+ item->val.datetime.value));
case TIMESTAMPOID:
- is_cast = item_coercion_is_cast[JsonItemTypeTimestamp];
- jump_to = eval_item_coercion_jumps[JsonItemTypeTimestamp];
- break;
+ return DatumGetCString(DirectFunctionCall1(timestamp_out,
+ item->val.datetime.value));
case TIMESTAMPTZOID:
- is_cast = item_coercion_is_cast[JsonItemTypeTimestamptz];
- jump_to = eval_item_coercion_jumps[JsonItemTypeTimestamptz];
- break;
+ return DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ item->val.datetime.value));
default:
elog(ERROR, "unexpected jsonb datetime type oid %u",
item->val.datetime.typid);
@@ -4492,37 +4501,21 @@ ExecPrepareJsonItemCoercion(JsonbValue *item, JsonExprState *jsestate,
case jbvArray:
case jbvObject:
case jbvBinary:
- is_cast = item_coercion_is_cast[JsonItemTypeComposite];
- jump_to = eval_item_coercion_jumps[JsonItemTypeComposite];
- *resvalue = JsonbPGetDatum(JsonbValueToJsonb(item));
- break;
+ return DatumGetCString(DirectFunctionCall1(jsonb_out,
+ JsonbPGetDatum(JsonbValueToJsonb(item))));
default:
elog(ERROR, "unexpected jsonb value type %d", item->type);
}
- /* If the expression is not a cast expression, throw an error. */
- if (jump_to >= 0 && !is_cast)
- {
- if (throw_error)
- ereport(ERROR,
- errcode(ERRCODE_SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE),
- errmsg("SQL/JSON item cannot be cast to target type"));
-
- *resvalue = (Datum) 0;
- *resnull = true;
- }
-
- *jump_eval_item_coercion = jump_to;
+ Assert(false);
+ *resnull = true;
+ return NULL;
}
/*
- * Coerce a jsonb or a scalar string value produced by ExecEvalJsonExprPath()
- * or an ON ERROR / EMPTY behavior expression to the target type.
- *
- * This is also responsible for removing any quotes present in the source
- * value if JsonCoercion.omit_quotes is true before coercing to the target
- * type.
+ * Coerce a jsonb value produced by ExecEvalJsonExprPath() or an ON ERROR /
+ * ON EMPTY behavior expression to the target type.
*
* Any soft errors that occur here will be checked by
* EEOP_JSONEXPR_COERCION_FINISH that will run after this.
@@ -4531,48 +4524,14 @@ void
ExecEvalJsonCoercion(ExprState *state, ExprEvalStep *op,
ExprContext *econtext)
{
- JsonCoercion *coercion = op->d.jsonexpr_coercion.coercion;
ErrorSaveContext *escontext = op->d.jsonexpr_coercion.escontext;
- Datum res = *op->resvalue;
- /*
- * Handle OMIT QUOTES.
- *
- * Normally, json_populate_type() is the place to coerce jsonb values to
- * the requested target type, including those that are scalar strings, but
- * it doesn't have the support for removing quotes to implement the
- * OMIT QUOTES clause, so we handle it here.
- */
- if (coercion->omit_quotes)
- {
- FmgrInfo *input_finfo = op->d.jsonexpr_coercion.input_finfo;
- Oid typioparam = op->d.jsonexpr_coercion.typioparam;
- char *val = !*op->resnull ?
- JsonbUnquote(DatumGetJsonbP(res)) : NULL;
-
- /*
- * If the coercion must be done using a cast expression, pass to it
- * the text version of the quote-stripped string. If not, finish the
- * coercion by calling the input function.
- */
- if (coercion->cast_expr)
- *op->resvalue = DirectFunctionCall1(textin,
- CStringGetDatum(val));
- else
- (void) InputFunctionCallSafe(input_finfo, val, typioparam,
- coercion->targettypmod,
- (Node *) escontext,
- op->resvalue);
- }
- else
- {
- *op->resvalue = json_populate_type(res, JSONBOID,
- coercion->targettype,
- coercion->targettypmod,
- &op->d.jsonexpr_coercion.json_populate_type_cache,
- econtext->ecxt_per_query_memory,
- op->resnull, (Node *) escontext);
- }
+ *op->resvalue = json_populate_type(*op->resvalue, JSONBOID,
+ op->d.jsonexpr_coercion.targettype,
+ op->d.jsonexpr_coercion.targettypmod,
+ &op->d.jsonexpr_coercion.json_populate_type_cache,
+ econtext->ecxt_per_query_memory,
+ op->resnull, (Node *) escontext);
}
/*
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 0c4d5953dc..2c49100259 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1950,10 +1950,8 @@ llvm_compile_expr(ExprState *state)
*/
if (jsestate->jump_empty >= 0 ||
jsestate->jump_error >= 0 ||
- jsestate->jump_eval_result_coercion >= 0 ||
- jsestate->num_item_coercions > 0)
+ jsestate->jump_eval_coercion_expr >= 0)
{
- int i;
LLVMValueRef v_jump_empty;
LLVMValueRef v_jump_error;
LLVMValueRef v_jump_coercion;
@@ -1961,8 +1959,7 @@ llvm_compile_expr(ExprState *state)
LLVMBasicBlockRef b_done,
b_empty,
b_error,
- b_result_coercion,
- *b_item_coercions = NULL;
+ b_coercion_expr;
b_empty =
l_bb_before_v(opblocks[opno + 1],
@@ -1970,21 +1967,9 @@ llvm_compile_expr(ExprState *state)
b_error =
l_bb_before_v(opblocks[opno + 1],
"op.%d.jsonexpr_error", opno);
- b_result_coercion =
+ b_coercion_expr =
l_bb_before_v(opblocks[opno + 1],
- "op.%d.jsonexpr_result_coercion", opno);
- if (jsestate->num_item_coercions > 0)
- {
- b_item_coercions = palloc(sizeof(LLVMBasicBlockRef) *
- jsestate->num_item_coercions);
- for (i = 0; i < jsestate->num_item_coercions; i++)
- {
- b_item_coercions[i] =
- l_bb_before_v(opblocks[opno + 1],
- "op.%d.jsonexpr_item_coercion.%d",
- opno, i);
- }
- }
+ "op.%d.jsonexpr_coercion_expr", opno);
b_done =
l_bb_before_v(opblocks[opno + 1],
"op.%d.jsonexpr_done", opno);
@@ -1992,66 +1977,45 @@ llvm_compile_expr(ExprState *state)
v_switch = LLVMBuildSwitch(b,
v_ret,
b_done,
- jsestate->num_item_coercions + 3);
+ 3);
/* Returned jsestate->jump_empty? */
if (jsestate->jump_empty >= 0)
{
v_jump_empty = l_int32_const(lc, jsestate->jump_empty);
LLVMAddCase(v_switch, v_jump_empty, b_empty);
}
- /* Returned jsestate->jump_error? */
- if (jsestate->jump_error >= 0)
- {
- v_jump_error = l_int32_const(lc, jsestate->jump_error);
- LLVMAddCase(v_switch, v_jump_error, b_error);
- }
- /* Returned jsestate->jump_eval_result_coercion? */
- if (jsestate->jump_eval_result_coercion >= 0)
- {
- v_jump_coercion = l_int32_const(lc, jsestate->jump_eval_result_coercion);
- LLVMAddCase(v_switch, v_jump_coercion, b_result_coercion);
- }
-
- /*
- * Returned one of
- * jsestate->eval_item_coercion_jumps[]?
- */
- for (i = 0; i < jsestate->num_item_coercions; i++)
- {
- if (jsestate->eval_item_coercion_jumps[i] >= 0)
- {
- v_jump_coercion = l_int32_const(lc, jsestate->eval_item_coercion_jumps[i]);
- LLVMAddCase(v_switch, v_jump_coercion, b_item_coercions[i]);
- }
- }
-
/* ON EMPTY code */
LLVMPositionBuilderAtEnd(b, b_empty);
if (jsestate->jump_empty >= 0)
LLVMBuildBr(b, opblocks[jsestate->jump_empty]);
else
LLVMBuildUnreachable(b);
+
+ /* Returned jsestate->jump_error? */
+ if (jsestate->jump_error >= 0)
+ {
+ v_jump_error = l_int32_const(lc, jsestate->jump_error);
+ LLVMAddCase(v_switch, v_jump_error, b_error);
+ }
/* ON ERROR code */
LLVMPositionBuilderAtEnd(b, b_error);
if (jsestate->jump_error >= 0)
LLVMBuildBr(b, opblocks[jsestate->jump_error]);
else
LLVMBuildUnreachable(b);
- /* result_coercion code */
- LLVMPositionBuilderAtEnd(b, b_result_coercion);
- if (jsestate->jump_eval_result_coercion >= 0)
- LLVMBuildBr(b, opblocks[jsestate->jump_eval_result_coercion]);
- else
- LLVMBuildUnreachable(b);
- /* item coercion code blocks */
- for (i = 0; i < jsestate->num_item_coercions; i++)
+
+ /* Returned jsestate->jump_eval_coercion_expr? */
+ if (jsestate->jump_eval_coercion_expr >= 0)
{
- LLVMPositionBuilderAtEnd(b, b_item_coercions[i]);
- if (jsestate->eval_item_coercion_jumps[i] >= 0)
- LLVMBuildBr(b, opblocks[jsestate->eval_item_coercion_jumps[i]]);
- else
- LLVMBuildUnreachable(b);
+ v_jump_coercion = l_int32_const(lc, jsestate->jump_eval_coercion_expr);
+ LLVMAddCase(v_switch, v_jump_coercion, b_coercion_expr);
}
+ /* coercion_expr code */
+ LLVMPositionBuilderAtEnd(b, b_coercion_expr);
+ if (jsestate->jump_eval_coercion_expr >= 0)
+ LLVMBuildBr(b, opblocks[jsestate->jump_eval_coercion_expr]);
+ else
+ LLVMBuildUnreachable(b);
LLVMPositionBuilderAtEnd(b, b_done);
}
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 538ccb30aa..b13cfa4201 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -861,14 +861,12 @@ makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr,
* creates a JsonBehavior node
*/
JsonBehavior *
-makeJsonBehavior(JsonBehaviorType type, Node *expr, JsonCoercion *coercion,
- int location)
+makeJsonBehavior(JsonBehaviorType btype, Node *expr, int location)
{
JsonBehavior *behavior = makeNode(JsonBehavior);
- behavior->btype = type;
+ behavior->btype = btype;
behavior->expr = expr;
- behavior->coercion = coercion;
behavior->location = location;
return behavior;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index d794192fae..aefdcdb9ec 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -243,24 +243,11 @@ exprType(const Node *expr)
type = jexpr->returning->typid;
break;
}
- case T_JsonCoercion:
- {
- const JsonCoercion *coercion = (const JsonCoercion *) expr;
-
- type = coercion->targettype;
- break;
- }
- case T_JsonItemCoercion:
- type = exprType(((JsonItemCoercion *) expr)->coercion);
- break;
case T_JsonBehavior:
{
const JsonBehavior *behavior = (const JsonBehavior *) expr;
- if (behavior->coercion)
- type = exprType((Node *) behavior->coercion);
- else
- type = exprType(behavior->expr);
+ type = exprType(behavior->expr);
break;
}
case T_NullTest:
@@ -527,23 +514,11 @@ exprTypmod(const Node *expr)
return jexpr->returning->typmod;
}
break;
- case T_JsonCoercion:
- {
- const JsonCoercion *coercion = (const JsonCoercion *) expr;
-
- return coercion->targettypmod;
- }
- break;
- case T_JsonItemCoercion:
- return exprTypmod(((JsonItemCoercion *) expr)->coercion);
case T_JsonBehavior:
{
const JsonBehavior *behavior = (const JsonBehavior *) expr;
- if (behavior->coercion)
- return exprTypmod((Node *) behavior->coercion);
- else
- return exprTypmod(behavior->expr);
+ return exprTypmod(behavior->expr);
}
break;
case T_CoerceToDomain:
@@ -1026,21 +1001,16 @@ exprCollation(const Node *expr)
coll = InvalidOid; /* ... so it has no collation */
break;
case T_JsonExpr:
- coll = exprCollation(((JsonExpr *) expr)->result_coercion);
- break;
- case T_JsonCoercion:
- coll = ((const JsonCoercion *) expr)->collation;
- break;
- case T_JsonItemCoercion:
- coll = exprCollation(((JsonItemCoercion *) expr)->coercion);
+ if (((JsonExpr *) expr)->coercion_expr)
+ coll = exprCollation(((JsonExpr *) expr)->coercion_expr);
+ else
+ coll = ((JsonExpr *) expr)->collation;
break;
case T_JsonBehavior:
{
JsonBehavior *behavior = (JsonBehavior *) expr;
- if (behavior->coercion)
- coll = exprCollation((Node *) behavior->coercion);
- else if (behavior->expr)
+ if (behavior->expr)
coll = exprCollation(behavior->expr);
else
coll = InvalidOid;
@@ -1289,28 +1259,10 @@ exprSetCollation(Node *expr, Oid collation)
{
JsonExpr *jexpr = (JsonExpr *) expr;
- if (jexpr->result_coercion)
- exprSetCollation((Node *) jexpr->result_coercion, collation);
+ if (jexpr->coercion_expr)
+ exprSetCollation((Node *) jexpr->coercion_expr, collation);
else
- Assert(!OidIsValid(collation)); /* result is always a
- * json[b] type */
- }
- break;
- case T_JsonItemCoercion:
- {
- JsonItemCoercion *item_coercion = (JsonItemCoercion *) expr;
-
- if (item_coercion->coercion)
- exprSetCollation(item_coercion->coercion, collation);
- }
- break;
- case T_JsonCoercion:
- {
- JsonCoercion *coercion = (JsonCoercion *) expr;
-
- if (coercion->cast_expr)
- exprSetCollation(coercion->cast_expr, collation);
- coercion->collation = collation;
+ jexpr->collation = collation;
}
break;
case T_JsonBehavior:
@@ -1319,8 +1271,6 @@ exprSetCollation(Node *expr, Oid collation)
if (behavior->expr)
exprSetCollation(behavior->expr, collation);
- if (behavior->coercion)
- exprSetCollation((Node *) behavior->coercion, collation);
}
break;
case T_NullTest:
@@ -2400,9 +2350,9 @@ expression_tree_walker_impl(Node *node,
if (WALK(jexpr->formatted_expr))
return true;
- if (WALK(jexpr->result_coercion))
+ if (WALK(jexpr->path_spec))
return true;
- if (WALK(jexpr->item_coercions))
+ if (WALK(jexpr->coercion_expr))
return true;
if (WALK(jexpr->passing_values))
return true;
@@ -2413,30 +2363,12 @@ expression_tree_walker_impl(Node *node,
return true;
}
break;
- case T_JsonCoercion:
- {
- JsonCoercion *coercion = (JsonCoercion *) node;
-
- if (WALK(coercion->cast_expr))
- return true;
- }
- break;
- case T_JsonItemCoercion:
- {
- JsonItemCoercion *item_coercion = (JsonItemCoercion *) node;
-
- if (WALK(item_coercion->coercion))
- return true;
- }
- break;
case T_JsonBehavior:
{
JsonBehavior *behavior = (JsonBehavior *) node;
if (WALK(behavior->expr))
return true;
- if (WALK(behavior->coercion))
- return true;
}
break;
case T_NullTest:
@@ -3449,10 +3381,9 @@ expression_tree_mutator_impl(Node *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, Node *);
- MUTATE(newnode->item_coercions, jexpr->item_coercions, List *);
+ MUTATE(newnode->path_spec, jexpr->path_spec, Node *);
+ MUTATE(newnode->coercion_expr, jexpr->coercion_expr, Node *);
MUTATE(newnode->passing_values, jexpr->passing_values, List *);
/* assume mutator does not care about passing_names */
MUTATE(newnode->on_empty, jexpr->on_empty, JsonBehavior *);
@@ -3460,25 +3391,6 @@ expression_tree_mutator_impl(Node *node,
return (Node *) newnode;
}
break;
- case T_JsonCoercion:
- {
- JsonCoercion *coercion = (JsonCoercion *) node;
- JsonCoercion *newnode;
-
- FLATCOPY(newnode, coercion, JsonCoercion);
- MUTATE(newnode->cast_expr, coercion->cast_expr, Node *);
- return (Node *) newnode;
- }
- case T_JsonItemCoercion:
- {
- JsonItemCoercion *item_coercion = (JsonItemCoercion *) node;
- JsonItemCoercion *newnode;
-
- FLATCOPY(newnode, item_coercion, JsonItemCoercion);
- MUTATE(newnode->coercion, item_coercion->coercion, Node *);
- return (Node *) newnode;
- }
- break;
case T_JsonBehavior:
{
JsonBehavior *behavior = (JsonBehavior *) node;
@@ -3486,7 +3398,6 @@ expression_tree_mutator_impl(Node *node,
FLATCOPY(newnode, behavior, JsonBehavior);
MUTATE(newnode->expr, behavior->expr, Node *);
- MUTATE(newnode->coercion, behavior->coercion, JsonCoercion *);
return (Node *) newnode;
}
break;
@@ -4205,8 +4116,6 @@ raw_expression_tree_walker_impl(Node *node,
if (WALK(jb->expr))
return true;
- if (WALK(jb->coercion))
- return true;
}
break;
case T_NullTest:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 91354b8178..c247eefb0c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -16632,9 +16632,9 @@ json_wrapper_behavior:
json_behavior:
DEFAULT a_expr
- { $$ = (Node *) makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2, NULL, @1); }
+ { $$ = (Node *) makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2, @1); }
| json_behavior_type
- { $$ = (Node *) makeJsonBehavior($1, NULL, NULL, @1); }
+ { $$ = (Node *) makeJsonBehavior($1, NULL, @1); }
;
json_behavior_type:
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 5a5130b160..a898449ed4 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -93,19 +93,10 @@ static Node *transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *expr);
static Node *transformJsonSerializeExpr(ParseState *pstate,
JsonSerializeExpr *expr);
static Node *transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *p);
-static JsonExpr *transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func,
- const char *constructName);
static void transformJsonPassingArgs(ParseState *pstate, const char *constructName,
JsonFormatType format, List *args,
List **passing_values, List **passing_names);
-static Node *coerceJsonFuncExprOutput(ParseState *pstate, JsonExpr *jsexpr);
-static Oid JsonFuncExprDefaultReturnType(JsonExpr *jsexpr);
-static Node *coerceJsonExpr(ParseState *pstate, Node *expr,
- const JsonReturning *returning, bool omit_quotes);
-static JsonCoercion *makeJsonCoercion(const JsonReturning *returning,
- bool omit_quotes, Node *cast_expr);
-static List *InitJsonItemCoercions(ParseState *pstate, const JsonReturning *returning,
- Oid contextItemTypeId);
+static void coerceJsonExprOutput(ParseState *pstate, JsonExpr *jsexpr);
static JsonBehavior *transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
JsonBehaviorType default_behavior,
JsonReturning *returning);
@@ -4258,22 +4249,27 @@ transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
static Node *
transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
{
- JsonExpr *jsexpr = NULL;
+ JsonExpr *jsexpr;
+ Node *path_spec;
const char *func_name = NULL;
+ JsonFormatType default_format;
switch (func->op)
{
case JSON_EXISTS_OP:
func_name = "JSON_EXISTS";
+ default_format = JS_FORMAT_DEFAULT;
break;
case JSON_QUERY_OP:
func_name = "JSON_QUERY";
+ default_format = JS_FORMAT_JSONB;
break;
case JSON_VALUE_OP:
func_name = "JSON_VALUE";
+ default_format = JS_FORMAT_DEFAULT;
break;
default:
- elog(ERROR, "invalid JsonFuncExpr op");
+ elog(ERROR, "invalid JsonFuncExpr op %d", (int) func->op);
break;
}
@@ -4289,7 +4285,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
format->encoding != JS_ENC_DEFAULT)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("cannot specify FORMAT in RETURNING clause of %s()",
+ errmsg("cannot specify FORMAT JSON in RETURNING clause of %s()",
func_name),
parser_errposition(pstate, format->location));
}
@@ -4304,7 +4300,49 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
errmsg("SQL/JSON QUOTES behavior must not be specified when WITH WRAPPER is used"),
parser_errposition(pstate, func->location));
- jsexpr = transformJsonExprCommon(pstate, func, func_name);
+ jsexpr = makeNode(JsonExpr);
+ jsexpr->location = func->location;
+ jsexpr->op = func->op;
+ jsexpr->formatted_expr = transformJsonValueExpr(pstate, func_name,
+ func->context_item,
+ default_format,
+ JSONBOID, false);
+
+ /*
+ * jsonpath machinery can only handle jsonb documents, so coerce the input
+ * if not already of jsonb type.
+ */
+ if (exprType(jsexpr->formatted_expr) != JSONBOID)
+ jsexpr->formatted_expr = coerce_to_specific_type(pstate,
+ jsexpr->formatted_expr, JSONBOID,
+ func_name);
+ jsexpr->format = func->context_item->format;
+
+ path_spec = transformExprRecurse(pstate, func->pathspec);
+ path_spec = coerce_to_target_type(pstate, path_spec, exprType(path_spec),
+ JSONPATHOID, -1,
+ COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
+ exprLocation(path_spec));
+ if (path_spec == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("JSON path expression must be of type %s, not of type %s",
+ "jsonpath", format_type_be(exprType(path_spec))),
+ parser_errposition(pstate, exprLocation(path_spec))));
+ jsexpr->path_spec = path_spec;
+
+ /*
+ * Transform and coerce to json[b] passing arguments, whose format is
+ * determined by context item type.
+ */
+ transformJsonPassingArgs(pstate, func_name,
+ exprType(jsexpr->formatted_expr) == JSONBOID ?
+ JS_FORMAT_JSONB : JS_FORMAT_JSON,
+ func->passing,
+ &jsexpr->passing_values,
+ &jsexpr->passing_names);
+
+ jsexpr->returning = transformJsonOutput(pstate, func->output, false);
switch (func->op)
{
@@ -4335,11 +4373,11 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
{
JsonReturning *ret = jsexpr->returning;
- ret->typid = JsonFuncExprDefaultReturnType(jsexpr);
+ ret->typid = JSONBOID;
ret->typmod = -1;
}
- jsexpr->result_coercion = coerceJsonFuncExprOutput(pstate, jsexpr);
+ coerceJsonExprOutput(pstate, jsexpr);
if (func->on_empty)
jsexpr->on_empty = transformJsonBehavior(pstate,
@@ -4353,7 +4391,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
case JSON_VALUE_OP:
/* Always omit quotes from scalar strings. */
- jsexpr->omit_quotes = (func->quotes == JS_QUOTES_OMIT);
+ jsexpr->omit_quotes = true;
/* JSON_VALUE returns text by default. */
if (!OidIsValid(jsexpr->returning->typid))
@@ -4369,16 +4407,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
- jsexpr->result_coercion = coerceJsonFuncExprOutput(pstate, jsexpr);
-
- /*
- * Initialize expressions to coerce the scalar value returned by
- * JsonPathValue() to the "returning" type.
- */
- if (jsexpr->result_coercion)
- jsexpr->item_coercions =
- InitJsonItemCoercions(pstate, jsexpr->returning,
- exprType(jsexpr->formatted_expr));
+ coerceJsonExprOutput(pstate, jsexpr);
if (func->on_empty)
jsexpr->on_empty = transformJsonBehavior(pstate,
@@ -4391,70 +4420,13 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
break;
default:
- elog(ERROR, "invalid JsonFuncExpr op");
+ elog(ERROR, "invalid JsonFuncExpr op %d", (int) func->op);
break;
}
return (Node *) jsexpr;
}
-/*
- * Common code for JSON_VALUE, JSON_QUERY, JSON_EXISTS transformation
- * into a JsonExpr node.
- */
-static JsonExpr *
-transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func,
- const char *constructName)
-{
- JsonExpr *jsexpr = makeNode(JsonExpr);
- Node *path_spec;
-
- jsexpr->location = func->location;
- jsexpr->op = func->op;
- jsexpr->formatted_expr = transformJsonValueExpr(pstate, constructName,
- func->context_item,
- JS_FORMAT_JSON,
- InvalidOid, false);
-
- if (exprType(jsexpr->formatted_expr) != JSONBOID)
- ereport(ERROR,
- errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("%s() is not yet implemented for the json type",
- constructName),
- errhint("Try casting the argument to jsonb"),
- parser_errposition(pstate, exprLocation(jsexpr->formatted_expr)));
-
- jsexpr->format = func->context_item->format;
-
- path_spec = transformExprRecurse(pstate, func->pathspec);
- jsexpr->path_spec =
- coerce_to_target_type(pstate, path_spec, exprType(path_spec),
- JSONPATHOID, -1,
- COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
- exprLocation(path_spec));
- if (!jsexpr->path_spec)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("JSON path expression must be of type %s, not of type %s",
- "jsonpath", format_type_be(exprType(path_spec))),
- parser_errposition(pstate, exprLocation(path_spec))));
-
- /*
- * Transform and coerce to json[b] passing arguments, whose format is
- * determined by context item type.
- */
- transformJsonPassingArgs(pstate, constructName,
- exprType(jsexpr->formatted_expr) == JSONBOID ?
- JS_FORMAT_JSONB : JS_FORMAT_JSON,
- func->passing,
- &jsexpr->passing_values,
- &jsexpr->passing_names);
-
- jsexpr->returning = transformJsonOutput(pstate, func->output, false);
-
- return jsexpr;
-}
-
/*
* Transform a JSON PASSING clause.
*/
@@ -4481,29 +4453,47 @@ transformJsonPassingArgs(ParseState *pstate, const char *constructName,
}
/*
- * Create an expression to coerce the output of JSON_VALUE() / JSON_QUERY()
- * to the output type, if needed.
+ * Set up to coerce the result value of JSON_VALUE() / JSON_QUERY() to the
+ * RETURNING type (default or user-specified), if needed.
*/
-static Node *
-coerceJsonFuncExprOutput(ParseState *pstate, JsonExpr *jsexpr)
+static void
+coerceJsonExprOutput(ParseState *pstate, JsonExpr *jsexpr)
{
JsonReturning *returning = jsexpr->returning;
Node *context_item = jsexpr->formatted_expr;
- Node *coercion_expr = NULL;
int default_typmod;
Oid default_typid;
bool omit_quotes =
jsexpr->op == JSON_QUERY_OP && jsexpr->omit_quotes;
+ Node *coercion_expr = NULL;
Assert(returning);
/*
- * Cast functions from jsonb to the following types (jsonb_bool() et al)
- * don't handle errors softly, so force to use json_populate_type() using
- * a JsonCoercion node so that any errors are handled appropriately.
+ * Check for cases where the coercion should be handled at runtime, that
+ * is, without using a cast expression.
*/
- if (jsexpr->op == JSON_QUERY_OP)
+ if (jsexpr->op == JSON_VALUE_OP)
{
+ /*
+ * Use cast expressions for types with typmod and domain types.
+ */
+ if (returning->typmod == -1 &&
+ get_typtype(returning->typid) != TYPTYPE_DOMAIN)
+ {
+ jsexpr->use_io_coercion = true;
+ return;
+ }
+ }
+ else if (jsexpr->op == JSON_QUERY_OP)
+ {
+ /*
+ * Cast functions from jsonb to the following types (jsonb_bool() et
+ * al) don't handle errors softly, so coerce either by calling
+ * json_populate_type() or the type's input function so that any
+ * errors are handled appropriately. The latter only if OMIT QUOTES
+ * is true.
+ */
switch (returning->typid)
{
case BOOLOID:
@@ -4513,173 +4503,68 @@ coerceJsonFuncExprOutput(ParseState *pstate, JsonExpr *jsexpr)
case INT8OID:
case FLOAT4OID:
case FLOAT8OID:
- return (Node *) makeJsonCoercion(returning, omit_quotes, NULL);
+ if (jsexpr->omit_quotes)
+ jsexpr->use_io_coercion = true;
+ else
+ jsexpr->use_json_coercion = true;
+ return;
default:
break;
}
}
- default_typid = JsonFuncExprDefaultReturnType(jsexpr);
- default_typmod = -1;
+ /* Look up a cast expression. */
+
+ /*
+ * For JSON_VALUE() and for JSON_QUERY() when OMIT QUOTES is true,
+ * ExecEvalJsonExprPath() will convert a quote-stripped source value to
+ * its text representation, so use TEXTOID as the source type.
+ */
+ if (omit_quotes || jsexpr->op == JSON_VALUE_OP)
+ {
+ default_typid = TEXTOID;
+ default_typmod = -1;
+ }
+ else
+ {
+ default_typid = exprType(context_item);
+ default_typmod = exprTypmod(context_item);
+ }
+
if (returning->typid != default_typid ||
- returning->typmod != default_typmod ||
- omit_quotes)
+ returning->typmod != default_typmod)
{
/*
* We abuse CaseTestExpr here as placeholder to pass the result of
- * evaluating the JSON_VALUE/QUERY jsonpath expression as input to the
- * coercion expression.
+ * jsonpath evaluation as input to the coercion expression.
*/
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
- /*
- * When OMIT QUOTES is true, ExecEvalJsonCoercion() will convert a
- * quote-stripped source value to its text representation, so use
- * TEXTOID as the source type.
- */
- placeholder->typeId = omit_quotes ? TEXTOID : exprType(context_item);
- placeholder->typeMod = omit_quotes ? -1 : exprTypmod(context_item);
-
- Assert(placeholder->typeId == default_typid ||
- placeholder->typeId == TEXTOID);
- Assert(placeholder->typeMod == default_typmod ||
- placeholder->typeMod == -1);
+ placeholder->typeId = default_typid;
+ placeholder->typeMod = default_typmod;
- coercion_expr = coerceJsonExpr(pstate, (Node *) placeholder,
- returning, omit_quotes);
+ coercion_expr = coerceJsonFuncExpr(pstate, (Node *) placeholder,
+ returning, false);
+ if (coercion_expr == (Node *) placeholder)
+ coercion_expr = NULL;
}
- return coercion_expr;
-}
-
-/* Returns the default type for a given JsonExpr for a given JsonFormat. */
-static Oid
-JsonFuncExprDefaultReturnType(JsonExpr *jsexpr)
-{
- JsonFormat *format = jsexpr->format;
- Node *context_item = jsexpr->formatted_expr;
-
- Assert(format);
- if (format->format_type == JS_FORMAT_JSONB)
- return JSONBOID;
- else if (format->format_type == JS_FORMAT_DEFAULT &&
- exprType(context_item) == JSONBOID)
- return JSONBOID;
-
- return JSONOID;
-}
-
-/*
- * Returns a JsonCoercion node to coerce a jsonb-valued expression to the
- * target type given by 'returning' using either json_populate_type() or
- * by using the target type's input function.
- */
-static JsonCoercion *
-makeJsonCoercion(const JsonReturning *returning, bool omit_quotes,
- Node *cast_expr)
-{
- JsonCoercion *coercion = makeNode(JsonCoercion);
+ jsexpr->coercion_expr = coercion_expr;
- coercion->targettype = returning->typid;
- coercion->targettypmod = returning->typmod;
- coercion->omit_quotes = omit_quotes;
- coercion->cast_expr = cast_expr;
-
- return coercion;
-}
-
-/*
- * Coerce the result of JSON_VALUE / JSON_QUERY () (or a behavior expression)
- * to the output type
- *
- * Returns NULL if no coercion needed (the input expresssion is already of the
- * desired type) and OMIT QUOTES is false.
- *
- * Returns a JsonCoercion node if the cast was not found or if OMIT QUOTES is
- * true.
- */
-static Node *
-coerceJsonExpr(ParseState *pstate, Node *expr, const JsonReturning *returning,
- bool omit_quotes)
-{
- Node *coerced_expr;
-
- Assert(expr != NULL);
- coerced_expr = coerceJsonFuncExpr(pstate, expr, returning, false);
-
- if (coerced_expr == expr && !omit_quotes)
- return NULL;
-
- /* Use coerced_expr for JsonCoercion.cast_expr iff coercion is needed. */
- if (coerced_expr == NULL || omit_quotes)
- return (Node *) makeJsonCoercion(returning, omit_quotes,
- coerced_expr == expr ?
- NULL : coerced_expr);
-
- return coerced_expr;
-}
-
-/*
- * Initialize JsonCoercion nodes for coercing a given JSON item value produced
- * by JSON_VALUE to the target "returning" type; also see
- * ExecPrepareJsonItemCoercion().
- */
-static List *
-InitJsonItemCoercions(ParseState *pstate, const JsonReturning *returning,
- Oid contextItemTypeId)
-{
- List *item_coercions = NIL;
- int i;
- Oid typeoid;
- struct
- {
- JsonItemType item_type;
- Oid typeoid;
- } item_types[] =
- {
- {JsonItemTypeNull, UNKNOWNOID},
- {JsonItemTypeString, TEXTOID},
- {JsonItemTypeNumeric, NUMERICOID},
- {JsonItemTypeBoolean, BOOLOID},
- {JsonItemTypeDate, DATEOID},
- {JsonItemTypeTime, TIMEOID},
- {JsonItemTypeTimetz, TIMETZOID},
- {JsonItemTypeTimestamp, TIMESTAMPOID},
- {JsonItemTypeTimestamptz, TIMESTAMPTZOID},
- {JsonItemTypeComposite, contextItemTypeId},
- {JsonItemTypeInvalid, InvalidOid}
- };
-
- for (i = 0; OidIsValid(typeoid = item_types[i].typeoid); i++)
+ if (coercion_expr == NULL)
{
- Node *expr;
- JsonItemCoercion *item_coercion = makeNode(JsonItemCoercion);
-
- if (typeoid == UNKNOWNOID)
- {
- expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid);
- }
+ /*
+ * Either no cast was found or coercion is unnecessary but still must
+ * convert the string value to the output type.
+ */
+ if (omit_quotes || jsexpr->op == JSON_VALUE_OP)
+ jsexpr->use_io_coercion = true;
else
- {
- CaseTestExpr *placeholder = makeNode(CaseTestExpr);
-
- /*
- * We abuse CaseTestExpr here as placeholder to pass the result of
- * JSON_VALUE jsonpath expression to the coercion function.
- */
- placeholder->typeId = item_types[i].typeoid;
- placeholder->typeMod = -1;
- placeholder->collation = InvalidOid;
-
- expr = (Node *) placeholder;
- }
-
- item_coercion->item_type = item_types[i].item_type;
- item_coercion->coercion = coerceJsonExpr(pstate, expr, returning, false);
- item_coercions = lappend(item_coercions, item_coercion);
+ jsexpr->use_json_coercion = true;
}
- return item_coercions;
+ Assert(jsexpr->coercion_expr != NULL ||
+ (jsexpr->use_io_coercion != jsexpr->use_json_coercion));
}
/*
@@ -4758,16 +4643,16 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
JsonBehaviorType default_behavior,
JsonReturning *returning)
{
- JsonBehaviorType behavior_type = default_behavior;
+ JsonBehaviorType btype = default_behavior;
Node *expr = NULL;
- JsonCoercion *coercion = NULL;
+ bool coerce = false;
int location = -1;
if (behavior)
{
- behavior_type = behavior->btype;
+ btype = behavior->btype;
location = behavior->location;
- if (behavior_type == JSON_BEHAVIOR_DEFAULT)
+ if (btype == JSON_BEHAVIOR_DEFAULT)
{
expr = transformExprRecurse(pstate, behavior->expr);
if (!IsA(expr, Const) && !IsA(expr, FuncExpr) &&
@@ -4791,8 +4676,8 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
}
}
- if (expr == NULL && behavior_type != JSON_BEHAVIOR_ERROR)
- expr = GetJsonBehaviorConstExpr(behavior_type, location);
+ if (expr == NULL && btype != JSON_BEHAVIOR_ERROR)
+ expr = GetJsonBehaviorConstExpr(btype, location);
if (expr)
{
@@ -4801,15 +4686,15 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
/*
* Coerce NULLs and "internal" (that is, not specified by the user)
- * jsonb-valued expressions with a JsonCoercion node.
+ * jsonb-valued expressions at runtime using json_populate_type().
*
* For other (user-specified) non-NULL values, try to find a cast and
* error out if one is not found.
*/
if (isnull ||
(exprType(expr) == JSONBOID &&
- behavior_type == default_behavior))
- coercion = makeJsonCoercion(returning, false, NULL);
+ btype == default_behavior))
+ coerce = true;
else
coerced_expr =
coerce_to_target_type(pstate, expr, exprType(expr),
@@ -4828,5 +4713,12 @@ transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
expr = coerced_expr;
}
- return makeJsonBehavior(behavior_type, expr, coercion, location);
+ if (behavior)
+ behavior->expr = expr;
+ else
+ behavior = makeJsonBehavior(btype, expr, location);
+
+ behavior->coerce = coerce;
+
+ return behavior;
}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 383efe5b6c..1276f33604 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -2019,6 +2019,9 @@ FigureColnameInternal(Node *node, char **name)
case JSON_VALUE_OP:
*name = "json_value";
return 2;
+ default:
+ elog(ERROR, "unrecognized JsonExpr op: %d",
+ (int) ((JsonFuncExpr *) node)->op);
}
break;
default:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0539c2424f..155274d7e8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9886,6 +9886,9 @@ get_rule_expr(Node *node, deparse_context *context,
case JSON_VALUE_OP:
appendStringInfoString(buf, "JSON_VALUE(");
break;
+ default:
+ elog(ERROR, "unrecognized JsonExpr op: %d",
+ (int) jexpr->op);
}
get_rule_expr(jexpr->formatted_expr, context, showimplicit);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 4bbf4acdf6..64698202a5 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -705,9 +705,8 @@ typedef struct ExprEvalStep
/* for EEOP_JSONEXPR_COERCION */
struct
{
- JsonCoercion *coercion;
- FmgrInfo *input_finfo;
- Oid typioparam;
+ Oid targettype;
+ int32 targettypmod;
void *json_populate_type_cache;
ErrorSaveContext *escontext;
} jsonexpr_coercion;
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index cd2ce63fa1..b17fa52d82 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1028,6 +1028,10 @@ typedef struct JsonExprState
/* JsonPathVariable entries for passing_values */
List *args;
+ /* Type input function info for JSON_VALUE() result coercion. */
+ FmgrInfo *input_finfo;
+ FunctionCallInfo input_fcinfo;
+
/*
* Addresses of steps that implement the non-ERROR variant of ON EMPTY
* and ON ERROR behaviors, respectively.
@@ -1039,22 +1043,10 @@ typedef struct JsonExprState
* Addresses of steps to coerce the result value of jsonpath evaluation to
* the RETURNING type.
*
- * jump_eval_result_coercion points to the step to evaluate the coercion
- * given in JsonExpr.result_coercion. -1 if no coercion is necessary.
- *
- * Only valid for JSON_VALUE, eval_item_coercion_jumps is an array of
- * num_item_coercions elements each containing a step address to coerce
- * a value of given JsonItemType returned by JsonPathValue() to the
- * RETURNING type, or -1 if no coercion is necessary.
- * item_coercion_is_cast is an array of boolean flags of the same length
- * that indicates whether each valid step address in the
- * eval_item_coercion_jumps array corresponds to a cast expression or a
- * JsonCoercion node.
+ * jump_eval_coercion_expr points to the step to evaluate the coercion
+ * given in JsonExpr.coercion_expr. -1 if no coercion is necessary.
*/
- int jump_eval_result_coercion;
- int num_item_coercions;
- int *eval_item_coercion_jumps;
- bool *item_coercion_is_cast;
+ int jump_eval_coercion_expr;
/*
* Address to jump to when skipping all the steps after performing
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 91d95fc52b..fdc78270e5 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -112,11 +112,11 @@ extern JsonFormat *makeJsonFormat(JsonFormatType type, JsonEncoding encoding,
int location);
extern JsonValueExpr *makeJsonValueExpr(Expr *raw_expr, Expr *formatted_expr,
JsonFormat *format);
-extern JsonBehavior *makeJsonBehavior(JsonBehaviorType type, Node *expr,
- JsonCoercion *coercion, 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 JsonBehavior *makeJsonBehavior(JsonBehaviorType btype, Node *expr,
+ int location);
#endif /* MAKEFUNC_H */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 377982f8fa..6da09bcb5f 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1725,77 +1725,13 @@ typedef enum JsonBehaviorType
JSON_BEHAVIOR_DEFAULT,
} JsonBehaviorType;
-/*
- * JsonCoercion
- * Information about coercing a SQL/JSON value to the specified
- * type at runtime
- *
- * A node of this type is created if the parser cannot find a cast expression
- * using coerce_type() or if OMIT QUOTES is specified for JSON_QUERY; see
- * coerceJsonFuncExprOutput().
- *
- * If it's the latter, 'cast_expr' may contain the cast expression, evaluated
- * separately from this node, that will do the actual coercion of the
- * quote-stripped string. If no cast expression is given, the string will be
- * coerced by calling the target type's input function. See
- * ExecEvalJsonCoercion().
- */
-typedef struct JsonCoercion
-{
- NodeTag type;
-
- Oid targettype;
- int32 targettypmod;
- bool omit_quotes; /* OMIT QUOTES specified for JSON_QUERY? */
- Node *cast_expr; /* coercion cast expression or NULL */
- Oid collation;
-} JsonCoercion;
-
-/*
- * JsonItemType
- * Possible types for scalar values returned by JSON_VALUE()
- *
- * The comment next to each item type mentions the corresponding
- * JsonbValue.jbvType.
- */
-typedef enum JsonItemType
-{
- JsonItemTypeNull, /* jbvNull */
- JsonItemTypeString, /* jbvString */
- JsonItemTypeNumeric, /* jbvNumeric */
- JsonItemTypeBoolean, /* jbvBool */
- JsonItemTypeDate, /* jbvDatetime: DATEOID */
- JsonItemTypeTime, /* jbvDatetime: TIMEOID */
- JsonItemTypeTimetz, /* jbvDatetime: TIMETZOID */
- JsonItemTypeTimestamp, /* jbvDatetime: TIMESTAMPOID */
- JsonItemTypeTimestamptz, /* jbvDatetime: TIMESTAMPTZOID */
- JsonItemTypeComposite, /* jbvArray, jbvObject, jbvBinary */
- JsonItemTypeInvalid,
-} JsonItemType;
-
-/*
- * JsonItemCoercion
- * Coercion expression for the given JsonItemType
- *
- * If not NULL, 'coercion' given the expression node to convert a scalar value
- * extracted from a JsonbValue of the given type to the target type given by
- * JsonExpr.returning. NULL means the coercion is unnecessary.
- */
-typedef struct JsonItemCoercion
-{
- NodeTag type;
-
- JsonItemType item_type;
- Node *coercion;
-} JsonItemCoercion;
-
/*
* JsonBehavior
* Information about ON ERROR / ON EMPTY behaviors of JSON_VALUE(),
* JSON_QUERY(), and JSON_EXISTS()
*
* 'expr' is the expression to emit when a given behavior (EMPTY or ERROR)
- * occurs on evaluating the SQL/JSON query function. 'coercion' is set
+ * occurs on evaluating the SQL/JSON query function. 'coerce' is set to true
* if 'expr' isn't already of the expected target type given by
* JsonExpr.returning.
*/
@@ -1805,8 +1741,7 @@ typedef struct JsonBehavior
JsonBehaviorType btype;
Node *expr;
- JsonCoercion *coercion; /* to coerce behavior expression when there is
- * no cast to the target type */
+ bool coerce;
int location; /* token location, or -1 if unknown */
} JsonBehavior;
@@ -1853,16 +1788,17 @@ typedef struct JsonExpr
JsonBehavior *on_error;
/*
- * Expression to convert the result of jsonpath functions to the RETURNING
- * type
- */
- Node *result_coercion;
-
- /*
- * List of expressions for coercing JSON_VALUE() result values, containing
- * one element for every JsonItemType.
+ * Information about converting the result of jsonpath functions
+ * JsonPathQuery() and JsonPathValue() to the RETURNING type.
+ *
+ * coercion_expr is a cast expression if the parser can find it for the
+ * source and the target type. If not, either use_io_coercion or
+ * use_json_coercion is set to determine the coercion method to use at
+ * runtime; see coerceJsonExprOutput() and ExecInitJsonExpr().
*/
- List *item_coercions;
+ Node *coercion_expr;
+ bool use_io_coercion;
+ bool use_json_coercion;
/* WRAPPER specification for JSON_QUERY */
JsonWrapper wrapper;
@@ -1870,6 +1806,9 @@ typedef struct JsonExpr
/* KEEP or OMIT QUOTES for singleton scalars returned by JSON_QUERY() */
bool omit_quotes;
+ /* JsonExpr's collation, if coercion_expr is NULL. */
+ Oid collation;
+
/* Original JsonFuncExpr's location */
int location;
} JsonExpr;
diff --git a/src/test/regress/expected/sqljson_queryfuncs.out b/src/test/regress/expected/sqljson_queryfuncs.out
index f5b57465d6..5a537d0655 100644
--- a/src/test/regress/expected/sqljson_queryfuncs.out
+++ b/src/test/regress/expected/sqljson_queryfuncs.out
@@ -1,10 +1,4 @@
-- JSON_EXISTS
--- json arguments currently not supported
-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
SELECT JSON_EXISTS(NULL::jsonb, '$');
json_exists
-------------
@@ -147,12 +141,6 @@ SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
(1 row)
-- JSON_VALUE
--- json arguments currently not supported
-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
SELECT JSON_VALUE(NULL::jsonb, '$');
json_value
------------
@@ -174,7 +162,7 @@ SELECT JSON_VALUE(jsonb 'null', '$' RETURNING int);
SELECT JSON_VALUE(jsonb 'true', '$');
json_value
------------
- true
+ t
(1 row)
SELECT JSON_VALUE(jsonb 'true', '$' RETURNING bool);
@@ -203,7 +191,11 @@ SELECT JSON_VALUE(jsonb '123', '$' RETURNING text);
/* jsonb bytea ??? */
SELECT JSON_VALUE(jsonb '123', '$' RETURNING bytea ERROR ON ERROR);
-ERROR: SQL/JSON item cannot be cast to target type
+ json_value
+------------
+ \x313233
+(1 row)
+
SELECT JSON_VALUE(jsonb '1.23', '$');
json_value
------------
@@ -213,7 +205,7 @@ SELECT JSON_VALUE(jsonb '1.23', '$');
SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
json_value
------------
- 1
+
(1 row)
SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
@@ -329,11 +321,7 @@ SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null DEFAULT 2 ON
(1 row)
SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null DEFAULT NULL ON EMPTY ERROR ON ERROR);
- json_value
-------------
-
-(1 row)
-
+ERROR: domain sqljsonb_int_not_null does not allow null values
CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
SELECT JSON_VALUE('"purple"'::jsonb, 'lax $[*]' RETURNING rgb);
@@ -433,7 +421,7 @@ SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
(1 row)
SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSON); -- RETURNING FORMAT not allowed
-ERROR: cannot specify FORMAT in RETURNING clause of JSON_VALUE()
+ERROR: cannot specify FORMAT JSON in RETURNING clause of JSON_VALUE()
LINE 1: ...CT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int FORMAT JSO...
^
-- RETUGNING pseudo-types not allowed
@@ -531,13 +519,12 @@ SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +
"2018-02-21T02:34:56+00:00"
(1 row)
+-- Test that numeric JSON values are coerced uniformly
+select json_value('{"a": 1.234}', '$.a' returning int error on error);
+ERROR: invalid input syntax for type integer: "1.234"
+select json_value('{"a": "1.234"}', '$.a' returning int error on error);
+ERROR: invalid input syntax for type integer: "1.234"
-- JSON_QUERY
--- json arguments currently not supported
-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
SELECT JSON_VALUE(NULL::jsonb, '$');
json_value
------------
@@ -1259,3 +1246,24 @@ SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
-- Should fail (invalid path)
SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
ERROR: syntax error at or near " " of jsonpath input
+-- Non-jsonb inputs automatically coerced to jsonb
+SELECT JSON_EXISTS(json '{"a": 123}', '$' || '.' || 'a');
+ json_exists
+-------------
+ t
+(1 row)
+
+SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+ json_query
+------------
+
+(1 row)
+
+-- Test non-const jsonpath
+CREATE TEMP TABLE jsonpaths (path) AS SELECT '$';
+SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths;
+ json_value
+------------
+ "aaa"
+(1 row)
+
diff --git a/src/test/regress/sql/sqljson_queryfuncs.sql b/src/test/regress/sql/sqljson_queryfuncs.sql
index 5be2d8e3f8..d01b172376 100644
--- a/src/test/regress/sql/sqljson_queryfuncs.sql
+++ b/src/test/regress/sql/sqljson_queryfuncs.sql
@@ -1,8 +1,4 @@
-- JSON_EXISTS
-
--- json arguments currently not supported
-SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
-
SELECT JSON_EXISTS(NULL::jsonb, '$');
SELECT JSON_EXISTS(jsonb '[]', '$');
SELECT JSON_EXISTS(JSON_OBJECT(RETURNING jsonb), '$');
@@ -35,10 +31,6 @@ SELECT JSON_EXISTS(jsonb '1', '$ > 2');
SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
-- JSON_VALUE
-
--- json arguments currently not supported
-SELECT JSON_VALUE(NULL FORMAT JSON, '$');
-
SELECT JSON_VALUE(NULL::jsonb, '$');
SELECT JSON_VALUE(jsonb 'null', '$');
@@ -143,10 +135,11 @@ SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamp '2018-02-21 12:34:56 +10
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
+-- Test that numeric JSON values are coerced uniformly
+select json_value('{"a": 1.234}', '$.a' returning int error on error);
+select json_value('{"a": "1.234"}', '$.a' returning int error on error);
--- json arguments currently not supported
-SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+-- JSON_QUERY
SELECT JSON_VALUE(NULL::jsonb, '$');
@@ -425,3 +418,11 @@ 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');
+
+-- Non-jsonb inputs automatically coerced to jsonb
+SELECT JSON_EXISTS(json '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+
+-- Test non-const jsonpath
+CREATE TEMP TABLE jsonpaths (path) AS SELECT '$';
+SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 616e315ec4..7b0395510d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1280,7 +1280,6 @@ JsonArrayQueryConstructor
JsonBaseObjectInfo
JsonBehavior
JsonBehaviorType
-JsonCoercion
JsonConstructorExpr
JsonConstructorExprState
JsonConstructorType
@@ -1293,7 +1292,6 @@ JsonFormat
JsonFormatType
JsonHashEntry
JsonIsPredicate
-JsonItemCoercion
JsonItemType
JsonIterateStringValuesAction
JsonKeyValue
--
2.43.0