diff options
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 553 |
1 files changed, 540 insertions, 13 deletions
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; +} |