aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/gram.y188
-rw-r--r--src/backend/parser/parse_expr.c553
-rw-r--r--src/backend/parser/parse_target.c18
3 files changed, 735 insertions, 24 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 39a801a1c38..c247eefb0cc 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -653,10 +653,19 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
json_returning_clause_opt
json_name_and_value
json_aggregate_func
+ json_argument
+ json_behavior
+ json_on_error_clause_opt
%type <list> json_name_and_value_list
json_value_expr_list
json_array_aggregate_order_by_clause_opt
-%type <ival> json_predicate_type_constraint
+ json_arguments
+ json_behavior_clause_opt
+ json_passing_clause_opt
+%type <ival> json_behavior_type
+ json_predicate_type_constraint
+ json_quotes_clause_opt
+ json_wrapper_behavior
%type <boolean> json_key_uniqueness_constraint_opt
json_object_constructor_null_clause_opt
json_array_constructor_null_clause_opt
@@ -697,7 +706,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
- COMMITTED COMPRESSION CONCURRENTLY CONFIGURATION CONFLICT
+ COMMITTED COMPRESSION CONCURRENTLY CONDITIONAL CONFIGURATION CONFLICT
CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY
COST CREATE CROSS CSV CUBE CURRENT_P
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
@@ -708,8 +717,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
+ EACH ELSE EMPTY_P ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ERROR_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR
@@ -724,10 +733,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
- JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_OBJECT JSON_OBJECTAGG
- JSON_SCALAR JSON_SERIALIZE
+ JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_EXISTS JSON_OBJECT JSON_OBJECTAGG
+ JSON_QUERY JSON_SCALAR JSON_SERIALIZE JSON_VALUE
- KEY KEYS
+ KEEP KEY KEYS
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
@@ -741,7 +750,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
NULLS_P NUMERIC
- OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
+ OBJECT_P OF OFF OFFSET OIDS OLD OMIT ON ONLY OPERATOR OPTION OPTIONS OR
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
@@ -750,7 +759,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
- QUOTE
+ QUOTE QUOTES
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF_P REFERENCES REFERENCING
REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
@@ -761,7 +770,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
- START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRIP_P
+ START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRING_P STRIP_P
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
@@ -769,7 +778,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
TREAT TRIGGER TRIM TRUE_P
TRUNCATE TRUSTED TYPE_P TYPES_P
- UESCAPE UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN
+ UESCAPE UNBOUNDED UNCONDITIONAL UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN
UNLISTEN UNLOGGED UNTIL UPDATE USER USING
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
@@ -15805,6 +15814,62 @@ func_expr_common_subexpr:
m->location = @1;
$$ = (Node *) m;
}
+ | JSON_QUERY '('
+ json_value_expr ',' a_expr json_passing_clause_opt
+ json_returning_clause_opt
+ json_wrapper_behavior
+ json_quotes_clause_opt
+ json_behavior_clause_opt
+ ')'
+ {
+ JsonFuncExpr *n = makeNode(JsonFuncExpr);
+
+ n->op = JSON_QUERY_OP;
+ n->context_item = (JsonValueExpr *) $3;
+ n->pathspec = $5;
+ n->passing = $6;
+ n->output = (JsonOutput *) $7;
+ n->wrapper = $8;
+ n->quotes = $9;
+ n->on_empty = (JsonBehavior *) linitial($10);
+ n->on_error = (JsonBehavior *) lsecond($10);
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | JSON_EXISTS '('
+ json_value_expr ',' a_expr json_passing_clause_opt
+ json_on_error_clause_opt
+ ')'
+ {
+ JsonFuncExpr *n = makeNode(JsonFuncExpr);
+
+ n->op = JSON_EXISTS_OP;
+ n->context_item = (JsonValueExpr *) $3;
+ n->pathspec = $5;
+ n->passing = $6;
+ n->output = NULL;
+ n->on_error = (JsonBehavior *) $7;
+ n->location = @1;
+ $$ = (Node *) n;
+ }
+ | JSON_VALUE '('
+ json_value_expr ',' a_expr json_passing_clause_opt
+ json_returning_clause_opt
+ json_behavior_clause_opt
+ ')'
+ {
+ JsonFuncExpr *n = makeNode(JsonFuncExpr);
+
+ n->op = JSON_VALUE_OP;
+ n->context_item = (JsonValueExpr *) $3;
+ n->pathspec = $5;
+ n->passing = $6;
+ n->output = (JsonOutput *) $7;
+ n->on_empty = (JsonBehavior *) linitial($8);
+ n->on_error = (JsonBehavior *) lsecond($8);
+ n->location = @1;
+ $$ = (Node *) n;
+ }
;
@@ -16531,6 +16596,77 @@ opt_asymmetric: ASYMMETRIC
;
/* SQL/JSON support */
+json_passing_clause_opt:
+ PASSING json_arguments { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
+json_arguments:
+ json_argument { $$ = list_make1($1); }
+ | json_arguments ',' json_argument { $$ = lappend($1, $3); }
+ ;
+
+json_argument:
+ json_value_expr AS ColLabel
+ {
+ JsonArgument *n = makeNode(JsonArgument);
+
+ n->val = (JsonValueExpr *) $1;
+ n->name = $3;
+ $$ = (Node *) n;
+ }
+ ;
+
+/* ARRAY is a noise word */
+json_wrapper_behavior:
+ WITHOUT WRAPPER { $$ = JSW_NONE; }
+ | WITHOUT ARRAY WRAPPER { $$ = JSW_NONE; }
+ | WITH WRAPPER { $$ = JSW_UNCONDITIONAL; }
+ | WITH ARRAY WRAPPER { $$ = JSW_UNCONDITIONAL; }
+ | WITH CONDITIONAL ARRAY WRAPPER { $$ = JSW_CONDITIONAL; }
+ | WITH UNCONDITIONAL ARRAY WRAPPER { $$ = JSW_UNCONDITIONAL; }
+ | WITH CONDITIONAL WRAPPER { $$ = JSW_CONDITIONAL; }
+ | WITH UNCONDITIONAL WRAPPER { $$ = JSW_UNCONDITIONAL; }
+ | /* empty */ { $$ = JSW_UNSPEC; }
+ ;
+
+json_behavior:
+ DEFAULT a_expr
+ { $$ = (Node *) makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2, @1); }
+ | json_behavior_type
+ { $$ = (Node *) makeJsonBehavior($1, NULL, @1); }
+ ;
+
+json_behavior_type:
+ ERROR_P { $$ = JSON_BEHAVIOR_ERROR; }
+ | NULL_P { $$ = JSON_BEHAVIOR_NULL; }
+ | TRUE_P { $$ = JSON_BEHAVIOR_TRUE; }
+ | FALSE_P { $$ = JSON_BEHAVIOR_FALSE; }
+ | UNKNOWN { $$ = JSON_BEHAVIOR_UNKNOWN; }
+ | EMPTY_P ARRAY { $$ = JSON_BEHAVIOR_EMPTY_ARRAY; }
+ | EMPTY_P OBJECT_P { $$ = JSON_BEHAVIOR_EMPTY_OBJECT; }
+ /* non-standard, for Oracle compatibility only */
+ | EMPTY_P { $$ = JSON_BEHAVIOR_EMPTY_ARRAY; }
+ ;
+
+json_behavior_clause_opt:
+ json_behavior ON EMPTY_P
+ { $$ = list_make2($1, NULL); }
+ | json_behavior ON ERROR_P
+ { $$ = list_make2(NULL, $1); }
+ | json_behavior ON EMPTY_P json_behavior ON ERROR_P
+ { $$ = list_make2($1, $4); }
+ | /* EMPTY */
+ { $$ = list_make2(NULL, NULL); }
+ ;
+
+json_on_error_clause_opt:
+ json_behavior ON ERROR_P
+ { $$ = $1; }
+ | /* EMPTY */
+ { $$ = NULL; }
+ ;
+
json_value_expr:
a_expr json_format_clause_opt
{
@@ -16575,6 +16711,14 @@ json_format_clause_opt:
}
;
+json_quotes_clause_opt:
+ KEEP QUOTES ON SCALAR STRING_P { $$ = JS_QUOTES_KEEP; }
+ | KEEP QUOTES { $$ = JS_QUOTES_KEEP; }
+ | OMIT QUOTES ON SCALAR STRING_P { $$ = JS_QUOTES_OMIT; }
+ | OMIT QUOTES { $$ = JS_QUOTES_OMIT; }
+ | /* EMPTY */ { $$ = JS_QUOTES_UNSPEC; }
+ ;
+
json_returning_clause_opt:
RETURNING Typename json_format_clause_opt
{
@@ -17191,6 +17335,7 @@ unreserved_keyword:
| COMMIT
| COMMITTED
| COMPRESSION
+ | CONDITIONAL
| CONFIGURATION
| CONFLICT
| CONNECTION
@@ -17227,10 +17372,12 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | EMPTY_P
| ENABLE_P
| ENCODING
| ENCRYPTED
| ENUM_P
+ | ERROR_P
| ESCAPE
| EVENT
| EXCLUDE
@@ -17280,6 +17427,7 @@ unreserved_keyword:
| INSTEAD
| INVOKER
| ISOLATION
+ | KEEP
| KEY
| KEYS
| LABEL
@@ -17326,6 +17474,7 @@ unreserved_keyword:
| OFF
| OIDS
| OLD
+ | OMIT
| OPERATOR
| OPTION
| OPTIONS
@@ -17356,6 +17505,7 @@ unreserved_keyword:
| PROGRAM
| PUBLICATION
| QUOTE
+ | QUOTES
| RANGE
| READ
| REASSIGN
@@ -17415,6 +17565,7 @@ unreserved_keyword:
| STORAGE
| STORED
| STRICT_P
+ | STRING_P
| STRIP_P
| SUBSCRIPTION
| SUPPORT
@@ -17437,6 +17588,7 @@ unreserved_keyword:
| UESCAPE
| UNBOUNDED
| UNCOMMITTED
+ | UNCONDITIONAL
| UNENCRYPTED
| UNKNOWN
| UNLISTEN
@@ -17497,10 +17649,13 @@ col_name_keyword:
| JSON
| JSON_ARRAY
| JSON_ARRAYAGG
+ | JSON_EXISTS
| JSON_OBJECT
| JSON_OBJECTAGG
+ | JSON_QUERY
| JSON_SCALAR
| JSON_SERIALIZE
+ | JSON_VALUE
| LEAST
| MERGE_ACTION
| NATIONAL
@@ -17734,6 +17889,7 @@ bare_label_keyword:
| COMMITTED
| COMPRESSION
| CONCURRENTLY
+ | CONDITIONAL
| CONFIGURATION
| CONFLICT
| CONNECTION
@@ -17786,11 +17942,13 @@ bare_label_keyword:
| DROP
| EACH
| ELSE
+ | EMPTY_P
| ENABLE_P
| ENCODING
| ENCRYPTED
| END_P
| ENUM_P
+ | ERROR_P
| ESCAPE
| EVENT
| EXCLUDE
@@ -17860,10 +18018,14 @@ bare_label_keyword:
| JSON
| JSON_ARRAY
| JSON_ARRAYAGG
+ | JSON_EXISTS
| JSON_OBJECT
| JSON_OBJECTAGG
+ | JSON_QUERY
| JSON_SCALAR
| JSON_SERIALIZE
+ | JSON_VALUE
+ | KEEP
| KEY
| KEYS
| LABEL
@@ -17925,6 +18087,7 @@ bare_label_keyword:
| OFF
| OIDS
| OLD
+ | OMIT
| ONLY
| OPERATOR
| OPTION
@@ -17962,6 +18125,7 @@ bare_label_keyword:
| PROGRAM
| PUBLICATION
| QUOTE
+ | QUOTES
| RANGE
| READ
| REAL
@@ -18030,6 +18194,7 @@ bare_label_keyword:
| STORAGE
| STORED
| STRICT_P
+ | STRING_P
| STRIP_P
| SUBSCRIPTION
| SUBSTRING
@@ -18064,6 +18229,7 @@ bare_label_keyword:
| UESCAPE
| UNBOUNDED
| UNCOMMITTED
+ | UNCONDITIONAL
| UNENCRYPTED
| UNIQUE
| UNKNOWN
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d44b1f2ab2f..7166138bf76 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -37,6 +37,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/fmgroids.h"
+#include "utils/jsonb.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/xml.h"
@@ -91,6 +92,15 @@ static Node *transformJsonParseExpr(ParseState *pstate, JsonParseExpr *expr);
static Node *transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *expr);
static Node *transformJsonSerializeExpr(ParseState *pstate,
JsonSerializeExpr *expr);
+static Node *transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *p);
+static void transformJsonPassingArgs(ParseState *pstate, const char *constructName,
+ JsonFormatType format, List *args,
+ List **passing_values, List **passing_names);
+static void coerceJsonExprOutput(ParseState *pstate, JsonExpr *jsexpr);
+static JsonBehavior *transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
+ JsonBehaviorType default_behavior,
+ JsonReturning *returning);
+static Node *GetJsonBehaviorConst(JsonBehaviorType btype, int location);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -359,6 +369,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
result = transformJsonSerializeExpr(pstate, (JsonSerializeExpr *) expr);
break;
+ case T_JsonFuncExpr:
+ result = transformJsonFuncExpr(pstate, (JsonFuncExpr *) expr);
+ break;
+
default:
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3263,7 +3277,7 @@ makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
static Node *
transformJsonValueExpr(ParseState *pstate, const char *constructName,
JsonValueExpr *ve, JsonFormatType default_format,
- Oid targettype)
+ Oid targettype, bool isarg)
{
Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
Node *rawexpr;
@@ -3295,6 +3309,41 @@ transformJsonValueExpr(ParseState *pstate, const char *constructName,
else
format = ve->format->format_type;
}
+ else if (isarg)
+ {
+ /*
+ * Special treatment for PASSING arguments.
+ *
+ * Pass types supported by GetJsonPathVar() / JsonItemFromDatum()
+ * directly without converting to json[b].
+ */
+ switch (exprtype)
+ {
+ case BOOLOID:
+ case NUMERICOID:
+ case INT2OID:
+ case INT4OID:
+ case INT8OID:
+ case FLOAT4OID:
+ case FLOAT8OID:
+ case TEXTOID:
+ case VARCHAROID:
+ case DATEOID:
+ case TIMEOID:
+ case TIMETZOID:
+ case TIMESTAMPOID:
+ case TIMESTAMPTZOID:
+ return expr;
+
+ default:
+ if (typcategory == TYPCATEGORY_STRING)
+ return expr;
+ /* else convert argument to json[b] type */
+ break;
+ }
+
+ format = default_format;
+ }
else if (exprtype == JSONOID || exprtype == JSONBOID)
format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
else
@@ -3306,7 +3355,12 @@ transformJsonValueExpr(ParseState *pstate, const char *constructName,
Node *coerced;
bool only_allow_cast = OidIsValid(targettype);
- if (!only_allow_cast &&
+ /*
+ * PASSING args are handled appropriately by GetJsonPathVar() /
+ * JsonItemFromDatum().
+ */
+ if (!isarg &&
+ !only_allow_cast &&
exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
ereport(ERROR,
errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -3459,6 +3513,11 @@ transformJsonOutput(ParseState *pstate, const JsonOutput *output,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("returning SETOF types is not supported in SQL/JSON functions"));
+ if (get_typtype(ret->typid) == TYPTYPE_PSEUDO)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("returning pseudo-types is not supported in SQL/JSON functions"));
+
if (ret->format->format_type == JS_FORMAT_DEFAULT)
/* assign JSONB format when returning jsonb, or JSON format otherwise */
ret->format->format_type =
@@ -3555,7 +3614,6 @@ coerceJsonFuncExpr(ParseState *pstate, Node *expr,
/* try to coerce expression to the output type */
res = coerce_to_target_type(pstate, expr, exprtype,
returning->typid, returning->typmod,
- /* XXX throwing errors when casting to char(N) */
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST,
location);
@@ -3655,7 +3713,7 @@ transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
Node *val = transformJsonValueExpr(pstate, "JSON_OBJECT()",
kv->value,
JS_FORMAT_DEFAULT,
- InvalidOid);
+ InvalidOid, false);
args = lappend(args, key);
args = lappend(args, val);
@@ -3842,7 +3900,7 @@ transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
val = transformJsonValueExpr(pstate, "JSON_OBJECTAGG()",
agg->arg->value,
JS_FORMAT_DEFAULT,
- InvalidOid);
+ InvalidOid, false);
args = list_make2(key, val);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
@@ -3898,9 +3956,8 @@ transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
Oid aggfnoid;
Oid aggtype;
- arg = transformJsonValueExpr(pstate, "JSON_ARRAYAGG()",
- agg->arg,
- JS_FORMAT_DEFAULT, InvalidOid);
+ arg = transformJsonValueExpr(pstate, "JSON_ARRAYAGG()", agg->arg,
+ JS_FORMAT_DEFAULT, InvalidOid, false);
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
list_make1(arg));
@@ -3947,9 +4004,8 @@ transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
{
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
Node *val = transformJsonValueExpr(pstate, "JSON_ARRAY()",
- jsval,
- JS_FORMAT_DEFAULT,
- InvalidOid);
+ jsval, JS_FORMAT_DEFAULT,
+ InvalidOid, false);
args = lappend(args, val);
}
@@ -4108,7 +4164,7 @@ transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
* function-like CASTs.
*/
arg = transformJsonValueExpr(pstate, "JSON()", jsexpr->expr,
- JS_FORMAT_JSON, returning->typid);
+ JS_FORMAT_JSON, returning->typid, false);
}
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_PARSE, list_make1(arg), NULL,
@@ -4153,7 +4209,7 @@ transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
Node *arg = transformJsonValueExpr(pstate, "JSON_SERIALIZE()",
expr->expr,
JS_FORMAT_JSON,
- InvalidOid);
+ InvalidOid, false);
if (expr->output)
{
@@ -4187,3 +4243,474 @@ transformJsonSerializeExpr(ParseState *pstate, JsonSerializeExpr *expr)
return makeJsonConstructorExpr(pstate, JSCTOR_JSON_SERIALIZE, list_make1(arg),
NULL, returning, false, false, expr->location);
}
+
+/*
+ * Transform JSON_VALUE, JSON_QUERY, JSON_EXISTS functions into a JsonExpr node.
+ */
+static Node *
+transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
+{
+ 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 %d", (int) func->op);
+ break;
+ }
+
+ /*
+ * Even though the syntax allows it, FORMAT JSON specification in
+ * RETURNING is meaningless except for JSON_QUERY(). Flag if not
+ * JSON_QUERY().
+ */
+ if (func->output && func->op != JSON_QUERY_OP)
+ {
+ JsonFormat *format = func->output->returning->format;
+
+ if (format->format_type != JS_FORMAT_DEFAULT ||
+ format->encoding != JS_ENC_DEFAULT)
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("cannot specify FORMAT JSON in RETURNING clause of %s()",
+ func_name),
+ parser_errposition(pstate, format->location));
+ }
+
+ /* OMIT QUOTES is meaningless when strings are wrapped. */
+ if (func->op == JSON_QUERY_OP &&
+ func->quotes != JS_QUOTES_UNSPEC &&
+ (func->wrapper == JSW_CONDITIONAL ||
+ func->wrapper == JSW_UNCONDITIONAL))
+ ereport(ERROR,
+ errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("SQL/JSON QUOTES behavior must not be specified when WITH WRAPPER is used"),
+ parser_errposition(pstate, func->location));
+
+ jsexpr = makeNode(JsonExpr);
+ jsexpr->location = func->location;
+ jsexpr->op = func->op;
+
+ /*
+ * jsonpath machinery can only handle jsonb documents, so coerce the input
+ * if not already of jsonb type.
+ */
+ jsexpr->formatted_expr = transformJsonValueExpr(pstate, func_name,
+ func->context_item,
+ default_format,
+ JSONBOID,
+ false);
+ 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 the PASSING arguments to to jsonb. */
+ transformJsonPassingArgs(pstate, func_name,
+ JS_FORMAT_JSONB,
+ func->passing,
+ &jsexpr->passing_values,
+ &jsexpr->passing_names);
+
+ /* Transform the JsonOutput into JsonReturning. */
+ jsexpr->returning = transformJsonOutput(pstate, func->output, false);
+
+ switch (func->op)
+ {
+ case JSON_EXISTS_OP:
+ /* JSON_EXISTS returns boolean by default. */
+ if (!OidIsValid(jsexpr->returning->typid))
+ {
+ jsexpr->returning->typid = BOOLOID;
+ jsexpr->returning->typmod = -1;
+ }
+
+ jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ JSON_BEHAVIOR_FALSE,
+ jsexpr->returning);
+ break;
+
+ case JSON_QUERY_OP:
+ /* JSON_QUERY returns jsonb by default. */
+ if (!OidIsValid(jsexpr->returning->typid))
+ {
+ JsonReturning *ret = jsexpr->returning;
+
+ ret->typid = JSONBOID;
+ ret->typmod = -1;
+ }
+
+ /*
+ * Keep quotes on scalar strings by default, omitting them only if
+ * OMIT QUOTES is specified.
+ */
+ jsexpr->omit_quotes = (func->quotes == JS_QUOTES_OMIT);
+ jsexpr->wrapper = func->wrapper;
+
+ coerceJsonExprOutput(pstate, jsexpr);
+
+ if (func->on_empty)
+ jsexpr->on_empty = transformJsonBehavior(pstate,
+ func->on_empty,
+ JSON_BEHAVIOR_NULL,
+ jsexpr->returning);
+ jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ JSON_BEHAVIOR_NULL,
+ jsexpr->returning);
+ break;
+
+ case JSON_VALUE_OP:
+ /* JSON_VALUE returns text by default. */
+ if (!OidIsValid(jsexpr->returning->typid))
+ {
+ jsexpr->returning->typid = TEXTOID;
+ jsexpr->returning->typmod = -1;
+ }
+
+ /*
+ * Override whatever transformJsonOutput() set these to, which
+ * assumes that output type to be jsonb.
+ */
+ jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
+ jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
+
+ /* Always omit quotes from scalar strings. */
+ jsexpr->omit_quotes = true;
+
+ coerceJsonExprOutput(pstate, jsexpr);
+
+ if (func->on_empty)
+ jsexpr->on_empty = transformJsonBehavior(pstate,
+ func->on_empty,
+ JSON_BEHAVIOR_NULL,
+ jsexpr->returning);
+ jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+ JSON_BEHAVIOR_NULL,
+ jsexpr->returning);
+ break;
+
+ default:
+ elog(ERROR, "invalid JsonFuncExpr op %d", (int) func->op);
+ break;
+ }
+
+ return (Node *) jsexpr;
+}
+
+/*
+ * Transform a SQL/JSON PASSING clause.
+ */
+static void
+transformJsonPassingArgs(ParseState *pstate, const char *constructName,
+ JsonFormatType format, List *args,
+ List **passing_values, List **passing_names)
+{
+ ListCell *lc;
+
+ *passing_values = NIL;
+ *passing_names = NIL;
+
+ foreach(lc, args)
+ {
+ JsonArgument *arg = castNode(JsonArgument, lfirst(lc));
+ Node *expr = transformJsonValueExpr(pstate, constructName,
+ arg->val, format,
+ InvalidOid, true);
+
+ *passing_values = lappend(*passing_values, expr);
+ *passing_names = lappend(*passing_names, makeString(arg->name));
+ }
+}
+
+/*
+ * Set up to coerce the result value of JSON_VALUE() / JSON_QUERY() to the
+ * RETURNING type (default or user-specified), if needed.
+ */
+static void
+coerceJsonExprOutput(ParseState *pstate, JsonExpr *jsexpr)
+{
+ JsonReturning *returning = jsexpr->returning;
+ Node *context_item = jsexpr->formatted_expr;
+ int default_typmod;
+ Oid default_typid;
+ bool omit_quotes =
+ jsexpr->op == JSON_QUERY_OP && jsexpr->omit_quotes;
+ Node *coercion_expr = NULL;
+
+ Assert(returning);
+
+ /*
+ * Check for cases where the coercion should be handled at runtime, that
+ * is, without using a cast expression.
+ */
+ 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:
+ case NUMERICOID:
+ case INT2OID:
+ case INT4OID:
+ case INT8OID:
+ case FLOAT4OID:
+ case FLOAT8OID:
+ if (jsexpr->omit_quotes)
+ jsexpr->use_io_coercion = true;
+ else
+ jsexpr->use_json_coercion = true;
+ return;
+ default:
+ break;
+ }
+ }
+
+ /* 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)
+ {
+ /*
+ * We abuse CaseTestExpr here as placeholder to pass the result of
+ * jsonpath evaluation as input to the coercion expression.
+ */
+ CaseTestExpr *placeholder = makeNode(CaseTestExpr);
+
+ placeholder->typeId = default_typid;
+ placeholder->typeMod = default_typmod;
+
+ coercion_expr = coerceJsonFuncExpr(pstate, (Node *) placeholder,
+ returning, false);
+ if (coercion_expr == (Node *) placeholder)
+ coercion_expr = NULL;
+ }
+
+ jsexpr->coercion_expr = coercion_expr;
+
+ if (coercion_expr == NULL)
+ {
+ /*
+ * 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
+ jsexpr->use_json_coercion = true;
+ }
+
+ Assert(jsexpr->coercion_expr != NULL ||
+ (jsexpr->use_io_coercion != jsexpr->use_json_coercion));
+}
+
+/*
+ * Transform a JSON BEHAVIOR clause.
+ */
+static JsonBehavior *
+transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
+ JsonBehaviorType default_behavior,
+ JsonReturning *returning)
+{
+ JsonBehaviorType btype = default_behavior;
+ Node *expr = NULL;
+ bool coerce_at_runtime = false;
+ int location = -1;
+
+ if (behavior)
+ {
+ btype = behavior->btype;
+ location = behavior->location;
+ if (btype == JSON_BEHAVIOR_DEFAULT)
+ {
+ expr = transformExprRecurse(pstate, behavior->expr);
+ if (!IsA(expr, Const) && !IsA(expr, FuncExpr) &&
+ !IsA(expr, OpExpr))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("can only specify constant, non-aggregate"
+ " function, or operator expression for"
+ " DEFAULT"),
+ parser_errposition(pstate, exprLocation(expr))));
+ if (contain_var_clause(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("DEFAULT expression must not contain column references"),
+ parser_errposition(pstate, exprLocation(expr))));
+ if (expression_returns_set(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("DEFAULT expression must not return a set"),
+ parser_errposition(pstate, exprLocation(expr))));
+ }
+ }
+
+ if (expr == NULL && btype != JSON_BEHAVIOR_ERROR)
+ expr = GetJsonBehaviorConst(btype, location);
+
+ if (expr)
+ {
+ Node *coerced_expr = expr;
+ bool isnull = (IsA(expr, Const) && ((Const *) expr)->constisnull);
+
+ /*
+ * Coerce NULLs and "internal" (that is, not specified by the user)
+ * 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 &&
+ btype == default_behavior))
+ coerce_at_runtime = true;
+ else
+ coerced_expr =
+ coerce_to_target_type(pstate, expr, exprType(expr),
+ returning->typid, returning->typmod,
+ COERCION_EXPLICIT, COERCE_EXPLICIT_CAST,
+ exprLocation((Node *) behavior));
+
+ if (coerced_expr == NULL)
+ ereport(ERROR,
+ errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast behavior expression of type %s to %s",
+ format_type_be(exprType(expr)),
+ format_type_be(returning->typid)),
+ parser_errposition(pstate, exprLocation(expr)));
+ else
+ expr = coerced_expr;
+ }
+
+ if (behavior)
+ behavior->expr = expr;
+ else
+ behavior = makeJsonBehavior(btype, expr, location);
+
+ behavior->coerce = coerce_at_runtime;
+
+ return behavior;
+}
+
+/*
+ * Returns a Const node holding the value for the given non-ERROR
+ * JsonBehaviorType.
+ */
+static Node *
+GetJsonBehaviorConst(JsonBehaviorType btype, int location)
+{
+ Datum val = (Datum) 0;
+ Oid typid = JSONBOID;
+ int len = -1;
+ bool isbyval = false;
+ bool isnull = false;
+ Const *con;
+
+ switch (btype)
+ {
+ case JSON_BEHAVIOR_EMPTY_ARRAY:
+ val = DirectFunctionCall1(jsonb_in, CStringGetDatum("[]"));
+ break;
+
+ case JSON_BEHAVIOR_EMPTY_OBJECT:
+ val = DirectFunctionCall1(jsonb_in, CStringGetDatum("{}"));
+ break;
+
+ case JSON_BEHAVIOR_TRUE:
+ val = BoolGetDatum(true);
+ typid = BOOLOID;
+ len = sizeof(bool);
+ isbyval = true;
+ break;
+
+ case JSON_BEHAVIOR_FALSE:
+ val = BoolGetDatum(false);
+ typid = BOOLOID;
+ len = sizeof(bool);
+ isbyval = true;
+ break;
+
+ case JSON_BEHAVIOR_NULL:
+ case JSON_BEHAVIOR_UNKNOWN:
+ case JSON_BEHAVIOR_EMPTY:
+ val = (Datum) 0;
+ isnull = true;
+ typid = INT4OID;
+ len = sizeof(int32);
+ isbyval = true;
+ break;
+
+ /* These two behavior types are handled by the caller. */
+ case JSON_BEHAVIOR_DEFAULT:
+ case JSON_BEHAVIOR_ERROR:
+ Assert(false);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized SQL/JSON behavior %d", btype);
+ break;
+ }
+
+ con = makeConst(typid, -1, InvalidOid, len, val, isnull, isbyval);
+ con->location = location;
+
+ return (Node *) con;
+}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index ea522b932b2..1276f336041 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -2006,6 +2006,24 @@ FigureColnameInternal(Node *node, char **name)
/* make JSON_ARRAYAGG act like a regular function */
*name = "json_arrayagg";
return 2;
+ case T_JsonFuncExpr:
+ /* make SQL/JSON functions act like a regular function */
+ switch (((JsonFuncExpr *) node)->op)
+ {
+ case JSON_EXISTS_OP:
+ *name = "json_exists";
+ return 2;
+ case JSON_QUERY_OP:
+ *name = "json_query";
+ return 2;
+ case JSON_VALUE_OP:
+ *name = "json_value";
+ return 2;
+ default:
+ elog(ERROR, "unrecognized JsonExpr op: %d",
+ (int) ((JsonFuncExpr *) node)->op);
+ }
+ break;
default:
break;
}