1
0
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:
Andrew Dunstan
2022-03-03 13:11:14 -05:00
parent 3d067c53b2
commit 1a36bc9dba
39 changed files with 4719 additions and 124 deletions

View File

@ -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

View File

@ -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:
/*

View File

@ -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;
}

View File

@ -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;
}