v3-0001-Common-SQL-JSON-clauses.patch
application/octet-stream
Filename: v3-0001-Common-SQL-JSON-clauses.patch
Type: application/octet-stream
Part: 10
Message:
Re: SQL/JSON revisited
Patch
Format: format-patch
Series: patch v3-0001
Subject: Common SQL/JSON clauses
| File | + | − |
|---|---|---|
| src/backend/executor/execExpr.c | 22 | 0 |
| src/backend/nodes/makefuncs.c | 54 | 0 |
| src/backend/nodes/nodeFuncs.c | 66 | 0 |
| src/backend/nodes/queryjumblefuncs.c | 26 | 0 |
| src/backend/optimizer/util/clauses.c | 23 | 0 |
| src/backend/parser/gram.y | 63 | 2 |
| src/backend/parser/parse_expr.c | 181 | 0 |
| src/backend/utils/adt/ruleutils.c | 56 | 0 |
| src/include/nodes/makefuncs.h | 5 | 0 |
| src/include/nodes/parsenodes.h | 13 | 0 |
| src/include/nodes/primnodes.h | 59 | 0 |
| src/include/parser/kwlist.h | 2 | 0 |
From 82325ae7b32a761ee2f786e108a1efdd0b0f12e9 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Thu, 3 Mar 2022 13:00:49 -0500
Subject: [PATCH v3 01/11] Common SQL/JSON clauses
This introduces some of the building blocks used by the SQL/JSON
constructor and query functions. Specifically, it provides node
executor and grammar support for the FORMAT JSON [ENCODING foo]
clause, and values decorated with it, and for the RETURNING clause.
The following SQL/JSON patches will leverage these.
Nikita Glukhov (who probably deserves an award for perseverance).
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
---
src/backend/executor/execExpr.c | 22 ++++
src/backend/nodes/makefuncs.c | 54 ++++++++
src/backend/nodes/nodeFuncs.c | 66 ++++++++++
src/backend/nodes/queryjumblefuncs.c | 26 ++++
src/backend/optimizer/util/clauses.c | 23 ++++
src/backend/parser/gram.y | 65 +++++++++-
src/backend/parser/parse_expr.c | 181 +++++++++++++++++++++++++++
src/backend/utils/adt/ruleutils.c | 56 +++++++++
src/include/nodes/makefuncs.h | 5 +
src/include/nodes/parsenodes.h | 13 ++
src/include/nodes/primnodes.h | 59 +++++++++
src/include/parser/kwlist.h | 2 +
12 files changed, 570 insertions(+), 2 deletions(-)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 812ead95bc..177230c7f5 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2401,6 +2401,28 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *jve = (JsonValueExpr *) node;
+
+ ExecInitExprRec(jve->raw_expr, state, resv, resnull);
+
+ if (jve->formatted_expr)
+ {
+ Datum *innermost_caseval = state->innermost_caseval;
+ bool *innermost_isnull = state->innermost_casenull;
+
+ state->innermost_caseval = resv;
+ state->innermost_casenull = resnull;
+
+ ExecInitExprRec(jve->formatted_expr, state, resv, resnull);
+
+ state->innermost_caseval = innermost_caseval;
+ state->innermost_casenull = innermost_isnull;
+ }
+ break;
+ }
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index cad9f28ef5..1a34db0aac 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -19,6 +19,7 @@
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "utils/errcodes.h"
#include "utils/lsyscache.h"
@@ -818,3 +819,56 @@ makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
v->va_cols = va_cols;
return v;
}
+
+/*
+ * makeJsonFormat -
+ * creates a JsonFormat node
+ */
+JsonFormat *
+makeJsonFormat(JsonFormatType type, JsonEncoding encoding, int location)
+{
+ JsonFormat *jf = makeNode(JsonFormat);
+
+ jf->format_type = type;
+ jf->encoding = encoding;
+ jf->location = location;
+
+ return jf;
+}
+
+/*
+ * makeJsonValueExpr -
+ * creates a JsonValueExpr node
+ */
+JsonValueExpr *
+makeJsonValueExpr(Expr *expr, JsonFormat *format)
+{
+ JsonValueExpr *jve = makeNode(JsonValueExpr);
+
+ jve->raw_expr = expr;
+ jve->formatted_expr = NULL;
+ jve->format = format;
+
+ return jve;
+}
+
+/*
+ * makeJsonEncoding -
+ * converts JSON encoding name to enum JsonEncoding
+ */
+JsonEncoding
+makeJsonEncoding(char *name)
+{
+ if (!pg_strcasecmp(name, "utf8"))
+ return JS_ENC_UTF8;
+ if (!pg_strcasecmp(name, "utf16"))
+ return JS_ENC_UTF16;
+ if (!pg_strcasecmp(name, "utf32"))
+ return JS_ENC_UTF32;
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized JSON encoding: %s", name)));
+
+ return JS_ENC_DEFAULT;
+}
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 8fc24c882b..85191dadda 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -249,6 +249,13 @@ exprType(const Node *expr)
case T_PlaceHolderVar:
type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
+ case T_JsonValueExpr:
+ {
+ const JsonValueExpr *jve = (const JsonValueExpr *) expr;
+
+ type = exprType((Node *) (jve->formatted_expr ? jve->formatted_expr : jve->raw_expr));
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
type = InvalidOid; /* keep compiler quiet */
@@ -479,6 +486,8 @@ exprTypmod(const Node *expr)
return ((const SetToDefault *) expr)->typeMod;
case T_PlaceHolderVar:
return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+ case T_JsonValueExpr:
+ return exprTypmod((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
default:
break;
}
@@ -954,6 +963,9 @@ exprCollation(const Node *expr)
case T_PlaceHolderVar:
coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
break;
+ case T_JsonValueExpr:
+ coll = exprCollation((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
coll = InvalidOid; /* keep compiler quiet */
@@ -1161,6 +1173,10 @@ exprSetCollation(Node *expr, Oid collation)
/* NextValueExpr's result is an integer type ... */
Assert(!OidIsValid(collation)); /* ... so never set a collation */
break;
+ case T_JsonValueExpr:
+ exprSetCollation((Node *) ((JsonValueExpr *) expr)->formatted_expr,
+ collation);
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
@@ -1603,6 +1619,9 @@ exprLocation(const Node *expr)
case T_PartitionRangeDatum:
loc = ((const PartitionRangeDatum *) expr)->location;
break;
+ case T_JsonValueExpr:
+ loc = exprLocation((Node *) ((const JsonValueExpr *) expr)->raw_expr);
+ break;
default:
/* for any other node type it's just unknown... */
loc = -1;
@@ -2334,6 +2353,16 @@ expression_tree_walker_impl(Node *node,
return true;
}
break;
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *jve = (JsonValueExpr *) node;
+
+ if (walker(jve->raw_expr, context))
+ return true;
+ if (walker(jve->formatted_expr, context))
+ return true;
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
@@ -2663,6 +2692,7 @@ expression_tree_mutator_impl(Node *node,
case T_RangeTblRef:
case T_SortGroupClause:
case T_CTESearchClause:
+ case T_JsonFormat:
return (Node *) copyObject(node);
case T_WithCheckOption:
{
@@ -3306,6 +3336,28 @@ expression_tree_mutator_impl(Node *node,
return (Node *) newnode;
}
break;
+ case T_JsonReturning:
+ {
+ JsonReturning *jr = (JsonReturning *) node;
+ JsonReturning *newnode;
+
+ FLATCOPY(newnode, jr, JsonReturning);
+ MUTATE(newnode->format, jr->format, JsonFormat *);
+
+ return (Node *) newnode;
+ }
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *jve = (JsonValueExpr *) node;
+ JsonValueExpr *newnode;
+
+ FLATCOPY(newnode, jve, JsonValueExpr);
+ MUTATE(newnode->raw_expr, jve->raw_expr, Expr *);
+ MUTATE(newnode->formatted_expr, jve->formatted_expr, Expr *);
+ MUTATE(newnode->format, jve->format, JsonFormat *);
+
+ return (Node *) newnode;
+ }
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
@@ -4039,6 +4091,20 @@ raw_expression_tree_walker_impl(Node *node,
case T_CommonTableExpr:
/* search_clause and cycle_clause are not interesting here */
return WALK(((CommonTableExpr *) node)->ctequery);
+ case T_JsonReturning:
+ return WALK(((JsonReturning *) node)->format);
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *jve = (JsonValueExpr *) node;
+
+ if (WALK(jve->raw_expr))
+ return true;
+ if (WALK(jve->formatted_expr))
+ return true;
+ if (WALK(jve->format))
+ return true;
+ }
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c
index 16084842a3..243f58df54 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -740,6 +740,32 @@ JumbleExpr(JumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) mergeaction->targetList);
}
break;
+ case T_JsonFormat:
+ {
+ JsonFormat *format = (JsonFormat *) node;
+
+ APP_JUMB(format->type);
+ APP_JUMB(format->encoding);
+ }
+ break;
+ case T_JsonReturning:
+ {
+ JsonReturning *returning = (JsonReturning *) node;
+
+ JumbleExpr(jstate, (Node *) returning->format);
+ APP_JUMB(returning->typid);
+ APP_JUMB(returning->typmod);
+ }
+ break;
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *expr = (JsonValueExpr *) node;
+
+ JumbleExpr(jstate, (Node *) expr->raw_expr);
+ JumbleExpr(jstate, (Node *) expr->formatted_expr);
+ JumbleExpr(jstate, (Node *) expr->format);
+ }
+ break;
case T_List:
foreach(temp, (List *) node)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index aa584848cf..290f5825dd 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -3533,6 +3533,29 @@ eval_const_expressions_mutator(Node *node,
return ece_evaluate_expr((Node *) newcre);
return (Node *) newcre;
}
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *jve = (JsonValueExpr *) node;
+ Node *raw = eval_const_expressions_mutator((Node *) jve->raw_expr,
+ context);
+
+ if (raw && IsA(raw, Const))
+ {
+ Node *formatted;
+ Node *save_case_val = context->case_val;
+
+ context->case_val = raw;
+
+ formatted = eval_const_expressions_mutator((Node *) jve->formatted_expr,
+ context);
+
+ context->case_val = save_case_val;
+
+ if (formatted && IsA(formatted, Const))
+ return formatted;
+ }
+ break;
+ }
default:
break;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a0138382a1..b3fa11817c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -644,6 +644,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <defelt> hash_partbound_elem
+%type <node> json_format_clause_opt
+ json_representation
+ json_value_expr
+ json_output_clause_opt
+
+%type <ival> json_encoding
+ json_encoding_clause_opt
+
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
* They must be listed first so that their numeric codes do not depend on
@@ -695,7 +703,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR
- FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
+ FORCE FOREIGN FORMAT FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS
@@ -706,7 +714,7 @@ 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
+ JOIN JSON
KEY
@@ -792,6 +800,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
/* Precedence: lowest to highest */
%nonassoc SET /* see relation_expr_opt_alias */
+%right FORMAT
%left UNION EXCEPT
%left INTERSECT
%left OR
@@ -16261,6 +16270,54 @@ opt_asymmetric: ASYMMETRIC
| /*EMPTY*/
;
+/* SQL/JSON support */
+
+json_value_expr:
+ a_expr json_format_clause_opt
+ {
+ $$ = (Node *) makeJsonValueExpr((Expr *) $1, $2);
+ }
+ ;
+
+json_format_clause_opt:
+ FORMAT json_representation
+ {
+ $$ = $2;
+ $$.location = @1;
+ }
+ | /* EMPTY */
+ {
+ $$ = (Node *) makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
+ }
+ ;
+
+json_representation:
+ JSON json_encoding_clause_opt
+ {
+ $$ = (Node *) makeJsonFormat(JS_FORMAT_JSON, $2, @1);
+ }
+ /* | other implementation defined JSON representation options (BSON, AVRO etc) */
+ ;
+
+json_encoding_clause_opt:
+ ENCODING json_encoding { $$ = $2; }
+ | /* EMPTY */ { $$ = JS_ENC_DEFAULT; }
+ ;
+
+json_encoding:
+ name { $$ = makeJsonEncoding($1); }
+ ;
+
+json_output_clause_opt:
+ RETURNING Typename json_format_clause_opt
+ {
+ JsonOutput *n = makeNode(JsonOutput);
+ n->typeName = $2;
+ n->returning.format = $3;
+ $$ = (Node *) n;
+ }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
/*****************************************************************************
*
@@ -16808,6 +16865,7 @@ unreserved_keyword:
| FIRST_P
| FOLLOWING
| FORCE
+ | FORMAT
| FORWARD
| FUNCTION
| FUNCTIONS
@@ -16839,6 +16897,7 @@ unreserved_keyword:
| INSTEAD
| INVOKER
| ISOLATION
+ | JSON
| KEY
| LABEL
| LANGUAGE
@@ -17359,6 +17418,7 @@ bare_label_keyword:
| FOLLOWING
| FORCE
| FOREIGN
+ | FORMAT
| FORWARD
| FREEZE
| FULL
@@ -17403,6 +17463,7 @@ bare_label_keyword:
| IS
| ISOLATION
| JOIN
+ | JSON
| KEY
| LABEL
| LANGUAGE
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 53e904ca6d..7c03772918 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -34,6 +34,7 @@
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/date.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "utils/xml.h"
@@ -3041,3 +3042,183 @@ ParseExprKindName(ParseExprKind exprKind)
}
return "unrecognized expression kind";
}
+
+/*
+ * Make string Const node from JSON encoding name.
+ *
+ * UTF8 is default encoding.
+ */
+static Const *
+getJsonEncodingConst(JsonFormat *format)
+{
+ JsonEncoding encoding;
+ const char *enc;
+ Name encname = palloc(sizeof(NameData));
+
+ if (!format ||
+ format->format_type == JS_FORMAT_DEFAULT ||
+ format->encoding == JS_ENC_DEFAULT)
+ encoding = JS_ENC_UTF8;
+ else
+ encoding = format->encoding;
+
+ switch (encoding)
+ {
+ case JS_ENC_UTF16:
+ enc = "UTF16";
+ break;
+ case JS_ENC_UTF32:
+ enc = "UTF32";
+ break;
+ case JS_ENC_UTF8:
+ enc = "UTF8";
+ break;
+ default:
+ elog(ERROR, "invalid JSON encoding: %d", encoding);
+ break;
+ }
+
+ namestrcpy(encname, enc);
+
+ return makeConst(NAMEOID, -1, InvalidOid, NAMEDATALEN,
+ NameGetDatum(encname), false, false);
+}
+
+/*
+ * Make bytea => text conversion using specified JSON format encoding.
+ */
+static Node *
+makeJsonByteaToTextConversion(Node *expr, JsonFormat *format, int location)
+{
+ Const *encoding = getJsonEncodingConst(format);
+ FuncExpr *fexpr = makeFuncExpr(F_CONVERT_FROM, TEXTOID,
+ list_make2(expr, encoding),
+ InvalidOid, InvalidOid,
+ COERCE_EXPLICIT_CALL);
+
+ fexpr->location = location;
+
+ return (Node *) fexpr;
+}
+
+/*
+ * Make CaseTestExpr node.
+ */
+static Node *
+makeCaseTestExpr(Node *expr)
+{
+ CaseTestExpr *placeholder = makeNode(CaseTestExpr);
+
+ placeholder->typeId = exprType(expr);
+ placeholder->typeMod = exprTypmod(expr);
+ placeholder->collation = exprCollation(expr);
+
+ return (Node *) placeholder;
+}
+
+/*
+ * Transform JSON value expression using specified input JSON format or
+ * default format otherwise.
+ */
+static Node *
+transformJsonValueExpr(ParseState *pstate, JsonValueExpr *ve,
+ JsonFormatType default_format)
+{
+ Node *expr = transformExprRecurse(pstate, (Node *) ve->raw_expr);
+ Node *rawexpr;
+ JsonFormatType format;
+ Oid exprtype;
+ int location;
+ char typcategory;
+ bool typispreferred;
+
+ if (exprType(expr) == UNKNOWNOID)
+ expr = coerce_to_specific_type(pstate, expr, TEXTOID, "JSON_VALUE_EXPR");
+
+ rawexpr = expr;
+ exprtype = exprType(expr);
+ location = exprLocation(expr);
+
+ get_type_category_preferred(exprtype, &typcategory, &typispreferred);
+
+ if (ve->format->format_type != JS_FORMAT_DEFAULT)
+ {
+ if (ve->format->encoding != JS_ENC_DEFAULT && exprtype != BYTEAOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("JSON ENCODING clause is only allowed for bytea input type"),
+ parser_errposition(pstate, ve->format->location)));
+
+ if (exprtype == JSONOID || exprtype == JSONBOID)
+ {
+ format = JS_FORMAT_DEFAULT; /* do not format json[b] types */
+ ereport(WARNING,
+ (errmsg("FORMAT JSON has no effect for json and jsonb types"),
+ parser_errposition(pstate, ve->format->location)));
+ }
+ else
+ format = ve->format->format_type;
+ }
+ 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)
+ {
+ Oid targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
+ Node *orig = makeCaseTestExpr(expr);
+ Node *coerced;
+
+ expr = orig;
+
+ if (exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg(ve->format->format_type == JS_FORMAT_DEFAULT ?
+ "cannot use non-string types with implicit FORMAT JSON clause" :
+ "cannot use non-string types with explicit FORMAT JSON clause"),
+ parser_errposition(pstate, ve->format->location >= 0 ?
+ ve->format->location : location)));
+
+ /* Convert encoded JSON text from bytea. */
+ if (format == JS_FORMAT_JSON && exprtype == BYTEAOID)
+ {
+ expr = makeJsonByteaToTextConversion(expr, ve->format, location);
+ exprtype = TEXTOID;
+ }
+
+ /* Try to coerce to the target type. */
+ coerced = coerce_to_target_type(pstate, expr, exprtype,
+ targettype, -1,
+ COERCION_EXPLICIT,
+ COERCE_EXPLICIT_CAST,
+ location);
+
+ if (!coerced)
+ {
+ /* If coercion failed, use to_json()/to_jsonb() functions. */
+ Oid fnoid = targettype == JSONOID ? F_TO_JSON : F_TO_JSONB;
+ FuncExpr *fexpr = makeFuncExpr(fnoid, targettype,
+ list_make1(expr),
+ InvalidOid, InvalidOid,
+ COERCE_EXPLICIT_CALL);
+ fexpr->location = location;
+
+ coerced = (Node *) fexpr;
+ }
+
+ if (coerced == orig)
+ expr = rawexpr;
+ else
+ {
+ ve = copyObject(ve);
+ ve->raw_expr = (Expr *) rawexpr;
+ ve->formatted_expr = (Expr *) coerced;
+
+ expr = (Node *) ve;
+ }
+ }
+
+ return expr;
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9ac42efdbc..3f11beeb54 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8332,6 +8332,11 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
}
+ case T_JsonValueExpr:
+ /* maybe simple, check args */
+ return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
+ node, prettyFlags);
+
default:
break;
}
@@ -8437,6 +8442,48 @@ get_rule_expr_paren(Node *node, deparse_context *context,
appendStringInfoChar(context->buf, ')');
}
+/*
+ * get_json_format - Parse back a JsonFormat node
+ */
+static void
+get_json_format(JsonFormat *format, deparse_context *context)
+{
+ if (format->format_type == JS_FORMAT_DEFAULT)
+ return;
+
+ appendStringInfoString(context->buf,
+ format->format_type == JS_FORMAT_JSONB ?
+ " FORMAT JSONB" : " FORMAT JSON");
+
+ if (format->encoding != JS_ENC_DEFAULT)
+ {
+ const char *encoding =
+ format->encoding == JS_ENC_UTF16 ? "UTF16" :
+ format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
+
+ appendStringInfo(context->buf, " ENCODING %s", encoding);
+ }
+}
+
+/*
+ * get_json_returning - Parse back a JsonReturning structure
+ */
+static void
+get_json_returning(JsonReturning *returning, deparse_context *context,
+ bool json_format_by_default)
+{
+ if (!OidIsValid(returning->typid))
+ return;
+
+ appendStringInfo(context->buf, " RETURNING %s",
+ format_type_with_typemod(returning->typid,
+ returning->typmod));
+
+ if (!json_format_by_default ||
+ returning->format->format_type !=
+ (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
+ get_json_format(returning->format, context);
+}
/* ----------
* get_rule_expr - Parse back an expression
@@ -9535,6 +9582,15 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_JsonValueExpr:
+ {
+ JsonValueExpr *jve = (JsonValueExpr *) node;
+
+ get_rule_expr((Node *) jve->raw_expr, context, false);
+ get_json_format(jve->format, context);
+ }
+ break;
+
case T_List:
{
char *sep;
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 80f1d5336b..ea3fd07b30 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -106,4 +106,9 @@ extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int loc
extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
+extern JsonFormat *makeJsonFormat(JsonFormatType type, JsonEncoding encoding,
+ int location);
+extern JsonValueExpr *makeJsonValueExpr(Expr *expr, JsonFormat *format);
+extern JsonEncoding makeJsonEncoding(char *name);
+
#endif /* MAKEFUNC_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 89335d95e7..97528fa553 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1687,6 +1687,19 @@ typedef struct TriggerTransition
bool isTable;
} TriggerTransition;
+/* Nodes for SQL/JSON support */
+
+/*
+ * JsonOutput -
+ * representation of JSON output clause (RETURNING type [FORMAT format])
+ */
+typedef struct JsonOutput
+{
+ NodeTag type;
+ TypeName *typeName; /* RETURNING type name, if specified */
+ JsonReturning *returning; /* RETURNING FORMAT clause and type Oids */
+} JsonOutput;
+
/*****************************************************************************
* Raw Grammar Output Statements
*****************************************************************************/
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index dec7d5c775..a4c73bb344 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1448,6 +1448,65 @@ typedef struct XmlExpr
int location;
} XmlExpr;
+/*
+ * JsonEncoding -
+ * representation of JSON ENCODING clause
+ */
+typedef enum JsonEncoding
+{
+ JS_ENC_DEFAULT, /* unspecified */
+ JS_ENC_UTF8,
+ JS_ENC_UTF16,
+ JS_ENC_UTF32,
+} JsonEncoding;
+
+/*
+ * JsonFormatType -
+ * enumeration of JSON formats used in JSON FORMAT clause
+ */
+typedef enum JsonFormatType
+{
+ JS_FORMAT_DEFAULT, /* unspecified */
+ JS_FORMAT_JSON, /* FORMAT JSON [ENCODING ...] */
+ JS_FORMAT_JSONB /* implicit internal format for RETURNING jsonb */
+} JsonFormatType;
+
+/*
+ * JsonFormat -
+ * representation of JSON FORMAT clause
+ */
+typedef struct JsonFormat
+{
+ NodeTag type;
+ JsonFormatType format_type; /* format type */
+ JsonEncoding encoding; /* JSON encoding */
+ int location; /* token location, or -1 if unknown */
+} JsonFormat;
+
+/*
+ * JsonReturning -
+ * transformed representation of JSON RETURNING clause
+ */
+typedef struct JsonReturning
+{
+ NodeTag type;
+ JsonFormat *format; /* output JSON format */
+ Oid typid; /* target type Oid */
+ int32 typmod; /* target type modifier */
+} JsonReturning;
+
+/*
+ * JsonValueExpr -
+ * representation of JSON value expression (expr [FORMAT json_format])
+ */
+typedef struct JsonValueExpr
+{
+ NodeTag type;
+ Expr *raw_expr; /* raw expression */
+ Expr *formatted_expr; /* formatted expression or NULL */
+ JsonFormat *format; /* FORMAT clause, if specified */
+} JsonValueExpr;
+
/* ----------------
* NullTest
*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index bb36213e6f..ba90024103 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -175,6 +175,7 @@ PG_KEYWORD("following", FOLLOWING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("for", FOR, RESERVED_KEYWORD, AS_LABEL)
PG_KEYWORD("force", FORCE, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("foreign", FOREIGN, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("format", FORMAT, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("forward", FORWARD, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("freeze", FREEZE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("from", FROM, RESERVED_KEYWORD, AS_LABEL)
@@ -227,6 +228,7 @@ PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json", JSON, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD, BARE_LABEL)
--
2.35.3