mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
SQL/JSON query functions
This introduces the SQL/JSON functions for querying JSON data using jsonpath expressions. The functions are: JSON_EXISTS() JSON_QUERY() JSON_VALUE() All of these functions only operate on jsonb. The workaround for now is to cast the argument to jsonb. JSON_EXISTS() tests if the jsonpath expression applied to the jsonb value yields any values. JSON_VALUE() must return a single value, and an error occurs if it tries to return multiple values. JSON_QUERY() must return a json object or array, and there are various WRAPPER options for handling scalar or multi-value results. Both these functions have options for handling EMPTY and ERROR conditions. Nikita Glukhov Reviewers have included (in no particular order) Andres Freund, Alexander Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu, Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby. Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru
This commit is contained in:
@ -281,6 +281,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
MergeWhenClause *mergewhen;
|
||||
struct KeyActions *keyactions;
|
||||
struct KeyAction *keyaction;
|
||||
JsonBehavior *jsbehavior;
|
||||
struct
|
||||
{
|
||||
JsonBehavior *on_empty;
|
||||
JsonBehavior *on_error;
|
||||
} on_behavior;
|
||||
JsonQuotes js_quotes;
|
||||
}
|
||||
|
||||
%type <node> stmt toplevel_stmt schema_stmt routine_body_stmt
|
||||
@ -646,7 +653,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
json_representation
|
||||
json_value_expr
|
||||
json_func_expr
|
||||
json_value_func_expr
|
||||
json_query_expr
|
||||
json_exists_predicate
|
||||
json_api_common_syntax
|
||||
json_context_item
|
||||
json_argument
|
||||
json_output_clause_opt
|
||||
json_returning_clause_opt
|
||||
json_value_constructor
|
||||
json_object_constructor
|
||||
json_object_constructor_args
|
||||
@ -658,15 +672,43 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
json_aggregate_func
|
||||
json_object_aggregate_constructor
|
||||
json_array_aggregate_constructor
|
||||
json_path_specification
|
||||
|
||||
%type <list> json_name_and_value_list
|
||||
json_value_expr_list
|
||||
json_array_aggregate_order_by_clause_opt
|
||||
json_arguments
|
||||
json_passing_clause_opt
|
||||
|
||||
%type <str> json_table_path_name
|
||||
json_as_path_name_clause_opt
|
||||
|
||||
%type <ival> json_encoding
|
||||
json_encoding_clause_opt
|
||||
json_wrapper_clause_opt
|
||||
json_wrapper_behavior
|
||||
json_conditional_or_unconditional_opt
|
||||
json_predicate_type_constraint_opt
|
||||
|
||||
%type <jsbehavior> json_behavior_error
|
||||
json_behavior_null
|
||||
json_behavior_true
|
||||
json_behavior_false
|
||||
json_behavior_unknown
|
||||
json_behavior_empty_array
|
||||
json_behavior_empty_object
|
||||
json_behavior_default
|
||||
json_value_behavior
|
||||
json_query_behavior
|
||||
json_exists_error_behavior
|
||||
json_exists_error_clause_opt
|
||||
|
||||
%type <on_behavior> json_value_on_behavior_clause_opt
|
||||
json_query_on_behavior_clause_opt
|
||||
|
||||
%type <js_quotes> json_quotes_behavior
|
||||
json_quotes_clause_opt
|
||||
|
||||
%type <boolean> json_key_uniqueness_constraint_opt
|
||||
json_object_constructor_null_clause_opt
|
||||
json_array_constructor_null_clause_opt
|
||||
@ -706,7 +748,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
|
||||
@ -717,8 +759,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
|
||||
@ -733,7 +775,8 @@ 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
|
||||
JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_EXISTS JSON_OBJECT JSON_OBJECTAGG
|
||||
JSON_QUERY JSON_VALUE
|
||||
|
||||
KEY KEYS KEEP
|
||||
|
||||
@ -749,7 +792,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
|
||||
|
||||
@ -757,7 +800,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 REFERENCES REFERENCING
|
||||
REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
|
||||
@ -767,7 +810,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
|
||||
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 STRIP_P
|
||||
SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P
|
||||
|
||||
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
|
||||
@ -775,7 +818,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
|
||||
@ -854,7 +897,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
* Using the same precedence as IDENT seems right for the reasons given above.
|
||||
*/
|
||||
%nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */
|
||||
%nonassoc ABSENT UNIQUE JSON
|
||||
%nonassoc ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
|
||||
%nonassoc FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN UNIQUE JSON
|
||||
%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
|
||||
%left Op OPERATOR /* multi-character ops and user-defined operators */
|
||||
%left '+' '-'
|
||||
@ -15549,6 +15593,80 @@ opt_asymmetric: ASYMMETRIC
|
||||
/* SQL/JSON support */
|
||||
json_func_expr:
|
||||
json_value_constructor
|
||||
| json_value_func_expr
|
||||
| json_query_expr
|
||||
| json_exists_predicate
|
||||
;
|
||||
|
||||
|
||||
json_value_func_expr:
|
||||
JSON_VALUE '('
|
||||
json_api_common_syntax
|
||||
json_returning_clause_opt
|
||||
json_value_on_behavior_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonFuncExpr *n = makeNode(JsonFuncExpr);
|
||||
n->op = JSON_VALUE_OP;
|
||||
n->common = (JsonCommon *) $3;
|
||||
n->output = (JsonOutput *) $4;
|
||||
n->on_empty = $5.on_empty;
|
||||
n->on_error = $5.on_error;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_api_common_syntax:
|
||||
json_context_item ',' json_path_specification
|
||||
json_as_path_name_clause_opt
|
||||
json_passing_clause_opt
|
||||
{
|
||||
JsonCommon *n = makeNode(JsonCommon);
|
||||
n->expr = (JsonValueExpr *) $1;
|
||||
n->pathspec = $3;
|
||||
n->pathname = $4;
|
||||
n->passing = $5;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_context_item:
|
||||
json_value_expr { $$ = $1; }
|
||||
;
|
||||
|
||||
json_path_specification:
|
||||
a_expr { $$ = $1; }
|
||||
;
|
||||
|
||||
json_as_path_name_clause_opt:
|
||||
AS json_table_path_name { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
json_table_path_name:
|
||||
name { $$ = $1; }
|
||||
;
|
||||
|
||||
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;
|
||||
}
|
||||
;
|
||||
|
||||
json_value_expr:
|
||||
@ -15587,6 +15705,153 @@ json_encoding:
|
||||
name { $$ = makeJsonEncoding($1); }
|
||||
;
|
||||
|
||||
json_behavior_error:
|
||||
ERROR_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_ERROR, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_null:
|
||||
NULL_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_NULL, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_true:
|
||||
TRUE_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_TRUE, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_false:
|
||||
FALSE_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_FALSE, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_unknown:
|
||||
UNKNOWN { $$ = makeJsonBehavior(JSON_BEHAVIOR_UNKNOWN, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_empty_array:
|
||||
EMPTY_P ARRAY { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_ARRAY, NULL); }
|
||||
/* non-standard, for Oracle compatibility only */
|
||||
| EMPTY_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_ARRAY, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_empty_object:
|
||||
EMPTY_P OBJECT_P { $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_OBJECT, NULL); }
|
||||
;
|
||||
|
||||
json_behavior_default:
|
||||
DEFAULT a_expr { $$ = makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2); }
|
||||
;
|
||||
|
||||
|
||||
json_value_behavior:
|
||||
json_behavior_null
|
||||
| json_behavior_error
|
||||
| json_behavior_default
|
||||
;
|
||||
|
||||
json_value_on_behavior_clause_opt:
|
||||
json_value_behavior ON EMPTY_P
|
||||
{ $$.on_empty = $1; $$.on_error = NULL; }
|
||||
| json_value_behavior ON EMPTY_P json_value_behavior ON ERROR_P
|
||||
{ $$.on_empty = $1; $$.on_error = $4; }
|
||||
| json_value_behavior ON ERROR_P
|
||||
{ $$.on_empty = NULL; $$.on_error = $1; }
|
||||
| /* EMPTY */
|
||||
{ $$.on_empty = NULL; $$.on_error = NULL; }
|
||||
;
|
||||
|
||||
json_query_expr:
|
||||
JSON_QUERY '('
|
||||
json_api_common_syntax
|
||||
json_output_clause_opt
|
||||
json_wrapper_clause_opt
|
||||
json_quotes_clause_opt
|
||||
json_query_on_behavior_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonFuncExpr *n = makeNode(JsonFuncExpr);
|
||||
n->op = JSON_QUERY_OP;
|
||||
n->common = (JsonCommon *) $3;
|
||||
n->output = (JsonOutput *) $4;
|
||||
n->wrapper = $5;
|
||||
if (n->wrapper != JSW_NONE && $6 != JS_QUOTES_UNSPEC)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("SQL/JSON QUOTES behavior must not be specified when WITH WRAPPER is used"),
|
||||
parser_errposition(@6)));
|
||||
n->omit_quotes = $6 == JS_QUOTES_OMIT;
|
||||
n->on_empty = $7.on_empty;
|
||||
n->on_error = $7.on_error;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
json_wrapper_clause_opt:
|
||||
json_wrapper_behavior WRAPPER { $$ = $1; }
|
||||
| /* EMPTY */ { $$ = 0; }
|
||||
;
|
||||
|
||||
json_wrapper_behavior:
|
||||
WITHOUT array_opt { $$ = JSW_NONE; }
|
||||
| WITH json_conditional_or_unconditional_opt array_opt { $$ = $2; }
|
||||
;
|
||||
|
||||
array_opt:
|
||||
ARRAY { }
|
||||
| /* EMPTY */ { }
|
||||
;
|
||||
|
||||
json_conditional_or_unconditional_opt:
|
||||
CONDITIONAL { $$ = JSW_CONDITIONAL; }
|
||||
| UNCONDITIONAL { $$ = JSW_UNCONDITIONAL; }
|
||||
| /* EMPTY */ { $$ = JSW_UNCONDITIONAL; }
|
||||
;
|
||||
|
||||
json_quotes_clause_opt:
|
||||
json_quotes_behavior QUOTES json_on_scalar_string_opt { $$ = $1; }
|
||||
| /* EMPTY */ { $$ = JS_QUOTES_UNSPEC; }
|
||||
;
|
||||
|
||||
json_quotes_behavior:
|
||||
KEEP { $$ = JS_QUOTES_KEEP; }
|
||||
| OMIT { $$ = JS_QUOTES_OMIT; }
|
||||
;
|
||||
|
||||
json_on_scalar_string_opt:
|
||||
ON SCALAR STRING { }
|
||||
| /* EMPTY */ { }
|
||||
;
|
||||
|
||||
json_query_behavior:
|
||||
json_behavior_error
|
||||
| json_behavior_null
|
||||
| json_behavior_empty_array
|
||||
| json_behavior_empty_object
|
||||
| json_behavior_default
|
||||
;
|
||||
|
||||
json_query_on_behavior_clause_opt:
|
||||
json_query_behavior ON EMPTY_P
|
||||
{ $$.on_empty = $1; $$.on_error = NULL; }
|
||||
| json_query_behavior ON EMPTY_P json_query_behavior ON ERROR_P
|
||||
{ $$.on_empty = $1; $$.on_error = $4; }
|
||||
| json_query_behavior ON ERROR_P
|
||||
{ $$.on_empty = NULL; $$.on_error = $1; }
|
||||
| /* EMPTY */
|
||||
{ $$.on_empty = NULL; $$.on_error = NULL; }
|
||||
;
|
||||
|
||||
json_returning_clause_opt:
|
||||
RETURNING Typename
|
||||
{
|
||||
JsonOutput *n = makeNode(JsonOutput);
|
||||
n->typeName = $2;
|
||||
n->returning = makeNode(JsonReturning);
|
||||
n->returning->format =
|
||||
makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, @2);
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
json_output_clause_opt:
|
||||
RETURNING Typename json_format_clause_opt
|
||||
{
|
||||
@ -15599,6 +15864,35 @@ json_output_clause_opt:
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
json_exists_predicate:
|
||||
JSON_EXISTS '('
|
||||
json_api_common_syntax
|
||||
json_returning_clause_opt
|
||||
json_exists_error_clause_opt
|
||||
')'
|
||||
{
|
||||
JsonFuncExpr *p = makeNode(JsonFuncExpr);
|
||||
p->op = JSON_EXISTS_OP;
|
||||
p->common = (JsonCommon *) $3;
|
||||
p->output = (JsonOutput *) $4;
|
||||
p->on_error = $5;
|
||||
p->location = @1;
|
||||
$$ = (Node *) p;
|
||||
}
|
||||
;
|
||||
|
||||
json_exists_error_clause_opt:
|
||||
json_exists_error_behavior ON ERROR_P { $$ = $1; }
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
json_exists_error_behavior:
|
||||
json_behavior_error
|
||||
| json_behavior_true
|
||||
| json_behavior_false
|
||||
| json_behavior_unknown
|
||||
;
|
||||
|
||||
json_value_constructor:
|
||||
json_object_constructor
|
||||
| json_array_constructor
|
||||
@ -16269,6 +16563,7 @@ unreserved_keyword:
|
||||
| COMMIT
|
||||
| COMMITTED
|
||||
| COMPRESSION
|
||||
| CONDITIONAL
|
||||
| CONFIGURATION
|
||||
| CONFLICT
|
||||
| CONNECTION
|
||||
@ -16305,10 +16600,12 @@ unreserved_keyword:
|
||||
| DOUBLE_P
|
||||
| DROP
|
||||
| EACH
|
||||
| EMPTY_P
|
||||
| ENABLE_P
|
||||
| ENCODING
|
||||
| ENCRYPTED
|
||||
| ENUM_P
|
||||
| ERROR_P
|
||||
| ESCAPE
|
||||
| EVENT
|
||||
| EXCLUDE
|
||||
@ -16358,6 +16655,7 @@ unreserved_keyword:
|
||||
| INVOKER
|
||||
| ISOLATION
|
||||
| JSON
|
||||
| KEEP
|
||||
| KEY
|
||||
| KEYS
|
||||
| LABEL
|
||||
@ -16404,6 +16702,7 @@ unreserved_keyword:
|
||||
| OFF
|
||||
| OIDS
|
||||
| OLD
|
||||
| OMIT
|
||||
| OPERATOR
|
||||
| OPTION
|
||||
| OPTIONS
|
||||
@ -16433,6 +16732,7 @@ unreserved_keyword:
|
||||
| PROGRAM
|
||||
| PUBLICATION
|
||||
| QUOTE
|
||||
| QUOTES
|
||||
| RANGE
|
||||
| READ
|
||||
| REASSIGN
|
||||
@ -16514,6 +16814,7 @@ unreserved_keyword:
|
||||
| UESCAPE
|
||||
| UNBOUNDED
|
||||
| UNCOMMITTED
|
||||
| UNCONDITIONAL
|
||||
| UNENCRYPTED
|
||||
| UNKNOWN
|
||||
| UNLISTEN
|
||||
@ -16573,8 +16874,11 @@ col_name_keyword:
|
||||
| INTERVAL
|
||||
| JSON_ARRAY
|
||||
| JSON_ARRAYAGG
|
||||
| JSON_EXISTS
|
||||
| JSON_OBJECT
|
||||
| JSON_OBJECTAGG
|
||||
| JSON_QUERY
|
||||
| JSON_VALUE
|
||||
| LEAST
|
||||
| NATIONAL
|
||||
| NCHAR
|
||||
@ -16642,6 +16946,7 @@ type_func_name_keyword:
|
||||
| OVERLAPS
|
||||
| RIGHT
|
||||
| SIMILAR
|
||||
| STRING
|
||||
| TABLESAMPLE
|
||||
| VERBOSE
|
||||
;
|
||||
@ -16806,6 +17111,7 @@ bare_label_keyword:
|
||||
| COMMITTED
|
||||
| COMPRESSION
|
||||
| CONCURRENTLY
|
||||
| CONDITIONAL
|
||||
| CONFIGURATION
|
||||
| CONFLICT
|
||||
| CONNECTION
|
||||
@ -16858,11 +17164,13 @@ bare_label_keyword:
|
||||
| DROP
|
||||
| EACH
|
||||
| ELSE
|
||||
| EMPTY_P
|
||||
| ENABLE_P
|
||||
| ENCODING
|
||||
| ENCRYPTED
|
||||
| END_P
|
||||
| ENUM_P
|
||||
| ERROR_P
|
||||
| ESCAPE
|
||||
| EVENT
|
||||
| EXCLUDE
|
||||
@ -16931,8 +17239,11 @@ bare_label_keyword:
|
||||
| JSON
|
||||
| JSON_ARRAY
|
||||
| JSON_ARRAYAGG
|
||||
| JSON_EXISTS
|
||||
| JSON_OBJECT
|
||||
| JSON_OBJECTAGG
|
||||
| JSON_QUERY
|
||||
| JSON_VALUE
|
||||
| KEEP
|
||||
| KEY
|
||||
| KEYS
|
||||
@ -16994,6 +17305,7 @@ bare_label_keyword:
|
||||
| OFF
|
||||
| OIDS
|
||||
| OLD
|
||||
| OMIT
|
||||
| ONLY
|
||||
| OPERATOR
|
||||
| OPTION
|
||||
@ -17030,6 +17342,7 @@ bare_label_keyword:
|
||||
| PROGRAM
|
||||
| PUBLICATION
|
||||
| QUOTE
|
||||
| QUOTES
|
||||
| RANGE
|
||||
| READ
|
||||
| REAL
|
||||
@ -17098,6 +17411,7 @@ bare_label_keyword:
|
||||
| STORAGE
|
||||
| STORED
|
||||
| STRICT_P
|
||||
| STRING
|
||||
| STRIP_P
|
||||
| SUBSCRIPTION
|
||||
| SUBSTRING
|
||||
@ -17131,6 +17445,7 @@ bare_label_keyword:
|
||||
| UESCAPE
|
||||
| UNBOUNDED
|
||||
| UNCOMMITTED
|
||||
| UNCONDITIONAL
|
||||
| UNENCRYPTED
|
||||
| UNIQUE
|
||||
| UNKNOWN
|
||||
|
@ -691,6 +691,10 @@ assign_collations_walker(Node *node, assign_collations_context *context)
|
||||
&loccontext);
|
||||
}
|
||||
break;
|
||||
case T_JsonExpr:
|
||||
/* Context item and PASSING arguments are already
|
||||
* marked with collations in parse_expr.c. */
|
||||
break;
|
||||
default:
|
||||
|
||||
/*
|
||||
|
@ -86,6 +86,8 @@ static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
|
||||
static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
|
||||
static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
|
||||
static Node *transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *p);
|
||||
static Node *transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *p);
|
||||
static Node *transformJsonValueExpr(ParseState *pstate, JsonValueExpr *jve);
|
||||
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,
|
||||
@ -337,6 +339,14 @@ transformExprRecurse(ParseState *pstate, Node *expr)
|
||||
result = transformJsonIsPredicate(pstate, (JsonIsPredicate *) expr);
|
||||
break;
|
||||
|
||||
case T_JsonFuncExpr:
|
||||
result = transformJsonFuncExpr(pstate, (JsonFuncExpr *) expr);
|
||||
break;
|
||||
|
||||
case T_JsonValueExpr:
|
||||
result = transformJsonValueExpr(pstate, (JsonValueExpr *) expr);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should not reach here */
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
@ -3218,8 +3228,8 @@ makeCaseTestExpr(Node *expr)
|
||||
* default format otherwise.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
JsonFormatType default_format)
|
||||
transformJsonValueExprExt(ParseState *pstate, JsonValueExpr *ve,
|
||||
JsonFormatType default_format, bool isarg)
|
||||
{
|
||||
Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
|
||||
Node *rawexpr;
|
||||
@ -3238,6 +3248,8 @@ transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
|
||||
get_type_category_preferred(exprtype, &typcategory, &typispreferred);
|
||||
|
||||
rawexpr = expr;
|
||||
|
||||
if (ve->format->format_type != JS_FORMAT_DEFAULT)
|
||||
{
|
||||
if (ve->format->encoding != JS_ENC_DEFAULT && exprtype != BYTEAOID)
|
||||
@ -3256,12 +3268,44 @@ transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
else
|
||||
format = ve->format->format_type;
|
||||
}
|
||||
else if (isarg)
|
||||
{
|
||||
/* Pass SQL/JSON item types directly without conversion to json[b]. */
|
||||
switch (exprtype)
|
||||
{
|
||||
case TEXTOID:
|
||||
case NUMERICOID:
|
||||
case BOOLOID:
|
||||
case INT2OID:
|
||||
case INT4OID:
|
||||
case INT8OID:
|
||||
case FLOAT4OID:
|
||||
case FLOAT8OID:
|
||||
case DATEOID:
|
||||
case TIMEOID:
|
||||
case TIMETZOID:
|
||||
case TIMESTAMPOID:
|
||||
case TIMESTAMPTZOID:
|
||||
return expr;
|
||||
|
||||
default:
|
||||
if (typcategory == TYPCATEGORY_STRING)
|
||||
return coerce_to_specific_type(pstate, expr, TEXTOID,
|
||||
"JSON_VALUE_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
|
||||
format = default_format;
|
||||
|
||||
if (format != JS_FORMAT_DEFAULT)
|
||||
if (format == JS_FORMAT_DEFAULT)
|
||||
expr = rawexpr;
|
||||
else
|
||||
{
|
||||
Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
|
||||
Node *orig = makeCaseTestExpr(expr);
|
||||
@ -3269,7 +3313,7 @@ transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
|
||||
expr = orig;
|
||||
|
||||
if (exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
|
||||
if (!isarg && exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg(ve->format->format_type == JS_FORMAT_DEFAULT ?
|
||||
@ -3320,6 +3364,24 @@ transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON value expression using FORMAT JSON by default.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonValueExpr(ParseState *pstate, JsonValueExpr *jve)
|
||||
{
|
||||
return transformJsonValueExprExt(pstate, jve, JS_FORMAT_JSON, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON value expression using unspecified format by default.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonValueExprDefault(ParseState *pstate, JsonValueExpr *jve)
|
||||
{
|
||||
return transformJsonValueExprExt(pstate, jve, JS_FORMAT_DEFAULT, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks specified output format for its applicability to the target type.
|
||||
*/
|
||||
@ -3576,8 +3638,7 @@ transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
|
||||
{
|
||||
JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc));
|
||||
Node *key = transformExprRecurse(pstate, (Node *) kv->key);
|
||||
Node *val = transformJsonValueExpr(pstate, kv->value,
|
||||
JS_FORMAT_DEFAULT);
|
||||
Node *val = transformJsonValueExprDefault(pstate, kv->value);
|
||||
|
||||
args = lappend(args, key);
|
||||
args = lappend(args, val);
|
||||
@ -3755,7 +3816,7 @@ transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
|
||||
Oid aggtype;
|
||||
|
||||
key = transformExprRecurse(pstate, (Node *) agg->arg->key);
|
||||
val = transformJsonValueExpr(pstate, agg->arg->value, JS_FORMAT_DEFAULT);
|
||||
val = transformJsonValueExprDefault(pstate, agg->arg->value);
|
||||
args = list_make2(key, val);
|
||||
|
||||
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
|
||||
@ -3813,7 +3874,7 @@ transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
|
||||
const char *aggfnname;
|
||||
Oid aggtype;
|
||||
|
||||
arg = transformJsonValueExpr(pstate, agg->arg, JS_FORMAT_DEFAULT);
|
||||
arg = transformJsonValueExprDefault(pstate, agg->arg);
|
||||
|
||||
returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
|
||||
list_make1(arg));
|
||||
@ -3861,8 +3922,7 @@ transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
|
||||
foreach(lc, ctor->exprs)
|
||||
{
|
||||
JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
|
||||
Node *val = transformJsonValueExpr(pstate, jsval,
|
||||
JS_FORMAT_DEFAULT);
|
||||
Node *val = transformJsonValueExprDefault(pstate, jsval);
|
||||
|
||||
args = lappend(args, val);
|
||||
}
|
||||
@ -3945,3 +4005,413 @@ transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred)
|
||||
return makeJsonIsPredicate(expr, NULL, pred->value_type,
|
||||
pred->unique_keys, pred->location);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a JSON PASSING clause.
|
||||
*/
|
||||
static void
|
||||
transformJsonPassingArgs(ParseState *pstate, 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 = transformJsonValueExprExt(pstate, arg->val,
|
||||
format, true);
|
||||
|
||||
assign_expr_collations(pstate, expr);
|
||||
|
||||
*passing_values = lappend(*passing_values, expr);
|
||||
*passing_names = lappend(*passing_names, makeString(arg->name));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a JSON BEHAVIOR clause.
|
||||
*/
|
||||
static JsonBehavior *
|
||||
transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
|
||||
JsonBehaviorType default_behavior)
|
||||
{
|
||||
JsonBehaviorType behavior_type;
|
||||
Node *default_expr;
|
||||
|
||||
behavior_type = behavior ? behavior->btype : default_behavior;
|
||||
default_expr = behavior_type != JSON_BEHAVIOR_DEFAULT ? NULL :
|
||||
transformExprRecurse(pstate, behavior->default_expr);
|
||||
|
||||
return makeJsonBehavior(behavior_type, default_expr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Common code for JSON_VALUE, JSON_QUERY, JSON_EXISTS transformation
|
||||
* into a JsonExpr node.
|
||||
*/
|
||||
static JsonExpr *
|
||||
transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
|
||||
{
|
||||
JsonExpr *jsexpr = makeNode(JsonExpr);
|
||||
Node *pathspec;
|
||||
JsonFormatType format;
|
||||
|
||||
if (func->common->pathname)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("JSON_TABLE path name is not allowed here"),
|
||||
parser_errposition(pstate, func->location)));
|
||||
|
||||
jsexpr->location = func->location;
|
||||
jsexpr->op = func->op;
|
||||
jsexpr->formatted_expr = transformJsonValueExpr(pstate, func->common->expr);
|
||||
|
||||
assign_expr_collations(pstate, jsexpr->formatted_expr);
|
||||
|
||||
/* format is determined by context item type */
|
||||
format = exprType(jsexpr->formatted_expr) == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
|
||||
|
||||
jsexpr->result_coercion = NULL;
|
||||
jsexpr->omit_quotes = false;
|
||||
|
||||
jsexpr->format = func->common->expr->format;
|
||||
|
||||
pathspec = transformExprRecurse(pstate, func->common->pathspec);
|
||||
|
||||
jsexpr->path_spec =
|
||||
coerce_to_target_type(pstate, pathspec, exprType(pathspec),
|
||||
JSONPATHOID, -1,
|
||||
COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
|
||||
exprLocation(pathspec));
|
||||
if (!jsexpr->path_spec)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("JSON path expression must be type %s, not type %s",
|
||||
"jsonpath", format_type_be(exprType(pathspec))),
|
||||
parser_errposition(pstate, exprLocation(pathspec))));
|
||||
|
||||
/* transform and coerce to json[b] passing arguments */
|
||||
transformJsonPassingArgs(pstate, format, func->common->passing,
|
||||
&jsexpr->passing_values, &jsexpr->passing_names);
|
||||
|
||||
if (func->op != JSON_EXISTS_OP)
|
||||
jsexpr->on_empty = transformJsonBehavior(pstate, func->on_empty,
|
||||
JSON_BEHAVIOR_NULL);
|
||||
|
||||
jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
|
||||
func->op == JSON_EXISTS_OP ?
|
||||
JSON_BEHAVIOR_FALSE :
|
||||
JSON_BEHAVIOR_NULL);
|
||||
|
||||
return jsexpr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign default JSON returning type from the specified format or from
|
||||
* the context item type.
|
||||
*/
|
||||
static void
|
||||
assignDefaultJsonReturningType(Node *context_item, JsonFormat *context_format,
|
||||
JsonReturning *ret)
|
||||
{
|
||||
bool is_jsonb;
|
||||
|
||||
ret->format = copyObject(context_format);
|
||||
|
||||
if (ret->format->format_type == JS_FORMAT_DEFAULT)
|
||||
is_jsonb = exprType(context_item) == JSONBOID;
|
||||
else
|
||||
is_jsonb = ret->format->format_type == JS_FORMAT_JSONB;
|
||||
|
||||
ret->typid = is_jsonb ? JSONBOID : JSONOID;
|
||||
ret->typmod = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to coerce expression to the output type or
|
||||
* use json_populate_type() for composite, array and domain types or
|
||||
* use coercion via I/O.
|
||||
*/
|
||||
static JsonCoercion *
|
||||
coerceJsonExpr(ParseState *pstate, Node *expr, const JsonReturning *returning)
|
||||
{
|
||||
char typtype;
|
||||
JsonCoercion *coercion = makeNode(JsonCoercion);
|
||||
|
||||
coercion->expr = coerceJsonFuncExpr(pstate, expr, returning, false);
|
||||
|
||||
if (coercion->expr)
|
||||
{
|
||||
if (coercion->expr == expr)
|
||||
coercion->expr = NULL;
|
||||
|
||||
return coercion;
|
||||
}
|
||||
|
||||
typtype = get_typtype(returning->typid);
|
||||
|
||||
if (returning->typid == RECORDOID ||
|
||||
typtype == TYPTYPE_COMPOSITE ||
|
||||
typtype == TYPTYPE_DOMAIN ||
|
||||
type_is_array(returning->typid))
|
||||
coercion->via_populate = true;
|
||||
else
|
||||
coercion->via_io = true;
|
||||
|
||||
return coercion;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a JSON output clause of JSON_VALUE and JSON_QUERY.
|
||||
*/
|
||||
static void
|
||||
transformJsonFuncExprOutput(ParseState *pstate, JsonFuncExpr *func,
|
||||
JsonExpr *jsexpr)
|
||||
{
|
||||
Node *expr = jsexpr->formatted_expr;
|
||||
|
||||
jsexpr->returning = transformJsonOutput(pstate, func->output, false);
|
||||
|
||||
/* JSON_VALUE returns text by default */
|
||||
if (func->op == JSON_VALUE_OP && !OidIsValid(jsexpr->returning->typid))
|
||||
{
|
||||
jsexpr->returning->typid = TEXTOID;
|
||||
jsexpr->returning->typmod = -1;
|
||||
}
|
||||
|
||||
if (OidIsValid(jsexpr->returning->typid))
|
||||
{
|
||||
JsonReturning ret;
|
||||
|
||||
if (func->op == JSON_VALUE_OP &&
|
||||
jsexpr->returning->typid != JSONOID &&
|
||||
jsexpr->returning->typid != JSONBOID)
|
||||
{
|
||||
/* Forced coercion via I/O for JSON_VALUE for non-JSON types */
|
||||
jsexpr->result_coercion = makeNode(JsonCoercion);
|
||||
jsexpr->result_coercion->expr = NULL;
|
||||
jsexpr->result_coercion->via_io = true;
|
||||
return;
|
||||
}
|
||||
|
||||
assignDefaultJsonReturningType(jsexpr->formatted_expr, jsexpr->format, &ret);
|
||||
|
||||
if (ret.typid != jsexpr->returning->typid ||
|
||||
ret.typmod != jsexpr->returning->typmod)
|
||||
{
|
||||
Node *placeholder = makeCaseTestExpr(expr);
|
||||
|
||||
Assert(((CaseTestExpr *) placeholder)->typeId == ret.typid);
|
||||
Assert(((CaseTestExpr *) placeholder)->typeMod == ret.typmod);
|
||||
|
||||
jsexpr->result_coercion = coerceJsonExpr(pstate, placeholder,
|
||||
jsexpr->returning);
|
||||
}
|
||||
}
|
||||
else
|
||||
assignDefaultJsonReturningType(jsexpr->formatted_expr, jsexpr->format,
|
||||
jsexpr->returning);
|
||||
}
|
||||
|
||||
/*
|
||||
* Coerce a expression in JSON DEFAULT behavior to the target output type.
|
||||
*/
|
||||
static Node *
|
||||
coerceDefaultJsonExpr(ParseState *pstate, JsonExpr *jsexpr, Node *defexpr)
|
||||
{
|
||||
int location;
|
||||
Oid exprtype;
|
||||
|
||||
if (!defexpr)
|
||||
return NULL;
|
||||
|
||||
exprtype = exprType(defexpr);
|
||||
location = exprLocation(defexpr);
|
||||
|
||||
if (location < 0)
|
||||
location = jsexpr->location;
|
||||
|
||||
defexpr = coerce_to_target_type(pstate,
|
||||
defexpr,
|
||||
exprtype,
|
||||
jsexpr->returning->typid,
|
||||
jsexpr->returning->typmod,
|
||||
COERCION_EXPLICIT,
|
||||
COERCE_IMPLICIT_CAST,
|
||||
location);
|
||||
|
||||
if (!defexpr)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CANNOT_COERCE),
|
||||
errmsg("cannot cast DEFAULT expression type %s to %s",
|
||||
format_type_be(exprtype),
|
||||
format_type_be(jsexpr->returning->typid)),
|
||||
parser_errposition(pstate, location)));
|
||||
|
||||
return defexpr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize SQL/JSON item coercion from the SQL type "typid" to the target
|
||||
* "returning" type.
|
||||
*/
|
||||
static JsonCoercion *
|
||||
initJsonItemCoercion(ParseState *pstate, Oid typid,
|
||||
const JsonReturning *returning)
|
||||
{
|
||||
Node *expr;
|
||||
|
||||
if (typid == UNKNOWNOID)
|
||||
{
|
||||
expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid);
|
||||
}
|
||||
else
|
||||
{
|
||||
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
|
||||
|
||||
placeholder->typeId = typid;
|
||||
placeholder->typeMod = -1;
|
||||
placeholder->collation = InvalidOid;
|
||||
|
||||
expr = (Node *) placeholder;
|
||||
}
|
||||
|
||||
return coerceJsonExpr(pstate, expr, returning);
|
||||
}
|
||||
|
||||
static void
|
||||
initJsonItemCoercions(ParseState *pstate, JsonItemCoercions *coercions,
|
||||
const JsonReturning *returning, Oid contextItemTypeId)
|
||||
{
|
||||
struct
|
||||
{
|
||||
JsonCoercion **coercion;
|
||||
Oid typid;
|
||||
} *p,
|
||||
coercionTypids[] =
|
||||
{
|
||||
{ &coercions->null, UNKNOWNOID },
|
||||
{ &coercions->string, TEXTOID },
|
||||
{ &coercions->numeric, NUMERICOID },
|
||||
{ &coercions->boolean, BOOLOID },
|
||||
{ &coercions->date, DATEOID },
|
||||
{ &coercions->time, TIMEOID },
|
||||
{ &coercions->timetz, TIMETZOID },
|
||||
{ &coercions->timestamp, TIMESTAMPOID },
|
||||
{ &coercions->timestamptz, TIMESTAMPTZOID },
|
||||
{ &coercions->composite, contextItemTypeId },
|
||||
{ NULL, InvalidOid }
|
||||
};
|
||||
|
||||
for (p = coercionTypids; p->coercion; p++)
|
||||
*p->coercion = initJsonItemCoercion(pstate, p->typid, returning);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform JSON_VALUE, JSON_QUERY, JSON_EXISTS functions into a JsonExpr node.
|
||||
*/
|
||||
static Node *
|
||||
transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
|
||||
{
|
||||
JsonExpr *jsexpr = transformJsonExprCommon(pstate, func);
|
||||
const char *func_name = NULL;
|
||||
Node *contextItemExpr = jsexpr->formatted_expr;
|
||||
|
||||
switch (func->op)
|
||||
{
|
||||
case JSON_VALUE_OP:
|
||||
func_name = "JSON_VALUE";
|
||||
|
||||
transformJsonFuncExprOutput(pstate, func, jsexpr);
|
||||
|
||||
jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
|
||||
jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
|
||||
|
||||
jsexpr->on_empty->default_expr =
|
||||
coerceDefaultJsonExpr(pstate, jsexpr,
|
||||
jsexpr->on_empty->default_expr);
|
||||
|
||||
jsexpr->on_error->default_expr =
|
||||
coerceDefaultJsonExpr(pstate, jsexpr,
|
||||
jsexpr->on_error->default_expr);
|
||||
|
||||
jsexpr->coercions = makeNode(JsonItemCoercions);
|
||||
initJsonItemCoercions(pstate, jsexpr->coercions, jsexpr->returning,
|
||||
exprType(contextItemExpr));
|
||||
|
||||
break;
|
||||
|
||||
case JSON_QUERY_OP:
|
||||
func_name = "JSON_QUERY";
|
||||
|
||||
transformJsonFuncExprOutput(pstate, func, jsexpr);
|
||||
|
||||
jsexpr->on_empty->default_expr =
|
||||
coerceDefaultJsonExpr(pstate, jsexpr,
|
||||
jsexpr->on_empty->default_expr);
|
||||
|
||||
jsexpr->on_error->default_expr =
|
||||
coerceDefaultJsonExpr(pstate, jsexpr,
|
||||
jsexpr->on_error->default_expr);
|
||||
|
||||
jsexpr->wrapper = func->wrapper;
|
||||
jsexpr->omit_quotes = func->omit_quotes;
|
||||
|
||||
break;
|
||||
|
||||
case JSON_EXISTS_OP:
|
||||
func_name = "JSON_EXISTS";
|
||||
|
||||
jsexpr->returning = transformJsonOutput(pstate, func->output, false);
|
||||
|
||||
jsexpr->returning->format->format_type = JS_FORMAT_DEFAULT;
|
||||
jsexpr->returning->format->encoding = JS_ENC_DEFAULT;
|
||||
|
||||
if (!OidIsValid(jsexpr->returning->typid))
|
||||
{
|
||||
jsexpr->returning->typid = BOOLOID;
|
||||
jsexpr->returning->typmod = -1;
|
||||
}
|
||||
else if (jsexpr->returning->typid != BOOLOID)
|
||||
{
|
||||
CaseTestExpr *placeholder = makeNode(CaseTestExpr);
|
||||
int location = exprLocation((Node *) jsexpr);
|
||||
|
||||
placeholder->typeId = BOOLOID;
|
||||
placeholder->typeMod = -1;
|
||||
placeholder->collation = InvalidOid;
|
||||
|
||||
jsexpr->result_coercion = makeNode(JsonCoercion);
|
||||
jsexpr->result_coercion->expr =
|
||||
coerce_to_target_type(pstate, (Node *) placeholder, BOOLOID,
|
||||
jsexpr->returning->typid,
|
||||
jsexpr->returning->typmod,
|
||||
COERCION_EXPLICIT,
|
||||
COERCE_IMPLICIT_CAST,
|
||||
location);
|
||||
|
||||
if (!jsexpr->result_coercion->expr)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CANNOT_COERCE),
|
||||
errmsg("cannot cast type %s to %s",
|
||||
format_type_be(BOOLOID),
|
||||
format_type_be(jsexpr->returning->typid)),
|
||||
parser_coercion_errposition(pstate, location, (Node *) jsexpr)));
|
||||
|
||||
if (jsexpr->result_coercion->expr == (Node *) placeholder)
|
||||
jsexpr->result_coercion->expr = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (exprType(contextItemExpr) != JSONBOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("%s() is not yet implemented for json type", func_name),
|
||||
parser_errposition(pstate, func->location)));
|
||||
|
||||
return (Node *) jsexpr;
|
||||
}
|
||||
|
@ -1971,6 +1971,21 @@ FigureColnameInternal(Node *node, char **name)
|
||||
case T_JsonArrayAgg:
|
||||
*name = "json_arrayagg";
|
||||
return 2;
|
||||
case T_JsonFuncExpr:
|
||||
/* make SQL/JSON functions act like a regular function */
|
||||
switch (((JsonFuncExpr *) node)->op)
|
||||
{
|
||||
case JSON_QUERY_OP:
|
||||
*name = "json_query";
|
||||
return 2;
|
||||
case JSON_VALUE_OP:
|
||||
*name = "json_value";
|
||||
return 2;
|
||||
case JSON_EXISTS_OP:
|
||||
*name = "json_exists";
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Reference in New Issue
Block a user