<primary>JSON</primary>
    <secondary>functions and operators</secondary>
   </indexterm>
+   <indexterm zone="functions-json">
+    <primary>SQL/JSON</primary>
+    <secondary>functions and expressions</secondary>
+   </indexterm>
 
   <para>
    This section describes:
    </itemizedlist>
   </para>
 
+  <para>
+   To provide native support for JSON data types within the SQL environment,
+   <productname>PostgreSQL</productname> implements the
+   <firstterm>SQL/JSON data model</firstterm>.
+   This model comprises sequences of items. Each item can hold SQL scalar
+   values, with an additional SQL/JSON null value, and composite data structures
+   that use JSON arrays and objects. The model is a formalization of the implied
+   data model in the JSON specification
+   <ulink url="https://tools.ietf.org/html/rfc7159">RFC 7159</ulink>.
+  </para>
+
+  <para>
+   SQL/JSON allows you to handle JSON data alongside regular SQL data,
+   with transaction support, including:
+
+  <itemizedlist>
+   <listitem>
+    <para>
+     Uploading JSON data into the database and storing it in
+     regular SQL columns as character or binary strings.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Generating JSON objects and arrays from relational data.
+    </para>
+   </listitem>
+   <listitem>
+    <para>
+     Querying JSON data using SQL/JSON query functions and
+     SQL/JSON path language expressions.
+    </para>
+   </listitem>
+  </itemizedlist>
+  </para>
+
   <para>
    To learn more about the SQL/JSON standard, see
    <xref linkend="sqltr-19075-6"/>. For details on JSON types
   <para>
    <xref linkend="functions-json-creation-table"/> shows the functions that are
    available for constructing <type>json</type> and <type>jsonb</type> values.
+   Some functions in this table have a <literal>RETURNING</literal> clause,
+   which specifies the data type returned.  It must be one of <type>json</type>,
+   <type>jsonb</type>, <type>bytea</type>, a character string type (<type>text</type>,
+   <type>char</type>, <type>varchar</type>, or <type>nchar</type>), or a type
+   for which there is a cast from <type>json</type> to that type.
+   By default, the <type>json</type> type is returned.
   </para>
 
   <table id="functions-json-creation-table">
        </para></entry>
       </row>
 
+      <row>
+       <!--
+           Note that this is barely legible in the output; it looks like a
+           salad of braces and brackets.  It would be better to split it out
+           in multiple lines, but that's surprisingly hard to do in a way that
+           matches in HTML and PDF output.  Other standard SQL/JSON functions
+           have the same problem.
+         -->
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm><primary>json_array</primary></indexterm>
+         <function>json_array</function> (
+         <optional> { <replaceable>value_expression</replaceable> <optional> <literal>FORMAT JSON</literal> </optional> } <optional>, ...</optional> </optional>
+         <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
+         <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
+        </para>
+        <para role="func_signature">
+         <function>json_array</function> (
+         <optional> <replaceable>query_expression</replaceable> </optional>
+         <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
+        </para>
+        <para>
+         Constructs a JSON array from either a series of
+         <replaceable>value_expression</replaceable> parameters or from the results
+         of <replaceable>query_expression</replaceable>,
+         which must be a SELECT query returning a single column. If
+         <literal>ABSENT ON NULL</literal> is specified, NULL values are ignored.
+         This is always the case if a
+         <replaceable>query_expression</replaceable> is used.
+        </para>
+        <para>
+         <literal>json_array(1,true,json '{"a":null}')</literal>
+         <returnvalue>[1, true, {"a":null}]</returnvalue>
+        </para>
+        <para>
+         <literal>json_array(SELECT * FROM (VALUES(1),(2)) t)</literal>
+         <returnvalue>[1, 2]</returnvalue>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+         <indexterm><primary>json_object</primary></indexterm>
+         <function>json_object</function> (
+         <optional> { <replaceable>key_expression</replaceable> { <literal>VALUE</literal> | ':' }
+          <replaceable>value_expression</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> }<optional>, ...</optional> </optional>
+         <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
+         <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional> </optional>
+         <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
+        </para>
+        <para>
+         Constructs a JSON object of all the key/value pairs given,
+         or an empty object if none are given.
+         <replaceable>key_expression</replaceable> is a scalar expression
+         defining the <acronym>JSON</acronym> key, which is
+         converted to the <type>text</type> type.
+         It cannot be <literal>NULL</literal> nor can it
+         belong to a type that has a cast to the <type>json</type> type.
+         If <literal>WITH UNIQUE KEYS</literal> is specified, there must not
+         be any duplicate <replaceable>key_expression</replaceable>.
+         Any pair for which the <replaceable>value_expression</replaceable>
+         evaluates to <literal>NULL</literal> is omitted from the output
+         if <literal>ABSENT ON NULL</literal> is specified;
+         if <literal>NULL ON NULL</literal> is specified or the clause
+         omitted, the key is included with value <literal>NULL</literal>.
+        </para>
+        <para>
+         <literal>json_object('code' VALUE 'P123', 'title': 'Jaws')</literal>
+         <returnvalue>{"code" : "P123", "title" : "Jaws"}</returnvalue>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
        <entry>No</entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+         <indexterm><primary>json_objectagg</primary></indexterm>
+         <function>json_objectagg</function> (
+         <optional> { <replaceable>key_expression</replaceable> { <literal>VALUE</literal> | ':' } <replaceable>value_expression</replaceable> } </optional>
+         <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
+        <optional> { <literal>WITH</literal> | <literal>WITHOUT</literal> } <literal>UNIQUE</literal> <optional> <literal>KEYS</literal> </optional> </optional>
+        <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
+        </para>
+        <para>
+         Behaves like <function>json_object</function><!-- xref -->, but as an
+         aggregate function, so it only takes one
+         <replaceable>key_expression</replaceable> and one
+         <replaceable>value_expression</replaceable> parameter.
+        </para>
+        <para>
+         <literal>SELECT json_objectagg(k:v) FROM (VALUES ('a'::text,current_date),('b',current_date + 1)) AS t(k,v)</literal>
+         <returnvalue>{ "a" : "2022-05-10", "b" : "2022-05-11" }</returnvalue>
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
        </para>
        <para>
         Collects all the key/value pairs into a JSON object.  Key arguments
-        are coerced to text; value arguments are converted as
-        per <function>to_json</function> or <function>to_jsonb</function>.
-        Values can be null, but not keys.
+        are coerced to text; value arguments are converted as per
+        <function>to_json</function> or <function>to_jsonb</function>.
+        Values can be null, but keys cannot.
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>json_object_agg_strict</primary>
+        </indexterm>
+        <function>json_object_agg_strict</function> (
+         <parameter>key</parameter> <type>"any"</type>,
+         <parameter>value</parameter> <type>"any"</type> )
+        <returnvalue>json</returnvalue>
+       </para>
+       <para role="func_signature">
+        <indexterm>
+         <primary>jsonb_object_agg_strict</primary>
+        </indexterm>
+        <function>jsonb_object_agg_strict</function> (
+         <parameter>key</parameter> <type>"any"</type>,
+         <parameter>value</parameter> <type>"any"</type> )
+        <returnvalue>jsonb</returnvalue>
+       </para>
+       <para>
+        Collects all the key/value pairs into a JSON object.  Key arguments
+        are coerced to text; value arguments are converted as per
+        <function>to_json</function> or <function>to_jsonb</function>.
+        The <parameter>key</parameter> can not be null. If the
+        <parameter>value</parameter> is null then the entry is skipped,
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>json_object_agg_unique</primary>
+        </indexterm>
+        <function>json_object_agg_unique</function> (
+         <parameter>key</parameter> <type>"any"</type>,
+         <parameter>value</parameter> <type>"any"</type> )
+        <returnvalue>json</returnvalue>
+       </para>
+       <para role="func_signature">
+        <indexterm>
+         <primary>jsonb_object_agg_unique</primary>
+        </indexterm>
+        <function>jsonb_object_agg_unique</function> (
+         <parameter>key</parameter> <type>"any"</type>,
+         <parameter>value</parameter> <type>"any"</type> )
+        <returnvalue>jsonb</returnvalue>
+       </para>
+       <para>
+        Collects all the key/value pairs into a JSON object.  Key arguments
+        are coerced to text; value arguments are converted as per
+        <function>to_json</function> or <function>to_jsonb</function>.
+        Values can be null, but keys cannot.
+        If there is a duplicate key an error is thrown.
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm><primary>json_arrayagg</primary></indexterm>
+        <function>json_arrayagg</function> (
+        <optional> <replaceable>value_expression</replaceable> </optional>
+        <optional> <literal>ORDER BY</literal> <replaceable>sort_expression</replaceable> </optional>
+        <optional> { <literal>NULL</literal> | <literal>ABSENT</literal> } <literal>ON NULL</literal> </optional>
+        <optional> <literal>RETURNING</literal> <replaceable>data_type</replaceable> <optional> <literal>FORMAT JSON</literal> <optional> <literal>ENCODING UTF8</literal> </optional> </optional> </optional>)
+       </para>
+       <para>
+        Behaves in the same way as <function>json_array</function>
+        but as an aggregate function so it only takes one
+        <replaceable>value_expression</replaceable> parameter.
+        If <literal>ABSENT ON NULL</literal> is specified, any NULL
+        values are omitted.
+        If <literal>ORDER BY</literal> is specified, the elements will
+        appear in the array in that order rather than in the input order.
+       </para>
+       <para>
+        <literal>SELECT json_arrayagg(v) FROM (VALUES(2),(1)) t(v)</literal>
+        <returnvalue>[2, 1]</returnvalue>
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>json_object_agg_unique_strict</primary>
+        </indexterm>
+        <function>json_object_agg_unique_strict</function> (
+         <parameter>key</parameter> <type>"any"</type>,
+         <parameter>value</parameter> <type>"any"</type> )
+        <returnvalue>json</returnvalue>
+       </para>
+       <para role="func_signature">
+        <indexterm>
+         <primary>jsonb_object_agg_unique_strict</primary>
+        </indexterm>
+        <function>jsonb_object_agg_unique_strict</function> (
+         <parameter>key</parameter> <type>"any"</type>,
+         <parameter>value</parameter> <type>"any"</type> )
+        <returnvalue>jsonb</returnvalue>
+       </para>
+       <para>
+        Collects all the key/value pairs into a JSON object.  Key arguments
+        are coerced to text; value arguments are converted as per
+        <function>to_json</function> or <function>to_jsonb</function>.
+        The <parameter>key</parameter> can not be null. If the
+        <parameter>value</parameter> is null then the entry is skipped.
+        If there is a duplicate key an error is thrown.
        </para></entry>
        <entry>No</entry>
       </row>
        <entry>No</entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>json_agg_strict</primary>
+        </indexterm>
+        <function>json_agg_strict</function> ( <type>anyelement</type> )
+        <returnvalue>json</returnvalue>
+       </para>
+       <para role="func_signature">
+        <indexterm>
+         <primary>jsonb_agg_strict</primary>
+        </indexterm>
+        <function>jsonb_agg_strict</function> ( <type>anyelement</type> )
+        <returnvalue>jsonb</returnvalue>
+       </para>
+       <para>
+        Collects all the input values, skipping nulls, into a JSON array.
+        Values are converted to JSON as per <function>to_json</function>
+        or <function>to_jsonb</function>.
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
   <para>
    The aggregate functions <function>array_agg</function>,
    <function>json_agg</function>, <function>jsonb_agg</function>,
+   <function>json_agg_strict</function>, <function>jsonb_agg_strict</function>,
    <function>json_object_agg</function>, <function>jsonb_object_agg</function>,
+   <function>json_object_agg_strict</function>, <function>jsonb_object_agg_strict</function>,
+   <function>json_object_agg_unique</function>, <function>jsonb_object_agg_unique</function>,
+   <function>json_object_agg_unique_strict</function>,
+   <function>jsonb_object_agg_unique_strict</function>,
    <function>string_agg</function>,
    and <function>xmlagg</function>, as well as similar user-defined
    aggregate functions, produce meaningfully different result values
 
                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;
+           }
+
+       case T_JsonConstructorExpr:
+           {
+               JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
+               List       *args = ctor->args;
+               ListCell   *lc;
+               int         nargs = list_length(args);
+               int         argno = 0;
+
+               if (ctor->func)
+               {
+                   ExecInitExprRec(ctor->func, state, resv, resnull);
+               }
+               else
+               {
+                   JsonConstructorExprState *jcstate;
+
+                   jcstate = palloc0(sizeof(JsonConstructorExprState));
+
+                   scratch.opcode = EEOP_JSON_CONSTRUCTOR;
+                   scratch.d.json_constructor.jcstate = jcstate;
+
+                   jcstate->constructor = ctor;
+                   jcstate->arg_values = (Datum *) palloc(sizeof(Datum) * nargs);
+                   jcstate->arg_nulls = (bool *) palloc(sizeof(bool) * nargs);
+                   jcstate->arg_types = (Oid *) palloc(sizeof(Oid) * nargs);
+                   jcstate->nargs = nargs;
+
+                   foreach(lc, args)
+                   {
+                       Expr       *arg = (Expr *) lfirst(lc);
+
+                       jcstate->arg_types[argno] = exprType((Node *) arg);
+
+                       if (IsA(arg, Const))
+                       {
+                           /* Don't evaluate const arguments every round */
+                           Const      *con = (Const *) arg;
+
+                           jcstate->arg_values[argno] = con->constvalue;
+                           jcstate->arg_nulls[argno] = con->constisnull;
+                       }
+                       else
+                       {
+                           ExecInitExprRec(arg, state,
+                                           &jcstate->arg_values[argno],
+                                           &jcstate->arg_nulls[argno]);
+                       }
+                       argno++;
+                   }
+
+                   ExprEvalPushStep(state, &scratch);
+               }
+
+               if (ctor->coercion)
+               {
+                   Datum      *innermost_caseval = state->innermost_caseval;
+                   bool       *innermost_isnull = state->innermost_casenull;
+
+                   state->innermost_caseval = resv;
+                   state->innermost_casenull = resnull;
+
+                   ExecInitExprRec(ctor->coercion, state, resv, resnull);
+
+                   state->innermost_caseval = innermost_caseval;
+                   state->innermost_casenull = innermost_isnull;
+               }
+           }
+           break;
+
        case T_NullTest:
            {
                NullTest   *ntest = (NullTest *) node;
 
 #include "utils/date.h"
 #include "utils/datum.h"
 #include "utils/expandedrecord.h"
+#include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/timestamp.h"
        &&CASE_EEOP_SCALARARRAYOP,
        &&CASE_EEOP_HASHED_SCALARARRAYOP,
        &&CASE_EEOP_XMLEXPR,
+       &&CASE_EEOP_JSON_CONSTRUCTOR,
        &&CASE_EEOP_AGGREF,
        &&CASE_EEOP_GROUPING_FUNC,
        &&CASE_EEOP_WINDOW_FUNC,
            EEO_NEXT();
        }
 
+       EEO_CASE(EEOP_JSON_CONSTRUCTOR)
+       {
+           /* too complex for an inline implementation */
+           ExecEvalJsonConstructor(state, op, econtext);
+           EEO_NEXT();
+       }
+
        EEO_CASE(EEOP_AGGREF)
        {
            /*
 
    MemoryContextSwitchTo(oldContext);
 }
+
+/*
+ * Evaluate a JSON constructor expression.
+ */
+void
+ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
+                       ExprContext *econtext)
+{
+   Datum       res;
+   JsonConstructorExprState *jcstate = op->d.json_constructor.jcstate;
+   JsonConstructorExpr *ctor = jcstate->constructor;
+   bool        is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
+   bool        isnull = false;
+
+   if (ctor->type == JSCTOR_JSON_ARRAY)
+       res = (is_jsonb ?
+              jsonb_build_array_worker :
+              json_build_array_worker) (jcstate->nargs,
+                                        jcstate->arg_values,
+                                        jcstate->arg_nulls,
+                                        jcstate->arg_types,
+                                        jcstate->constructor->absent_on_null);
+   else if (ctor->type == JSCTOR_JSON_OBJECT)
+       res = (is_jsonb ?
+              jsonb_build_object_worker :
+              json_build_object_worker) (jcstate->nargs,
+                                         jcstate->arg_values,
+                                         jcstate->arg_nulls,
+                                         jcstate->arg_types,
+                                         jcstate->constructor->absent_on_null,
+                                         jcstate->constructor->unique);
+   else
+   {
+       res = (Datum) 0;
+       elog(ERROR, "invalid JsonConstructorExpr type %d", ctor->type);
+   }
+
+   *op->resvalue = res;
+   *op->resnull = isnull;
+}
 
                LLVMBuildBr(b, opblocks[opno + 1]);
                break;
 
+           case EEOP_JSON_CONSTRUCTOR:
+               build_EvalXFunc(b, mod, "ExecEvalJsonConstructor",
+                               v_state, op, v_econtext);
+               LLVMBuildBr(b, opblocks[opno + 1]);
+               break;
+
            case EEOP_AGGREF:
                {
                    LLVMValueRef v_aggno;
 
    ExecEvalSysVar,
    ExecEvalWholeRowVar,
    ExecEvalXmlExpr,
+   ExecEvalJsonConstructor,
    MakeExpandedObjectReadOnlyInternal,
    slot_getmissingattrs,
    slot_getsomeattrs_int,
 
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "utils/errcodes.h"
 #include "utils/lsyscache.h"
 
 
    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;
+}
+
+/*
+ * makeJsonKeyValue -
+ *   creates a JsonKeyValue node
+ */
+Node *
+makeJsonKeyValue(Node *key, Node *value)
+{
+   JsonKeyValue *n = makeNode(JsonKeyValue);
+
+   n->key = (Expr *) key;
+   n->value = castNode(JsonValueExpr, value);
+
+   return (Node *) n;
+}
 
        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;
+       case T_JsonConstructorExpr:
+           type = ((const JsonConstructorExpr *) expr)->returning->typid;
+           break;
        default:
            elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
            type = InvalidOid;  /* keep compiler quiet */
            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);
+       case T_JsonConstructorExpr:
+           return -1;          /* XXX maybe expr->returning->typmod? */
        default:
            break;
    }
        case T_PlaceHolderVar:
            coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
            break;
+       case T_JsonValueExpr:
+           coll = exprCollation((Node *) ((const JsonValueExpr *) expr)->formatted_expr);
+           break;
+       case T_JsonConstructorExpr:
+           {
+               const JsonConstructorExpr *ctor = (const JsonConstructorExpr *) expr;
+
+               if (ctor->coercion)
+                   coll = exprCollation((Node *) ctor->coercion);
+               else
+                   coll = InvalidOid;
+           }
+           break;
        default:
            elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
            coll = InvalidOid;  /* keep compiler quiet */
            /* 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;
+       case T_JsonConstructorExpr:
+           {
+               JsonConstructorExpr *ctor = (JsonConstructorExpr *) expr;
+
+               if (ctor->coercion)
+                   exprSetCollation((Node *) ctor->coercion, collation);
+               else
+                   Assert(!OidIsValid(collation)); /* result is always a
+                                                    * json[b] type */
+           }
+           break;
        default:
            elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
            break;
        case T_PartitionRangeDatum:
            loc = ((const PartitionRangeDatum *) expr)->location;
            break;
+       case T_JsonValueExpr:
+           loc = exprLocation((Node *) ((const JsonValueExpr *) expr)->raw_expr);
+           break;
+       case T_JsonConstructorExpr:
+           loc = ((const JsonConstructorExpr *) expr)->location;
+           break;
        default:
            /* for any other node type it's just unknown... */
            loc = -1;
                    return true;
            }
            break;
+       case T_JsonValueExpr:
+           {
+               JsonValueExpr *jve = (JsonValueExpr *) node;
+
+               if (WALK(jve->raw_expr))
+                   return true;
+               if (WALK(jve->formatted_expr))
+                   return true;
+           }
+           break;
+       case T_JsonConstructorExpr:
+           {
+               JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
+
+               if (WALK(ctor->args))
+                   return true;
+               if (WALK(ctor->func))
+                   return true;
+               if (WALK(ctor->coercion))
+                   return true;
+           }
+           break;
        default:
            elog(ERROR, "unrecognized node type: %d",
                 (int) nodeTag(node));
        case T_RangeTblRef:
        case T_SortGroupClause:
        case T_CTESearchClause:
+       case T_JsonFormat:
            return (Node *) copyObject(node);
        case T_WithCheckOption:
            {
                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;
+           }
+       case T_JsonConstructorExpr:
+           {
+               JsonConstructorExpr *jve = (JsonConstructorExpr *) node;
+               JsonConstructorExpr *newnode;
+
+               FLATCOPY(newnode, jve, JsonConstructorExpr);
+               MUTATE(newnode->args, jve->args, List *);
+               MUTATE(newnode->func, jve->func, Expr *);
+               MUTATE(newnode->coercion, jve->coercion, Expr *);
+               MUTATE(newnode->returning, jve->returning, JsonReturning *);
+
+               return (Node *) newnode;
+           }
        default:
            elog(ERROR, "unrecognized node type: %d",
                 (int) nodeTag(node));
        case T_ParamRef:
        case T_A_Const:
        case T_A_Star:
+       case T_JsonFormat:
            /* primitive node types with no subnodes */
            break;
        case T_Alias:
        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;
+       case T_JsonConstructorExpr:
+           {
+               JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
+
+               if (WALK(ctor->args))
+                   return true;
+               if (WALK(ctor->func))
+                   return true;
+               if (WALK(ctor->coercion))
+                   return true;
+               if (WALK(ctor->returning))
+                   return true;
+           }
+           break;
+       case T_JsonOutput:
+           {
+               JsonOutput *out = (JsonOutput *) node;
+
+               if (WALK(out->typeName))
+                   return true;
+               if (WALK(out->returning))
+                   return true;
+           }
+           break;
+       case T_JsonKeyValue:
+           {
+               JsonKeyValue *jkv = (JsonKeyValue *) node;
+
+               if (WALK(jkv->key))
+                   return true;
+               if (WALK(jkv->value))
+                   return true;
+           }
+           break;
+       case T_JsonObjectConstructor:
+           {
+               JsonObjectConstructor *joc = (JsonObjectConstructor *) node;
+
+               if (WALK(joc->output))
+                   return true;
+               if (WALK(joc->exprs))
+                   return true;
+           }
+           break;
+       case T_JsonArrayConstructor:
+           {
+               JsonArrayConstructor *jac = (JsonArrayConstructor *) node;
+
+               if (WALK(jac->output))
+                   return true;
+               if (WALK(jac->exprs))
+                   return true;
+           }
+           break;
+       case T_JsonAggConstructor:
+           {
+               JsonAggConstructor *ctor = (JsonAggConstructor *) node;
+
+               if (WALK(ctor->output))
+                   return true;
+               if (WALK(ctor->agg_order))
+                   return true;
+               if (WALK(ctor->agg_filter))
+                   return true;
+               if (WALK(ctor->over))
+                   return true;
+           }
+           break;
+       case T_JsonObjectAgg:
+           {
+               JsonObjectAgg *joa = (JsonObjectAgg *) node;
+
+               if (WALK(joa->constructor))
+                   return true;
+               if (WALK(joa->arg))
+                   return true;
+           }
+           break;
+       case T_JsonArrayAgg:
+           {
+               JsonArrayAgg *jaa = (JsonArrayAgg *) node;
+
+               if (WALK(jaa->constructor))
+                   return true;
+               if (WALK(jaa->arg))
+                   return true;
+           }
+           break;
+       case T_JsonArrayQueryConstructor:
+           {
+               JsonArrayQueryConstructor *jaqc = (JsonArrayQueryConstructor *) node;
+
+               if (WALK(jaqc->output))
+                   return true;
+               if (WALK(jaqc->query))
+                   return true;
+           }
+           break;
        default:
            elog(ERROR, "unrecognized node type: %d",
                 (int) nodeTag(node));
 
 #include "utils/builtins.h"
 #include "utils/datum.h"
 #include "utils/fmgroids.h"
+#include "utils/json.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/syscache.h"
                                context))
        return true;
 
+   if (IsA(node, JsonConstructorExpr))
+   {
+       const JsonConstructorExpr *ctor = (JsonConstructorExpr *) node;
+       ListCell   *lc;
+       bool        is_jsonb;
+
+       is_jsonb = ctor->returning->format->format_type == JS_FORMAT_JSONB;
+
+       /*
+        * Check argument_type => json[b] conversions specifically.  We still
+        * recurse to check 'args' below, but here we want to specifically
+        * check whether or not the emitted clause would fail to be immutable
+        * because of TimeZone, for example.
+        */
+       foreach(lc, ctor->args)
+       {
+           Oid         typid = exprType(lfirst(lc));
+
+           if (is_jsonb ?
+               !to_jsonb_is_immutable(typid) :
+               !to_json_is_immutable(typid))
+               return true;
+       }
+
+       /* Check all subnodes */
+   }
+
    if (IsA(node, NextValueExpr))
    {
        /* NextValueExpr is volatile */
                }
                break;
            }
+
+       case T_JsonValueExpr:
+           {
+               JsonValueExpr *jve = (JsonValueExpr *) node;
+               Node       *raw;
+
+               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;
+           }
+
        case T_SubPlan:
        case T_AlternativeSubPlan:
 
 
 %type <defelt>     hash_partbound_elem
 
 
+%type <node>       json_format_clause_opt
+                   json_value_expr
+                   json_output_clause_opt
+                   json_name_and_value
+                   json_aggregate_func
+
+%type <list>       json_name_and_value_list
+                   json_value_expr_list
+                   json_array_aggregate_order_by_clause_opt
+
+%type <ival>       json_encoding_clause_opt
+
+%type <boolean>        json_key_uniqueness_constraint_opt
+                   json_object_constructor_null_clause_opt
+                   json_array_constructor_null_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
  */
 
 /* ordinary key words in alphabetical order */
-%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
+%token <keyword> ABORT_P ABSENT ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
    AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
    ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC ATOMIC AT ATTACH ATTRIBUTE AUTHORIZATION
 
    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
 
    INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
    INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
 
-   JOIN
+   JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_OBJECT JSON_OBJECTAGG
 
-   KEY
+   KEY KEYS
 
    LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
    LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  * NOT_LA exists so that productions such as NOT LIKE can be given the same
  * precedence as LIKE; otherwise they'd effectively have the same precedence
  * as NOT, at least with respect to their left-hand subexpression.
- * NULLS_LA and WITH_LA are needed to make the grammar LALR(1).
+ * FORMAT_LA, NULLS_LA, WITH_LA, and WITHOUT_LA are needed to make the grammar
+ * LALR(1).
  */
-%token     NOT_LA NULLS_LA WITH_LA
+%token     FORMAT_LA NOT_LA NULLS_LA WITH_LA WITHOUT_LA
 
 /*
  * The grammar likewise thinks these tokens are keywords, but they are never
 
 /* Precedence: lowest to highest */
 %nonassoc  SET             /* see relation_expr_opt_alias */
+%right     FORMAT
 %left      UNION EXCEPT
 %left      INTERSECT
 %left      OR
 utility_option_name:
            NonReservedWord                         { $$ = $1; }
            | analyze_keyword                       { $$ = "analyze"; }
+           | FORMAT_LA                             { $$ = "format"; }
        ;
 
 utility_option_arg:
                    n->over = $4;
                    $$ = (Node *) n;
                }
+           | json_aggregate_func filter_clause over_clause
+               {
+                   JsonAggConstructor *n = IsA($1, JsonObjectAgg) ?
+                       ((JsonObjectAgg *) $1)->constructor :
+                       ((JsonArrayAgg *) $1)->constructor;
+
+                   n->agg_filter = $2;
+                   n->over = $3;
+                   $$ = (Node *) $1;
+               }
            | func_expr_common_subexpr
                { $$ = $1; }
        ;
 func_expr_windowless:
            func_application                        { $$ = $1; }
            | func_expr_common_subexpr              { $$ = $1; }
+           | json_aggregate_func                   { $$ = $1; }
        ;
 
 /*
                    n->location = @1;
                    $$ = (Node *) n;
                }
+           | JSON_OBJECT '(' func_arg_list ')'
+               {
+                   /* Support for legacy (non-standard) json_object() */
+                   $$ = (Node *) makeFuncCall(SystemFuncName("json_object"),
+                                              $3, COERCE_EXPLICIT_CALL, @1);
+               }
+           | JSON_OBJECT '(' json_name_and_value_list
+               json_object_constructor_null_clause_opt
+               json_key_uniqueness_constraint_opt
+               json_output_clause_opt ')'
+               {
+                   JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
+
+                   n->exprs = $3;
+                   n->absent_on_null = $4;
+                   n->unique = $5;
+                   n->output = (JsonOutput *) $6;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
+           | JSON_OBJECT '(' json_output_clause_opt ')'
+               {
+                   JsonObjectConstructor *n = makeNode(JsonObjectConstructor);
+
+                   n->exprs = NULL;
+                   n->absent_on_null = false;
+                   n->unique = false;
+                   n->output = (JsonOutput *) $3;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
+           | JSON_ARRAY '('
+               json_value_expr_list
+               json_array_constructor_null_clause_opt
+               json_output_clause_opt
+           ')'
+               {
+                   JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
+
+                   n->exprs = $3;
+                   n->absent_on_null = $4;
+                   n->output = (JsonOutput *) $5;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
+           | JSON_ARRAY '('
+               select_no_parens
+               json_format_clause_opt
+               /* json_array_constructor_null_clause_opt */
+               json_output_clause_opt
+           ')'
+               {
+                   JsonArrayQueryConstructor *n = makeNode(JsonArrayQueryConstructor);
+
+                   n->query = $3;
+                   n->format = (JsonFormat *) $4;
+                   n->absent_on_null = true;   /* XXX */
+                   n->output = (JsonOutput *) $5;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
+           | JSON_ARRAY '('
+               json_output_clause_opt
+           ')'
+               {
+                   JsonArrayConstructor *n = makeNode(JsonArrayConstructor);
+
+                   n->exprs = NIL;
+                   n->absent_on_null = true;
+                   n->output = (JsonOutput *) $3;
+                   n->location = @1;
+                   $$ = (Node *) n;
+               }
        ;
 
 /*
            | /*EMPTY*/
        ;
 
+/* SQL/JSON support */
+json_value_expr:
+           a_expr json_format_clause_opt
+           {
+               $$ = (Node *) makeJsonValueExpr((Expr *) $1, castNode(JsonFormat, $2));
+           }
+       ;
+
+json_format_clause_opt:
+           FORMAT_LA JSON json_encoding_clause_opt
+               {
+                   $$ = (Node *) makeJsonFormat(JS_FORMAT_JSON, $3, @1);
+               }
+           | /* EMPTY */
+               {
+                   $$ = (Node *) makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
+               }
+       ;
+
+json_encoding_clause_opt:
+           ENCODING name                   { $$ = makeJsonEncoding($2); }
+           | /* EMPTY */                   { $$ = JS_ENC_DEFAULT; }
+       ;
+
+json_output_clause_opt:
+           RETURNING Typename json_format_clause_opt
+               {
+                   JsonOutput *n = makeNode(JsonOutput);
+
+                   n->typeName = $2;
+                   n->returning = makeNode(JsonReturning);
+                   n->returning->format = (JsonFormat *) $3;
+                   $$ = (Node *) n;
+               }
+           | /* EMPTY */                           { $$ = NULL; }
+       ;
+
+/* KEYS is a noise word here */
+json_key_uniqueness_constraint_opt:
+           WITH UNIQUE KEYS                { $$ = true; }
+           | WITH UNIQUE                   { $$ = true; }
+           | WITHOUT_LA UNIQUE KEYS        { $$ = false; }
+           | WITHOUT_LA UNIQUE             { $$ = false; }
+           | /* EMPTY */                   { $$ = false; }
+       ;
+
+json_name_and_value_list:
+           json_name_and_value
+               { $$ = list_make1($1); }
+           | json_name_and_value_list ',' json_name_and_value
+               { $$ = lappend($1, $3); }
+       ;
+
+json_name_and_value:
+/* Supporting this syntax seems to require major surgery
+           KEY c_expr VALUE_P json_value_expr
+               { $$ = makeJsonKeyValue($2, $4); }
+           |
+*/
+           c_expr VALUE_P json_value_expr
+               { $$ = makeJsonKeyValue($1, $3); }
+           |
+           a_expr ':' json_value_expr
+               { $$ = makeJsonKeyValue($1, $3); }
+       ;
+
+/* empty means false for objects, true for arrays */
+json_object_constructor_null_clause_opt:
+           NULL_P ON NULL_P                    { $$ = false; }
+           | ABSENT ON NULL_P                  { $$ = true; }
+           | /* EMPTY */                       { $$ = false; }
+       ;
+
+json_array_constructor_null_clause_opt:
+           NULL_P ON NULL_P                        { $$ = false; }
+           | ABSENT ON NULL_P                      { $$ = true; }
+           | /* EMPTY */                           { $$ = true; }
+       ;
+
+json_value_expr_list:
+           json_value_expr                             { $$ = list_make1($1); }
+           | json_value_expr_list ',' json_value_expr  { $$ = lappend($1, $3);}
+       ;
+
+json_aggregate_func:
+           JSON_OBJECTAGG '('
+               json_name_and_value
+               json_object_constructor_null_clause_opt
+               json_key_uniqueness_constraint_opt
+               json_output_clause_opt
+           ')'
+               {
+                   JsonObjectAgg *n = makeNode(JsonObjectAgg);
+
+                   n->arg = (JsonKeyValue *) $3;
+                   n->absent_on_null = $4;
+                   n->unique = $5;
+                   n->constructor = makeNode(JsonAggConstructor);
+                   n->constructor->output = (JsonOutput *) $6;
+                   n->constructor->agg_order = NULL;
+                   n->constructor->location = @1;
+                   $$ = (Node *) n;
+               }
+           | JSON_ARRAYAGG '('
+               json_value_expr
+               json_array_aggregate_order_by_clause_opt
+               json_array_constructor_null_clause_opt
+               json_output_clause_opt
+           ')'
+               {
+                   JsonArrayAgg *n = makeNode(JsonArrayAgg);
+
+                   n->arg = (JsonValueExpr *) $3;
+                   n->absent_on_null = $5;
+                   n->constructor = makeNode(JsonAggConstructor);
+                   n->constructor->agg_order = $4;
+                   n->constructor->output = (JsonOutput *) $6;
+                   n->constructor->location = @1;
+                   $$ = (Node *) n;
+               }
+       ;
+
+json_array_aggregate_order_by_clause_opt:
+           ORDER BY sortby_list                    { $$ = $3; }
+           | /* EMPTY */                           { $$ = NIL; }
+       ;
 
 /*****************************************************************************
  *
  */
 unreserved_keyword:
              ABORT_P
+           | ABSENT
            | ABSOLUTE_P
            | ACCESS
            | ACTION
            | FIRST_P
            | FOLLOWING
            | FORCE
+           | FORMAT
            | FORWARD
            | FUNCTION
            | FUNCTIONS
            | INSTEAD
            | INVOKER
            | ISOLATION
+           | JSON
            | KEY
+           | KEYS
            | LABEL
            | LANGUAGE
            | LARGE_P
            | INT_P
            | INTEGER
            | INTERVAL
+           | JSON_ARRAY
+           | JSON_ARRAYAGG
+           | JSON_OBJECT
+           | JSON_OBJECTAGG
            | LEAST
            | NATIONAL
            | NCHAR
  */
 bare_label_keyword:
              ABORT_P
+           | ABSENT
            | ABSOLUTE_P
            | ACCESS
            | ACTION
            | FOLLOWING
            | FORCE
            | FOREIGN
+           | FORMAT
            | FORWARD
            | FREEZE
            | FULL
            | IS
            | ISOLATION
            | JOIN
+           | JSON
+           | JSON_ARRAY
+           | JSON_ARRAYAGG
+           | JSON_OBJECT
+           | JSON_OBJECTAGG
            | KEY
+           | KEYS
            | LABEL
            | LANGUAGE
            | LARGE_P
 
 
 #include "postgres.h"
 
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
 #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"
 static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
+static Node *transformJsonObjectConstructor(ParseState *pstate,
+                                           JsonObjectConstructor *ctor);
+static Node *transformJsonArrayConstructor(ParseState *pstate,
+                                          JsonArrayConstructor *ctor);
+static Node *transformJsonArrayQueryConstructor(ParseState *pstate,
+                                               JsonArrayQueryConstructor *ctor);
+static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
+static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
 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,
                break;
            }
 
+       case T_JsonObjectConstructor:
+           result = transformJsonObjectConstructor(pstate, (JsonObjectConstructor *) expr);
+           break;
+
+       case T_JsonArrayConstructor:
+           result = transformJsonArrayConstructor(pstate, (JsonArrayConstructor *) expr);
+           break;
+
+       case T_JsonArrayQueryConstructor:
+           result = transformJsonArrayQueryConstructor(pstate, (JsonArrayQueryConstructor *) expr);
+           break;
+
+       case T_JsonObjectAgg:
+           result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
+           break;
+
+       case T_JsonArrayAgg:
+           result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
+           break;
+
        default:
            /* should not reach here */
            elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
    }
    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;
+
+   /*
+    * Using JSON_VALUE here is slightly bogus: perhaps we need to be passed a
+    * JsonConstructorType so that we can use one of JSON_OBJECTAGG, etc.
+    */
+   if (exprType(expr) == UNKNOWNOID)
+       expr = coerce_to_specific_type(pstate, expr, TEXTOID, "JSON_VALUE");
+
+   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;
+}
+
+/*
+ * Checks specified output format for its applicability to the target type.
+ */
+static void
+checkJsonOutputFormat(ParseState *pstate, const JsonFormat *format,
+                     Oid targettype, bool allow_format_for_non_strings)
+{
+   if (!allow_format_for_non_strings &&
+       format->format_type != JS_FORMAT_DEFAULT &&
+       (targettype != BYTEAOID &&
+        targettype != JSONOID &&
+        targettype != JSONBOID))
+   {
+       char        typcategory;
+       bool        typispreferred;
+
+       get_type_category_preferred(targettype, &typcategory, &typispreferred);
+
+       if (typcategory != TYPCATEGORY_STRING)
+           ereport(ERROR,
+                   errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                   parser_errposition(pstate, format->location),
+                   errmsg("cannot use JSON format with non-string output types"));
+   }
+
+   if (format->format_type == JS_FORMAT_JSON)
+   {
+       JsonEncoding enc = format->encoding != JS_ENC_DEFAULT ?
+       format->encoding : JS_ENC_UTF8;
+
+       if (targettype != BYTEAOID &&
+           format->encoding != JS_ENC_DEFAULT)
+           ereport(ERROR,
+                   errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                   parser_errposition(pstate, format->location),
+                   errmsg("cannot set JSON encoding for non-bytea output types"));
+
+       if (enc != JS_ENC_UTF8)
+           ereport(ERROR,
+                   errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                   errmsg("unsupported JSON encoding"),
+                   errhint("Only UTF8 JSON encoding is supported."),
+                   parser_errposition(pstate, format->location));
+   }
+}
+
+/*
+ * Transform JSON output clause.
+ *
+ * Assigns target type oid and modifier.
+ * Assigns default format or checks specified format for its applicability to
+ * the target type.
+ */
+static JsonReturning *
+transformJsonOutput(ParseState *pstate, const JsonOutput *output,
+                   bool allow_format)
+{
+   JsonReturning *ret;
+
+   /* if output clause is not specified, make default clause value */
+   if (!output)
+   {
+       ret = makeNode(JsonReturning);
+
+       ret->format = makeJsonFormat(JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1);
+       ret->typid = InvalidOid;
+       ret->typmod = -1;
+
+       return ret;
+   }
+
+   ret = copyObject(output->returning);
+
+   typenameTypeIdAndMod(pstate, output->typeName, &ret->typid, &ret->typmod);
+
+   if (output->typeName->setof)
+       ereport(ERROR,
+               errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+               errmsg("returning SETOF 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 =
+           ret->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
+   else
+       checkJsonOutputFormat(pstate, ret->format, ret->typid, allow_format);
+
+   return ret;
+}
+
+/*
+ * Transform JSON output clause of JSON constructor functions.
+ *
+ * Derive RETURNING type, if not specified, from argument types.
+ */
+static JsonReturning *
+transformJsonConstructorOutput(ParseState *pstate, JsonOutput *output,
+                              List *args)
+{
+   JsonReturning *returning = transformJsonOutput(pstate, output, true);
+
+   if (!OidIsValid(returning->typid))
+   {
+       ListCell   *lc;
+       bool        have_jsonb = false;
+
+       foreach(lc, args)
+       {
+           Node       *expr = lfirst(lc);
+           Oid         typid = exprType(expr);
+
+           have_jsonb |= typid == JSONBOID;
+
+           if (have_jsonb)
+               break;
+       }
+
+       if (have_jsonb)
+       {
+           returning->typid = JSONBOID;
+           returning->format->format_type = JS_FORMAT_JSONB;
+       }
+       else
+       {
+           /* XXX TEXT is default by the standard, but we return JSON */
+           returning->typid = JSONOID;
+           returning->format->format_type = JS_FORMAT_JSON;
+       }
+
+       returning->typmod = -1;
+   }
+
+   return returning;
+}
+
+/*
+ * Coerce json[b]-valued function expression to the output type.
+ */
+static Node *
+coerceJsonFuncExpr(ParseState *pstate, Node *expr,
+                  const JsonReturning *returning, bool report_error)
+{
+   Node       *res;
+   int         location;
+   Oid         exprtype = exprType(expr);
+
+   /* if output type is not specified or equals to function type, return */
+   if (!OidIsValid(returning->typid) || returning->typid == exprtype)
+       return expr;
+
+   location = exprLocation(expr);
+
+   if (location < 0)
+       location = returning->format->location;
+
+   /* special case for RETURNING bytea FORMAT json */
+   if (returning->format->format_type == JS_FORMAT_JSON &&
+       returning->typid == BYTEAOID)
+   {
+       /* encode json text into bytea using pg_convert_to() */
+       Node       *texpr = coerce_to_specific_type(pstate, expr, TEXTOID,
+                                                   "JSON_FUNCTION");
+       Const      *enc = getJsonEncodingConst(returning->format);
+       FuncExpr   *fexpr = makeFuncExpr(F_CONVERT_TO, BYTEAOID,
+                                        list_make2(texpr, enc),
+                                        InvalidOid, InvalidOid,
+                                        COERCE_EXPLICIT_CALL);
+
+       fexpr->location = location;
+
+       return (Node *) fexpr;
+   }
+
+   /* 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);
+
+   if (!res && report_error)
+       ereport(ERROR,
+               errcode(ERRCODE_CANNOT_COERCE),
+               errmsg("cannot cast type %s to %s",
+                      format_type_be(exprtype),
+                      format_type_be(returning->typid)),
+               parser_coercion_errposition(pstate, location, expr));
+
+   return res;
+}
+
+static Node *
+makeJsonConstructorExpr(ParseState *pstate, JsonConstructorType type,
+                       List *args, Expr *fexpr, JsonReturning *returning,
+                       bool unique, bool absent_on_null, int location)
+{
+   JsonConstructorExpr *jsctor = makeNode(JsonConstructorExpr);
+   Node       *placeholder;
+   Node       *coercion;
+   Oid         intermediate_typid =
+   returning->format->format_type == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
+
+   jsctor->args = args;
+   jsctor->func = fexpr;
+   jsctor->type = type;
+   jsctor->returning = returning;
+   jsctor->unique = unique;
+   jsctor->absent_on_null = absent_on_null;
+   jsctor->location = location;
+
+   if (fexpr)
+       placeholder = makeCaseTestExpr((Node *) fexpr);
+   else
+   {
+       CaseTestExpr *cte = makeNode(CaseTestExpr);
+
+       cte->typeId = intermediate_typid;
+       cte->typeMod = -1;
+       cte->collation = InvalidOid;
+
+       placeholder = (Node *) cte;
+   }
+
+   coercion = coerceJsonFuncExpr(pstate, placeholder, returning, true);
+
+   if (coercion != placeholder)
+       jsctor->coercion = (Expr *) coercion;
+
+   return (Node *) jsctor;
+}
+
+/*
+ * Transform JSON_OBJECT() constructor.
+ *
+ * JSON_OBJECT() is transformed into json[b]_build_object[_ext]() call
+ * depending on the output JSON format. The first two arguments of
+ * json[b]_build_object_ext() are absent_on_null and check_key_uniqueness.
+ *
+ * Then function call result is coerced to the target type.
+ */
+static Node *
+transformJsonObjectConstructor(ParseState *pstate, JsonObjectConstructor *ctor)
+{
+   JsonReturning *returning;
+   List       *args = NIL;
+
+   /* transform key-value pairs, if any */
+   if (ctor->exprs)
+   {
+       ListCell   *lc;
+
+       /* transform and append key-value arguments */
+       foreach(lc, ctor->exprs)
+       {
+           JsonKeyValue *kv = castNode(JsonKeyValue, lfirst(lc));
+           Node       *key = transformExprRecurse(pstate, (Node *) kv->key);
+           Node       *val = transformJsonValueExpr(pstate, kv->value,
+                                                    JS_FORMAT_DEFAULT);
+
+           args = lappend(args, key);
+           args = lappend(args, val);
+       }
+   }
+
+   returning = transformJsonConstructorOutput(pstate, ctor->output, args);
+
+   return makeJsonConstructorExpr(pstate, JSCTOR_JSON_OBJECT, args, NULL,
+                                  returning, ctor->unique,
+                                  ctor->absent_on_null, ctor->location);
+}
+
+/*
+ * Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
+ *  (SELECT  JSON_ARRAYAGG(a  [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
+ */
+static Node *
+transformJsonArrayQueryConstructor(ParseState *pstate,
+                                  JsonArrayQueryConstructor *ctor)
+{
+   SubLink    *sublink = makeNode(SubLink);
+   SelectStmt *select = makeNode(SelectStmt);
+   RangeSubselect *range = makeNode(RangeSubselect);
+   Alias      *alias = makeNode(Alias);
+   ResTarget  *target = makeNode(ResTarget);
+   JsonArrayAgg *agg = makeNode(JsonArrayAgg);
+   ColumnRef  *colref = makeNode(ColumnRef);
+   Query      *query;
+   ParseState *qpstate;
+
+   /* Transform query only for counting target list entries. */
+   qpstate = make_parsestate(pstate);
+
+   query = transformStmt(qpstate, ctor->query);
+
+   if (count_nonjunk_tlist_entries(query->targetList) != 1)
+       ereport(ERROR,
+               errcode(ERRCODE_SYNTAX_ERROR),
+               errmsg("subquery must return only one column"),
+               parser_errposition(pstate, ctor->location));
+
+   free_parsestate(qpstate);
+
+   colref->fields = list_make2(makeString(pstrdup("q")),
+                               makeString(pstrdup("a")));
+   colref->location = ctor->location;
+
+   agg->arg = makeJsonValueExpr((Expr *) colref, ctor->format);
+   agg->absent_on_null = ctor->absent_on_null;
+   agg->constructor = makeNode(JsonAggConstructor);
+   agg->constructor->agg_order = NIL;
+   agg->constructor->output = ctor->output;
+   agg->constructor->location = ctor->location;
+
+   target->name = NULL;
+   target->indirection = NIL;
+   target->val = (Node *) agg;
+   target->location = ctor->location;
+
+   alias->aliasname = pstrdup("q");
+   alias->colnames = list_make1(makeString(pstrdup("a")));
+
+   range->lateral = false;
+   range->subquery = ctor->query;
+   range->alias = alias;
+
+   select->targetList = list_make1(target);
+   select->fromClause = list_make1(range);
+
+   sublink->subLinkType = EXPR_SUBLINK;
+   sublink->subLinkId = 0;
+   sublink->testexpr = NULL;
+   sublink->operName = NIL;
+   sublink->subselect = (Node *) select;
+   sublink->location = ctor->location;
+
+   return transformExprRecurse(pstate, (Node *) sublink);
+}
+
+/*
+ * Common code for JSON_OBJECTAGG and JSON_ARRAYAGG transformation.
+ */
+static Node *
+transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
+                           JsonReturning *returning, List *args,
+                           const char *aggfn, Oid aggtype,
+                           JsonConstructorType ctor_type,
+                           bool unique, bool absent_on_null)
+{
+   Oid         aggfnoid;
+   Node       *node;
+   Expr       *aggfilter = agg_ctor->agg_filter ? (Expr *)
+   transformWhereClause(pstate, agg_ctor->agg_filter,
+                        EXPR_KIND_FILTER, "FILTER") : NULL;
+
+   aggfnoid = DatumGetInt32(DirectFunctionCall1(regprocin,
+                                                CStringGetDatum(aggfn)));
+
+   if (agg_ctor->over)
+   {
+       /* window function */
+       WindowFunc *wfunc = makeNode(WindowFunc);
+
+       wfunc->winfnoid = aggfnoid;
+       wfunc->wintype = aggtype;
+       /* wincollid and inputcollid will be set by parse_collate.c */
+       wfunc->args = args;
+       /* winref will be set by transformWindowFuncCall */
+       wfunc->winstar = false;
+       wfunc->winagg = true;
+       wfunc->aggfilter = aggfilter;
+       wfunc->location = agg_ctor->location;
+
+       /*
+        * ordered aggs not allowed in windows yet
+        */
+       if (agg_ctor->agg_order != NIL)
+           ereport(ERROR,
+                   errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                   errmsg("aggregate ORDER BY is not implemented for window functions"),
+                   parser_errposition(pstate, agg_ctor->location));
+
+       /* parse_agg.c does additional window-func-specific processing */
+       transformWindowFuncCall(pstate, wfunc, agg_ctor->over);
+
+       node = (Node *) wfunc;
+   }
+   else
+   {
+       Aggref     *aggref = makeNode(Aggref);
+
+       aggref->aggfnoid = aggfnoid;
+       aggref->aggtype = aggtype;
+
+       /* aggcollid and inputcollid will be set by parse_collate.c */
+       aggref->aggtranstype = InvalidOid;  /* will be set by planner */
+       /* aggargtypes will be set by transformAggregateCall */
+       /* aggdirectargs and args will be set by transformAggregateCall */
+       /* aggorder and aggdistinct will be set by transformAggregateCall */
+       aggref->aggfilter = aggfilter;
+       aggref->aggstar = false;
+       aggref->aggvariadic = false;
+       aggref->aggkind = AGGKIND_NORMAL;
+       /* agglevelsup will be set by transformAggregateCall */
+       aggref->aggsplit = AGGSPLIT_SIMPLE; /* planner might change this */
+       aggref->location = agg_ctor->location;
+
+       transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
+
+       node = (Node *) aggref;
+   }
+
+   return makeJsonConstructorExpr(pstate, ctor_type, NIL, (Expr *) node,
+                                  returning, unique, absent_on_null,
+                                  agg_ctor->location);
+}
+
+/*
+ * Transform JSON_OBJECTAGG() aggregate function.
+ *
+ * JSON_OBJECTAGG() is transformed into
+ * json[b]_objectagg(key, value, absent_on_null, check_unique) call depending on
+ * the output JSON format.  Then the function call result is coerced to the
+ * target output type.
+ */
+static Node *
+transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg)
+{
+   JsonReturning *returning;
+   Node       *key;
+   Node       *val;
+   List       *args;
+   const char *aggfnname;
+   Oid         aggtype;
+
+   key = transformExprRecurse(pstate, (Node *) agg->arg->key);
+   val = transformJsonValueExpr(pstate, agg->arg->value, JS_FORMAT_DEFAULT);
+   args = list_make2(key, val);
+
+   returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
+                                              args);
+
+   if (returning->format->format_type == JS_FORMAT_JSONB)
+   {
+       if (agg->absent_on_null)
+           if (agg->unique)
+               aggfnname = "pg_catalog.jsonb_object_agg_unique_strict";
+           else
+               aggfnname = "pg_catalog.jsonb_object_agg_strict";
+       else if (agg->unique)
+           aggfnname = "pg_catalog.jsonb_object_agg_unique";
+       else
+           aggfnname = "pg_catalog.jsonb_object_agg";
+
+       aggtype = JSONBOID;
+   }
+   else
+   {
+       if (agg->absent_on_null)
+           if (agg->unique)
+               aggfnname = "pg_catalog.json_object_agg_unique_strict";
+           else
+               aggfnname = "pg_catalog.json_object_agg_strict";
+       else if (agg->unique)
+           aggfnname = "pg_catalog.json_object_agg_unique";
+       else
+           aggfnname = "pg_catalog.json_object_agg";
+
+       aggtype = JSONOID;
+   }
+
+   return transformJsonAggConstructor(pstate, agg->constructor, returning,
+                                      args, aggfnname, aggtype,
+                                      JSCTOR_JSON_OBJECTAGG,
+                                      agg->unique, agg->absent_on_null);
+}
+
+/*
+ * Transform JSON_ARRAYAGG() aggregate function.
+ *
+ * JSON_ARRAYAGG() is transformed into json[b]_agg[_strict]() call depending
+ * on the output JSON format and absent_on_null.  Then the function call result
+ * is coerced to the target output type.
+ */
+static Node *
+transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg)
+{
+   JsonReturning *returning;
+   Node       *arg;
+   const char *aggfnname;
+   Oid         aggtype;
+
+   arg = transformJsonValueExpr(pstate, agg->arg, JS_FORMAT_DEFAULT);
+
+   returning = transformJsonConstructorOutput(pstate, agg->constructor->output,
+                                              list_make1(arg));
+
+   if (returning->format->format_type == JS_FORMAT_JSONB)
+   {
+       aggfnname = agg->absent_on_null ?
+           "pg_catalog.jsonb_agg_strict" : "pg_catalog.jsonb_agg";
+       aggtype = JSONBOID;
+   }
+   else
+   {
+       aggfnname = agg->absent_on_null ?
+           "pg_catalog.json_agg_strict" : "pg_catalog.json_agg";
+       aggtype = JSONOID;
+   }
+
+   return transformJsonAggConstructor(pstate, agg->constructor, returning,
+                                      list_make1(arg), aggfnname, aggtype,
+                                      JSCTOR_JSON_ARRAYAGG,
+                                      false, agg->absent_on_null);
+}
+
+/*
+ * Transform JSON_ARRAY() constructor.
+ *
+ * JSON_ARRAY() is transformed into json[b]_build_array[_ext]() call
+ * depending on the output JSON format. The first argument of
+ * json[b]_build_array_ext() is absent_on_null.
+ *
+ * Then function call result is coerced to the target type.
+ */
+static Node *
+transformJsonArrayConstructor(ParseState *pstate, JsonArrayConstructor *ctor)
+{
+   JsonReturning *returning;
+   List       *args = NIL;
+
+   /* transform element expressions, if any */
+   if (ctor->exprs)
+   {
+       ListCell   *lc;
+
+       /* transform and append element arguments */
+       foreach(lc, ctor->exprs)
+       {
+           JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
+           Node       *val = transformJsonValueExpr(pstate, jsval,
+                                                    JS_FORMAT_DEFAULT);
+
+           args = lappend(args, val);
+       }
+   }
+
+   returning = transformJsonConstructorOutput(pstate, ctor->output, args);
+
+   return makeJsonConstructorExpr(pstate, JSCTOR_JSON_ARRAY, args, NULL,
+                                  returning, false, ctor->absent_on_null,
+                                  ctor->location);
+}
 
            }
            break;
        case T_XmlSerialize:
+           /* make XMLSERIALIZE act like a regular function */
            *name = "xmlserialize";
            return 2;
+       case T_JsonObjectConstructor:
+           /* make JSON_OBJECT act like a regular function */
+           *name = "json_object";
+           return 2;
+       case T_JsonArrayConstructor:
+       case T_JsonArrayQueryConstructor:
+           /* make JSON_ARRAY act like a regular function */
+           *name = "json_array";
+           return 2;
+       case T_JsonObjectAgg:
+           /* make JSON_OBJECTAGG act like a regular function */
+           *name = "json_objectagg";
+           return 2;
+       case T_JsonArrayAgg:
+           /* make JSON_ARRAYAGG act like a regular function */
+           *name = "json_arrayagg";
+           return 2;
        default:
            break;
    }
 
     */
    switch (cur_token)
    {
+       case FORMAT:
+           cur_token_length = 6;
+           break;
        case NOT:
            cur_token_length = 3;
            break;
        case USCONST:
            cur_token_length = strlen(yyextra->core_yy_extra.scanbuf + *llocp);
            break;
+       case WITHOUT:
+           cur_token_length = 7;
+           break;
        default:
            return cur_token;
    }
    /* Replace cur_token if needed, based on lookahead */
    switch (cur_token)
    {
+       case FORMAT:
+           /* Replace FORMAT by FORMAT_LA if it's followed by JSON */
+           switch (next_token)
+           {
+               case JSON:
+                   cur_token = FORMAT_LA;
+                   break;
+           }
+           break;
+
        case NOT:
            /* Replace NOT by NOT_LA if it's followed by BETWEEN, IN, etc */
            switch (next_token)
            }
            break;
 
+       case WITHOUT:
+           /* Replace WITHOUT by WITHOUT_LA if it's followed by UNIQUE */
+           switch (next_token)
+           {
+               case UNIQUE:
+                   cur_token = WITHOUT_LA;
+                   break;
+           }
+           break;
+
        case UIDENT:
        case USCONST:
            /* Look ahead for UESCAPE */
 
  */
 #include "postgres.h"
 
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
+#include "common/hashfn.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
    JSONTYPE_OTHER              /* all else */
 } JsonTypeCategory;
 
+
+/*
+ * Support for fast key uniqueness checking.
+ *
+ * We maintain a hash table of used keys in JSON objects for fast detection
+ * of duplicates.
+ */
+/* Common context for key uniqueness check */
+typedef struct HTAB *JsonUniqueCheckState; /* hash table for key names */
+
+/* Hash entry for JsonUniqueCheckState */
+typedef struct JsonUniqueHashEntry
+{
+   const char *key;
+   int         key_len;
+   int         object_id;
+} JsonUniqueHashEntry;
+
+/* Context struct for key uniqueness check during JSON building */
+typedef struct JsonUniqueBuilderState
+{
+   JsonUniqueCheckState check; /* unique check */
+   StringInfoData skipped_keys;    /* skipped keys with NULL values */
+   MemoryContext mcxt;         /* context for saving skipped keys */
+} JsonUniqueBuilderState;
+
+
+/* State struct for JSON aggregation */
 typedef struct JsonAggState
 {
    StringInfo  str;
    Oid         key_output_func;
    JsonTypeCategory val_category;
    Oid         val_output_func;
+   JsonUniqueBuilderState unique_check;
 } JsonAggState;
 
 static void composite_to_json(Datum composite, StringInfo result,
    PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
 }
 
+/*
+ * Is the given type immutable when coming out of a JSON context?
+ *
+ * At present, datetimes are all considered mutable, because they
+ * depend on timezone.  XXX we should also drill down into objects
+ * and arrays, but do not.
+ */
+bool
+to_json_is_immutable(Oid typoid)
+{
+   JsonTypeCategory tcategory;
+   Oid         outfuncoid;
+
+   json_categorize_type(typoid, &tcategory, &outfuncoid);
+
+   switch (tcategory)
+   {
+       case JSONTYPE_BOOL:
+       case JSONTYPE_JSON:
+       case JSONTYPE_NULL:
+           return true;
+
+       case JSONTYPE_DATE:
+       case JSONTYPE_TIMESTAMP:
+       case JSONTYPE_TIMESTAMPTZ:
+           return false;
+
+       case JSONTYPE_ARRAY:
+           return false;       /* TODO recurse into elements */
+
+       case JSONTYPE_COMPOSITE:
+           return false;       /* TODO recurse into fields */
+
+       case JSONTYPE_NUMERIC:
+       case JSONTYPE_CAST:
+       case JSONTYPE_OTHER:
+           return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
+   }
+
+   return false;               /* not reached */
+}
+
 /*
  * SQL function to_json(anyvalue)
  */
  *
  * aggregate input column as a json array value.
  */
-Datum
-json_agg_transfn(PG_FUNCTION_ARGS)
+static Datum
+json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
 {
    MemoryContext aggcontext,
                oldcontext;
    else
    {
        state = (JsonAggState *) PG_GETARG_POINTER(0);
-       appendStringInfoString(state->str, ", ");
    }
 
+   if (absent_on_null && PG_ARGISNULL(1))
+       PG_RETURN_POINTER(state);
+
+   if (state->str->len > 1)
+       appendStringInfoString(state->str, ", ");
+
    /* fast path for NULLs */
    if (PG_ARGISNULL(1))
    {
    val = PG_GETARG_DATUM(1);
 
    /* add some whitespace if structured type and not first item */
-   if (!PG_ARGISNULL(0) &&
+   if (!PG_ARGISNULL(0) && state->str->len > 1 &&
        (state->val_category == JSONTYPE_ARRAY ||
         state->val_category == JSONTYPE_COMPOSITE))
    {
    PG_RETURN_POINTER(state);
 }
 
+
+/*
+ * json_agg aggregate function
+ */
+Datum
+json_agg_transfn(PG_FUNCTION_ARGS)
+{
+   return json_agg_transfn_worker(fcinfo, false);
+}
+
+/*
+ * json_agg_strict aggregate function
+ */
+Datum
+json_agg_strict_transfn(PG_FUNCTION_ARGS)
+{
+   return json_agg_transfn_worker(fcinfo, true);
+}
+
 /*
  * json_agg final function
  */
    PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
 }
 
+/* Functions implementing hash table for key uniqueness check */
+static uint32
+json_unique_hash(const void *key, Size keysize)
+{
+   const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
+   uint32      hash = hash_bytes_uint32(entry->object_id);
+
+   hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
+
+   return DatumGetUInt32(hash);
+}
+
+static int
+json_unique_hash_match(const void *key1, const void *key2, Size keysize)
+{
+   const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
+   const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
+
+   if (entry1->object_id != entry2->object_id)
+       return entry1->object_id > entry2->object_id ? 1 : -1;
+
+   if (entry1->key_len != entry2->key_len)
+       return entry1->key_len > entry2->key_len ? 1 : -1;
+
+   return strncmp(entry1->key, entry2->key, entry1->key_len);
+}
+
+/*
+ * Uniqueness detection support.
+ *
+ * In order to detect uniqueness during building or parsing of a JSON
+ * object, we maintain a hash table of key names already seen.
+ */
+static void
+json_unique_check_init(JsonUniqueCheckState *cxt)
+{
+   HASHCTL     ctl;
+
+   memset(&ctl, 0, sizeof(ctl));
+   ctl.keysize = sizeof(JsonUniqueHashEntry);
+   ctl.entrysize = sizeof(JsonUniqueHashEntry);
+   ctl.hcxt = CurrentMemoryContext;
+   ctl.hash = json_unique_hash;
+   ctl.match = json_unique_hash_match;
+
+   *cxt = hash_create("json object hashtable",
+                      32,
+                      &ctl,
+                      HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
+}
+
+static void
+json_unique_builder_init(JsonUniqueBuilderState *cxt)
+{
+   json_unique_check_init(&cxt->check);
+   cxt->mcxt = CurrentMemoryContext;
+   cxt->skipped_keys.data = NULL;
+}
+
+static bool
+json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
+{
+   JsonUniqueHashEntry entry;
+   bool        found;
+
+   entry.key = key;
+   entry.key_len = strlen(key);
+   entry.object_id = object_id;
+
+   (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
+
+   return !found;
+}
+
+/*
+ * On-demand initialization of a throwaway StringInfo.  This is used to
+ * read a key name that we don't need to store in the output object, for
+ * duplicate key detection when the value is NULL.
+ */
+static StringInfo
+json_unique_builder_get_throwawaybuf(JsonUniqueBuilderState *cxt)
+{
+   StringInfo  out = &cxt->skipped_keys;
+
+   if (!out->data)
+   {
+       MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
+
+       initStringInfo(out);
+       MemoryContextSwitchTo(oldcxt);
+   }
+   else
+       /* Just reset the string to empty */
+       out->len = 0;
+
+   return out;
+}
+
 /*
  * json_object_agg transition function.
  *
  * aggregate two input columns as a single json object value.
  */
-Datum
-json_object_agg_transfn(PG_FUNCTION_ARGS)
+static Datum
+json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
+                              bool absent_on_null, bool unique_keys)
 {
    MemoryContext aggcontext,
                oldcontext;
    JsonAggState *state;
+   StringInfo  out;
    Datum       arg;
+   bool        skip;
+   int         key_offset;
 
    if (!AggCheckCallContext(fcinfo, &aggcontext))
    {
        /*
         * Make the StringInfo in a context where it will persist for the
         * duration of the aggregate call. Switching context is only needed
-        * for this initial step, as the StringInfo routines make sure they
-        * use the right context to enlarge the object if necessary.
+        * for this initial step, as the StringInfo and dynahash routines make
+        * sure they use the right context to enlarge the object if necessary.
         */
        oldcontext = MemoryContextSwitchTo(aggcontext);
        state = (JsonAggState *) palloc(sizeof(JsonAggState));
        state->str = makeStringInfo();
+       if (unique_keys)
+           json_unique_builder_init(&state->unique_check);
+       else
+           memset(&state->unique_check, 0, sizeof(state->unique_check));
        MemoryContextSwitchTo(oldcontext);
 
        arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
    else
    {
        state = (JsonAggState *) PG_GETARG_POINTER(0);
-       appendStringInfoString(state->str, ", ");
    }
 
    /*
 
    if (PG_ARGISNULL(1))
        ereport(ERROR,
-               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                errmsg("field name must not be null")));
+               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                errmsg("null value not allowed for object key")));
+
+   /* Skip null values if absent_on_null */
+   skip = absent_on_null && PG_ARGISNULL(2);
+
+   if (skip)
+   {
+       /*
+        * We got a NULL value and we're not storing those; if we're not
+        * testing key uniqueness, we're done.  If we are, use the throwaway
+        * buffer to store the key name so that we can check it.
+        */
+       if (!unique_keys)
+           PG_RETURN_POINTER(state);
+
+       out = json_unique_builder_get_throwawaybuf(&state->unique_check);
+   }
+   else
+   {
+       out = state->str;
+
+       /*
+        * Append comma delimiter only if we have already output some fields
+        * after the initial string "{ ".
+        */
+       if (out->len > 2)
+           appendStringInfoString(out, ", ");
+   }
 
    arg = PG_GETARG_DATUM(1);
 
-   datum_to_json(arg, false, state->str, state->key_category,
+   key_offset = out->len;
+
+   datum_to_json(arg, false, out, state->key_category,
                  state->key_output_func, true);
 
+   if (unique_keys)
+   {
+       const char *key = &out->data[key_offset];
+
+       if (!json_unique_check_key(&state->unique_check.check, key, 0))
+           ereport(ERROR,
+                   errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
+                   errmsg("duplicate JSON key %s", key));
+
+       if (skip)
+           PG_RETURN_POINTER(state);
+   }
+
    appendStringInfoString(state->str, " : ");
 
    if (PG_ARGISNULL(2))
    PG_RETURN_POINTER(state);
 }
 
+/*
+ * json_object_agg aggregate function
+ */
+Datum
+json_object_agg_transfn(PG_FUNCTION_ARGS)
+{
+   return json_object_agg_transfn_worker(fcinfo, false, false);
+}
+
+/*
+ * json_object_agg_strict aggregate function
+ */
+Datum
+json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
+{
+   return json_object_agg_transfn_worker(fcinfo, true, false);
+}
+
+/*
+ * json_object_agg_unique aggregate function
+ */
+Datum
+json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
+{
+   return json_object_agg_transfn_worker(fcinfo, false, true);
+}
+
+/*
+ * json_object_agg_unique_strict aggregate function
+ */
+Datum
+json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
+{
+   return json_object_agg_transfn_worker(fcinfo, true, true);
+}
+
 /*
  * json_object_agg final function.
  */
    return result;
 }
 
-/*
- * SQL function json_build_object(variadic "any")
- */
 Datum
-json_build_object(PG_FUNCTION_ARGS)
+json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
+                        bool absent_on_null, bool unique_keys)
 {
-   int         nargs;
    int         i;
    const char *sep = "";
    StringInfo  result;
-   Datum      *args;
-   bool       *nulls;
-   Oid        *types;
-
-   /* fetch argument values to build the object */
-   nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
-
-   if (nargs < 0)
-       PG_RETURN_NULL();
+   JsonUniqueBuilderState unique_check;
 
    if (nargs % 2 != 0)
        ereport(ERROR,
 
    appendStringInfoChar(result, '{');
 
+   if (unique_keys)
+       json_unique_builder_init(&unique_check);
+
    for (i = 0; i < nargs; i += 2)
    {
-       appendStringInfoString(result, sep);
-       sep = ", ";
+       StringInfo  out;
+       bool        skip;
+       int         key_offset;
+
+       /* Skip null values if absent_on_null */
+       skip = absent_on_null && nulls[i + 1];
+
+       if (skip)
+       {
+           /* If key uniqueness check is needed we must save skipped keys */
+           if (!unique_keys)
+               continue;
+
+           out = json_unique_builder_get_throwawaybuf(&unique_check);
+       }
+       else
+       {
+           appendStringInfoString(result, sep);
+           sep = ", ";
+           out = result;
+       }
 
        /* process key */
        if (nulls[i])
            ereport(ERROR,
-                   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                    errmsg("argument %d cannot be null", i + 1),
-                    errhint("Object keys should be text.")));
+                   (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                    errmsg("null value not allowed for object key")));
+
+       /* save key offset before appending it */
+       key_offset = out->len;
 
-       add_json(args[i], false, result, types[i], true);
+       add_json(args[i], false, out, types[i], true);
+
+       if (unique_keys)
+       {
+           /* check key uniqueness after key appending */
+           const char *key = &out->data[key_offset];
+
+           if (!json_unique_check_key(&unique_check.check, key, 0))
+               ereport(ERROR,
+                       errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
+                       errmsg("duplicate JSON key %s", key));
+
+           if (skip)
+               continue;
+       }
 
        appendStringInfoString(result, " : ");
 
 
    appendStringInfoChar(result, '}');
 
-   PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
+   return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
+}
+
+/*
+ * SQL function json_build_object(variadic "any")
+ */
+Datum
+json_build_object(PG_FUNCTION_ARGS)
+{
+   Datum      *args;
+   bool       *nulls;
+   Oid        *types;
+
+   /* build argument values to build the object */
+   int         nargs = extract_variadic_args(fcinfo, 0, true,
+                                             &args, &types, &nulls);
+
+   if (nargs < 0)
+       PG_RETURN_NULL();
+
+   PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
 }
 
 /*
    PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
 }
 
-/*
- * SQL function json_build_array(variadic "any")
- */
 Datum
-json_build_array(PG_FUNCTION_ARGS)
+json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
+                       bool absent_on_null)
 {
-   int         nargs;
    int         i;
    const char *sep = "";
    StringInfo  result;
-   Datum      *args;
-   bool       *nulls;
-   Oid        *types;
-
-   /* fetch argument values to build the array */
-   nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
-
-   if (nargs < 0)
-       PG_RETURN_NULL();
 
    result = makeStringInfo();
 
 
    for (i = 0; i < nargs; i++)
    {
+       if (absent_on_null && nulls[i])
+           continue;
+
        appendStringInfoString(result, sep);
        sep = ", ";
        add_json(args[i], nulls[i], result, types[i], false);
 
    appendStringInfoChar(result, ']');
 
-   PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
+   return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
+}
+
+/*
+ * SQL function json_build_array(variadic "any")
+ */
+Datum
+json_build_array(PG_FUNCTION_ARGS)
+{
+   Datum      *args;
+   bool       *nulls;
+   Oid        *types;
+
+   /* build argument values to build the object */
+   int         nargs = extract_variadic_args(fcinfo, 0, true,
+                                             &args, &types, &nulls);
+
+   if (nargs < 0)
+       PG_RETURN_NULL();
+
+   PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
 }
 
 /*
 
 
 #include "access/htup_details.h"
 #include "access/transam.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
    datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
 }
 
+/*
+ * Is the given type immutable when coming out of a JSONB context?
+ *
+ * At present, datetimes are all considered mutable, because they
+ * depend on timezone.  XXX we should also drill down into objects and
+ * arrays, but do not.
+ */
+bool
+to_jsonb_is_immutable(Oid typoid)
+{
+   JsonbTypeCategory tcategory;
+   Oid         outfuncoid;
+
+   jsonb_categorize_type(typoid, &tcategory, &outfuncoid);
+
+   switch (tcategory)
+   {
+       case JSONBTYPE_NULL:
+       case JSONBTYPE_BOOL:
+       case JSONBTYPE_JSON:
+       case JSONBTYPE_JSONB:
+           return true;
+
+       case JSONBTYPE_DATE:
+       case JSONBTYPE_TIMESTAMP:
+       case JSONBTYPE_TIMESTAMPTZ:
+           return false;
+
+       case JSONBTYPE_ARRAY:
+           return false;       /* TODO recurse into elements */
+
+       case JSONBTYPE_COMPOSITE:
+           return false;       /* TODO recurse into fields */
+
+       case JSONBTYPE_NUMERIC:
+       case JSONBTYPE_JSONCAST:
+       case JSONBTYPE_OTHER:
+           return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
+   }
+
+   return false;               /* not reached */
+}
+
 /*
  * SQL function to_jsonb(anyvalue)
  */
    PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
 }
 
-/*
- * SQL function jsonb_build_object(variadic "any")
- */
 Datum
-jsonb_build_object(PG_FUNCTION_ARGS)
+jsonb_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
+                         bool absent_on_null, bool unique_keys)
 {
-   int         nargs;
    int         i;
    JsonbInState result;
-   Datum      *args;
-   bool       *nulls;
-   Oid        *types;
-
-   /* build argument values to build the object */
-   nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
-
-   if (nargs < 0)
-       PG_RETURN_NULL();
 
    if (nargs % 2 != 0)
        ereport(ERROR,
    memset(&result, 0, sizeof(JsonbInState));
 
    result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+   result.parseState->unique_keys = unique_keys;
+   result.parseState->skip_nulls = absent_on_null;
 
    for (i = 0; i < nargs; i += 2)
    {
        /* process key */
+       bool        skip;
+
        if (nulls[i])
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                     errmsg("argument %d: key must not be null", i + 1)));
 
+       /* skip null values if absent_on_null */
+       skip = absent_on_null && nulls[i + 1];
+
+       /* we need to save skipped keys for the key uniqueness check */
+       if (skip && !unique_keys)
+           continue;
+
        add_jsonb(args[i], false, &result, types[i], true);
 
        /* process value */
 
    result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
 
-   PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+   return JsonbPGetDatum(JsonbValueToJsonb(result.res));
+}
+
+/*
+ * SQL function jsonb_build_object(variadic "any")
+ */
+Datum
+jsonb_build_object(PG_FUNCTION_ARGS)
+{
+   Datum      *args;
+   bool       *nulls;
+   Oid        *types;
+
+   /* build argument values to build the object */
+   int         nargs = extract_variadic_args(fcinfo, 0, true,
+                                             &args, &types, &nulls);
+
+   if (nargs < 0)
+       PG_RETURN_NULL();
+
+   PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false));
 }
 
 /*
    PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
 }
 
-/*
- * SQL function jsonb_build_array(variadic "any")
- */
 Datum
-jsonb_build_array(PG_FUNCTION_ARGS)
+jsonb_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
+                        bool absent_on_null)
 {
-   int         nargs;
    int         i;
    JsonbInState result;
-   Datum      *args;
-   bool       *nulls;
-   Oid        *types;
-
-   /* build argument values to build the array */
-   nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
-
-   if (nargs < 0)
-       PG_RETURN_NULL();
 
    memset(&result, 0, sizeof(JsonbInState));
 
    result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
 
    for (i = 0; i < nargs; i++)
+   {
+       if (absent_on_null && nulls[i])
+           continue;
+
        add_jsonb(args[i], nulls[i], &result, types[i], false);
+   }
 
    result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
 
-   PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+   return JsonbPGetDatum(JsonbValueToJsonb(result.res));
 }
 
+/*
+ * SQL function jsonb_build_array(variadic "any")
+ */
+Datum
+jsonb_build_array(PG_FUNCTION_ARGS)
+{
+   Datum      *args;
+   bool       *nulls;
+   Oid        *types;
+
+   /* build argument values to build the object */
+   int         nargs = extract_variadic_args(fcinfo, 0, true,
+                                             &args, &types, &nulls);
+
+   if (nargs < 0)
+       PG_RETURN_NULL();
+
+   PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false));
+}
+
+
 /*
  * degenerate case of jsonb_build_array where it gets 0 arguments.
  */
    {
        ocursor->contVal = icursor->contVal;
        ocursor->size = icursor->size;
+       ocursor->unique_keys = icursor->unique_keys;
+       ocursor->skip_nulls = icursor->skip_nulls;
        icursor = icursor->next;
        if (icursor == NULL)
            break;
    return result;
 }
 
-
-/*
- * jsonb_agg aggregate function
- */
-Datum
-jsonb_agg_transfn(PG_FUNCTION_ARGS)
+static Datum
+jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
 {
    MemoryContext oldcontext,
                aggcontext;
        result = state->res;
    }
 
+   if (absent_on_null && PG_ARGISNULL(1))
+       PG_RETURN_POINTER(state);
+
    /* turn the argument into jsonb in the normal function context */
 
    val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
    PG_RETURN_POINTER(state);
 }
 
+/*
+ * jsonb_agg aggregate function
+ */
+Datum
+jsonb_agg_transfn(PG_FUNCTION_ARGS)
+{
+   return jsonb_agg_transfn_worker(fcinfo, false);
+}
+
+/*
+ * jsonb_agg_strict aggregate function
+ */
+Datum
+jsonb_agg_strict_transfn(PG_FUNCTION_ARGS)
+{
+   return jsonb_agg_transfn_worker(fcinfo, true);
+}
+
 Datum
 jsonb_agg_finalfn(PG_FUNCTION_ARGS)
 {
    PG_RETURN_POINTER(out);
 }
 
-/*
- * jsonb_object_agg aggregate function
- */
-Datum
-jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
+static Datum
+jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
+                               bool absent_on_null, bool unique_keys)
 {
    MemoryContext oldcontext,
                aggcontext;
               *jbval;
    JsonbValue  v;
    JsonbIteratorToken type;
+   bool        skip;
 
    if (!AggCheckCallContext(fcinfo, &aggcontext))
    {
        state->res = result;
        result->res = pushJsonbValue(&result->parseState,
                                     WJB_BEGIN_OBJECT, NULL);
+       result->parseState->unique_keys = unique_keys;
+       result->parseState->skip_nulls = absent_on_null;
+
        MemoryContextSwitchTo(oldcontext);
 
        arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("field name must not be null")));
 
+   /*
+    * Skip null values if absent_on_null unless key uniqueness check is
+    * needed (because we must save keys in this case).
+    */
+   skip = absent_on_null && PG_ARGISNULL(2);
+
+   if (skip && !unique_keys)
+       PG_RETURN_POINTER(state);
+
    val = PG_GETARG_DATUM(1);
 
    memset(&elem, 0, sizeof(JsonbInState));
                }
                result->res = pushJsonbValue(&result->parseState,
                                             WJB_KEY, &v);
+
+               if (skip)
+               {
+                   v.type = jbvNull;
+                   result->res = pushJsonbValue(&result->parseState,
+                                                WJB_VALUE, &v);
+                   MemoryContextSwitchTo(oldcontext);
+                   PG_RETURN_POINTER(state);
+               }
+
                break;
            case WJB_END_ARRAY:
                break;
    PG_RETURN_POINTER(state);
 }
 
+/*
+ * jsonb_object_agg aggregate function
+ */
+Datum
+jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
+{
+   return jsonb_object_agg_transfn_worker(fcinfo, false, false);
+}
+
+
+/*
+ * jsonb_object_agg_strict aggregate function
+ */
+Datum
+jsonb_object_agg_strict_transfn(PG_FUNCTION_ARGS)
+{
+   return jsonb_object_agg_transfn_worker(fcinfo, true, false);
+}
+
+/*
+ * jsonb_object_agg_unique aggregate function
+ */
+Datum
+jsonb_object_agg_unique_transfn(PG_FUNCTION_ARGS)
+{
+   return jsonb_object_agg_transfn_worker(fcinfo, false, true);
+}
+
+/*
+ * jsonb_object_agg_unique_strict aggregate function
+ */
+Datum
+jsonb_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
+{
+   return jsonb_object_agg_transfn_worker(fcinfo, true, true);
+}
+
 Datum
 jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
 {
 
 static int lengthCompareJsonbString(const char *val1, int len1,
                                     const char *val2, int len2);
 static int lengthCompareJsonbPair(const void *a, const void *b, void *binequal);
-static void uniqueifyJsonbObject(JsonbValue *object);
+static void uniqueifyJsonbObject(JsonbValue *object, bool unique_keys,
+                                bool skip_nulls);
 static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
                                        JsonbIteratorToken seq,
                                        JsonbValue *scalarVal);
            appendElement(*pstate, scalarVal);
            break;
        case WJB_END_OBJECT:
-           uniqueifyJsonbObject(&(*pstate)->contVal);
+           uniqueifyJsonbObject(&(*pstate)->contVal,
+                                (*pstate)->unique_keys,
+                                (*pstate)->skip_nulls);
            /* fall through! */
        case WJB_END_ARRAY:
            /* Steps here common to WJB_END_OBJECT case */
    JsonbParseState *ns = palloc(sizeof(JsonbParseState));
 
    ns->next = *pstate;
+   ns->unique_keys = false;
+   ns->skip_nulls = false;
+
    return ns;
 }
 
  * Sort and unique-ify pairs in JsonbValue object
  */
 static void
-uniqueifyJsonbObject(JsonbValue *object)
+uniqueifyJsonbObject(JsonbValue *object, bool unique_keys, bool skip_nulls)
 {
    bool        hasNonUniq = false;
 
        qsort_arg(object->val.object.pairs, object->val.object.nPairs, sizeof(JsonbPair),
                  lengthCompareJsonbPair, &hasNonUniq);
 
-   if (hasNonUniq)
+   if (hasNonUniq && unique_keys)
+       ereport(ERROR,
+               errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
+               errmsg("duplicate JSON object key"));
+
+   if (hasNonUniq || skip_nulls)
    {
-       JsonbPair  *ptr = object->val.object.pairs + 1,
-                  *res = object->val.object.pairs;
+       JsonbPair  *ptr,
+                  *res;
+
+       while (skip_nulls && object->val.object.nPairs > 0 &&
+              object->val.object.pairs->value.type == jbvNull)
+       {
+           /* If skip_nulls is true, remove leading items with null */
+           object->val.object.pairs++;
+           object->val.object.nPairs--;
+       }
 
-       while (ptr - object->val.object.pairs < object->val.object.nPairs)
+       if (object->val.object.nPairs > 0)
        {
-           /* Avoid copying over duplicate */
-           if (lengthCompareJsonbStringValue(ptr, res) != 0)
+           ptr = object->val.object.pairs + 1;
+           res = object->val.object.pairs;
+
+           while (ptr - object->val.object.pairs < object->val.object.nPairs)
            {
-               res++;
-               if (ptr != res)
-                   memcpy(res, ptr, sizeof(JsonbPair));
+               /* Avoid copying over duplicate or null */
+               if (lengthCompareJsonbStringValue(ptr, res) != 0 &&
+                   (!skip_nulls || ptr->value.type != jbvNull))
+               {
+                   res++;
+                   if (ptr != res)
+                       memcpy(res, ptr, sizeof(JsonbPair));
+               }
+               ptr++;
            }
-           ptr++;
-       }
 
-       object->val.object.nPairs = res + 1 - object->val.object.pairs;
+           object->val.object.nPairs = res + 1 - object->val.object.pairs;
+       }
    }
 }
 
                          bool showimplicit);
 static void get_agg_expr(Aggref *aggref, deparse_context *context,
                         Aggref *original_aggref);
+static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
+                               Aggref *original_aggref, const char *funcname,
+                               const char *options, bool is_json_objectagg);
 static void get_agg_combine_expr(Node *node, deparse_context *context,
                                 void *callback_arg);
 static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
+static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
+                                      const char *funcname, const char *options,
+                                      bool is_json_objectagg);
 static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
 static void get_coercion_expr(Node *arg, deparse_context *context,
                              Oid resulttype, int32 resulttypmod,
 static void get_const_expr(Const *constval, deparse_context *context,
                           int showtype);
 static void get_const_collation(Const *constval, deparse_context *context);
+static void get_json_format(JsonFormat *format, StringInfo buf);
+static void get_json_constructor(JsonConstructorExpr *ctor,
+                                deparse_context *context, bool showimplicit);
+static void get_json_constructor_options(JsonConstructorExpr *ctor,
+                                        StringInfo buf);
+static void get_json_agg_constructor(JsonConstructorExpr *ctor,
+                                    deparse_context *context,
+                                    const char *funcname,
+                                    bool is_json_objectagg);
 static void simple_quote_literal(StringInfo buf, const char *val);
 static void get_sublink_expr(SubLink *sublink, deparse_context *context);
 static void get_tablefunc(TableFunc *tf, deparse_context *context,
        bool        need_paren = (PRETTY_PAREN(context)
                                  || IsA(expr, FuncExpr)
                                  || IsA(expr, Aggref)
-                                 || IsA(expr, WindowFunc));
+                                 || IsA(expr, WindowFunc)
+                                 || IsA(expr, JsonConstructorExpr));
 
        if (need_paren)
            appendStringInfoChar(context->buf, '(');
        case T_GroupingFunc:
        case T_WindowFunc:
        case T_FuncExpr:
+       case T_JsonConstructorExpr:
            /* function-like: name(..) or name[..] */
            return true;
 
                    return false;
            }
 
+       case T_JsonValueExpr:
+           /* maybe simple, check args */
+           return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
+                               node, prettyFlags);
+
        default:
            break;
    }
            }
            break;
 
+       case T_JsonValueExpr:
+           {
+               JsonValueExpr *jve = (JsonValueExpr *) node;
+
+               get_rule_expr((Node *) jve->raw_expr, context, false);
+               get_json_format(jve->format, context->buf);
+           }
+           break;
+
+       case T_JsonConstructorExpr:
+           get_json_constructor((JsonConstructorExpr *) node, context, false);
+           break;
+
        case T_List:
            {
                char       *sep;
 static void
 get_agg_expr(Aggref *aggref, deparse_context *context,
             Aggref *original_aggref)
+{
+   get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
+                       false);
+}
+
+/*
+ * get_agg_expr_helper     - subroutine for get_agg_expr and
+ *                         get_json_agg_constructor
+ */
+static void
+get_agg_expr_helper(Aggref *aggref, deparse_context *context,
+                   Aggref *original_aggref, const char *funcname,
+                   const char *options, bool is_json_objectagg)
 {
    StringInfo  buf = context->buf;
    Oid         argtypes[FUNC_MAX_ARGS];
    int         nargs;
-   bool        use_variadic;
+   bool        use_variadic = false;
 
    /*
     * For a combining aggregate, we look up and deparse the corresponding
    /* Extract the argument types as seen by the parser */
    nargs = get_aggregate_argtypes(aggref, argtypes);
 
+   if (!funcname)
+       funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
+                                         argtypes, aggref->aggvariadic,
+                                         &use_variadic,
+                                         context->special_exprkind);
+
    /* Print the aggregate name, schema-qualified if needed */
-   appendStringInfo(buf, "%s(%s",
-                    generate_function_name(aggref->aggfnoid, nargs,
-                                           NIL, argtypes,
-                                           aggref->aggvariadic,
-                                           &use_variadic,
-                                           context->special_exprkind),
+   appendStringInfo(buf, "%s(%s", funcname,
                     (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
 
    if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
                if (tle->resjunk)
                    continue;
                if (i++ > 0)
-                   appendStringInfoString(buf, ", ");
+               {
+                   if (is_json_objectagg)
+                   {
+                       /*
+                        * the ABSENT ON NULL and WITH UNIQUE args are printed
+                        * separately, so ignore them here
+                        */
+                       if (i > 2)
+                           break;
+
+                       appendStringInfoString(buf, " : ");
+                   }
+                   else
+                       appendStringInfoString(buf, ", ");
+               }
                if (use_variadic && i == nargs)
                    appendStringInfoString(buf, "VARIADIC ");
                get_rule_expr(arg, context, true);
        }
    }
 
+   if (options)
+       appendStringInfoString(buf, options);
+
    if (aggref->aggfilter != NULL)
    {
        appendStringInfoString(buf, ") FILTER (WHERE ");
  */
 static void
 get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
+{
+   get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
+}
+
+
+/*
+ * get_windowfunc_expr_helper  - subroutine for get_windowfunc_expr and
+ *                             get_json_agg_constructor
+ */
+static void
+get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
+                          const char *funcname, const char *options,
+                          bool is_json_objectagg)
 {
    StringInfo  buf = context->buf;
    Oid         argtypes[FUNC_MAX_ARGS];
        nargs++;
    }
 
-   appendStringInfo(buf, "%s(",
-                    generate_function_name(wfunc->winfnoid, nargs,
-                                           argnames, argtypes,
-                                           false, NULL,
-                                           context->special_exprkind));
+   if (!funcname)
+       funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
+                                         argtypes, false, NULL,
+                                         context->special_exprkind);
+
+   appendStringInfo(buf, "%s(", funcname);
+
    /* winstar can be set only in zero-argument aggregates */
    if (wfunc->winstar)
        appendStringInfoChar(buf, '*');
    else
-       get_rule_expr((Node *) wfunc->args, context, true);
+   {
+       if (is_json_objectagg)
+       {
+           get_rule_expr((Node *) linitial(wfunc->args), context, false);
+           appendStringInfoString(buf, " : ");
+           get_rule_expr((Node *) lsecond(wfunc->args), context, false);
+       }
+       else
+           get_rule_expr((Node *) wfunc->args, context, true);
+   }
+
+   if (options)
+       appendStringInfoString(buf, options);
 
    if (wfunc->aggfilter != NULL)
    {
    }
 }
 
+/*
+ * get_json_format         - Parse back a JsonFormat node
+ */
+static void
+get_json_format(JsonFormat *format, StringInfo buf)
+{
+   if (format->format_type == JS_FORMAT_DEFAULT)
+       return;
+
+   appendStringInfoString(buf,
+                          format->format_type == JS_FORMAT_JSONB ?
+                          " FORMAT JSONB" : " FORMAT JSON");
+
+   if (format->encoding != JS_ENC_DEFAULT)
+   {
+       const char *encoding;
+
+       encoding =
+           format->encoding == JS_ENC_UTF16 ? "UTF16" :
+           format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
+
+       appendStringInfo(buf, " ENCODING %s", encoding);
+   }
+}
+
+/*
+ * get_json_returning      - Parse back a JsonReturning structure
+ */
+static void
+get_json_returning(JsonReturning *returning, StringInfo buf,
+                  bool json_format_by_default)
+{
+   if (!OidIsValid(returning->typid))
+       return;
+
+   appendStringInfo(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, buf);
+}
+
+/*
+ * get_json_constructor        - Parse back a JsonConstructorExpr node
+ */
+static void
+get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
+                    bool showimplicit)
+{
+   StringInfo  buf = context->buf;
+   const char *funcname;
+   bool        is_json_object;
+   int         curridx;
+   ListCell   *lc;
+
+   if (ctor->type == JSCTOR_JSON_OBJECTAGG)
+   {
+       get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
+       return;
+   }
+   else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
+   {
+       get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
+       return;
+   }
+
+   switch (ctor->type)
+   {
+       case JSCTOR_JSON_OBJECT:
+           funcname = "JSON_OBJECT";
+           break;
+       case JSCTOR_JSON_ARRAY:
+           funcname = "JSON_ARRAY";
+           break;
+       default:
+           elog(ERROR, "invalid JsonConstructorExprType %d", ctor->type);
+   }
+
+   appendStringInfo(buf, "%s(", funcname);
+
+   is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
+   foreach(lc, ctor->args)
+   {
+       curridx = foreach_current_index(lc);
+       if (curridx > 0)
+       {
+           const char *sep;
+
+           sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
+           appendStringInfoString(buf, sep);
+       }
+
+       get_rule_expr((Node *) lfirst(lc), context, true);
+   }
+
+   get_json_constructor_options(ctor, buf);
+   appendStringInfo(buf, ")");
+}
+
+/*
+ * Append options, if any, to the JSON constructor being deparsed
+ */
+static void
+get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
+{
+   if (ctor->absent_on_null)
+   {
+       if (ctor->type == JSCTOR_JSON_OBJECT ||
+           ctor->type == JSCTOR_JSON_OBJECTAGG)
+           appendStringInfoString(buf, " ABSENT ON NULL");
+   }
+   else
+   {
+       if (ctor->type == JSCTOR_JSON_ARRAY ||
+           ctor->type == JSCTOR_JSON_ARRAYAGG)
+           appendStringInfoString(buf, " NULL ON NULL");
+   }
+
+   if (ctor->unique)
+       appendStringInfoString(buf, " WITH UNIQUE KEYS");
+
+   get_json_returning(ctor->returning, buf, true);
+}
+
+/*
+ * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
+ */
+static void
+get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
+                        const char *funcname, bool is_json_objectagg)
+{
+   StringInfoData options;
+
+   initStringInfo(&options);
+   get_json_constructor_options(ctor, &options);
+
+   if (IsA(ctor->func, Aggref))
+       get_agg_expr_helper((Aggref *) ctor->func, context,
+                           (Aggref *) ctor->func,
+                           funcname, options.data, is_json_objectagg);
+   else if (IsA(ctor->func, WindowFunc))
+       get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
+                                  funcname, options.data,
+                                  is_json_objectagg);
+   else
+       elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
+            nodeTag(ctor->func));
+}
+
 /*
  * simple_quote_literal - Format a string as a SQL literal, append to buf
  */
 
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202303292
+#define CATALOG_VERSION_NO 202303293
 
 #endif
 
 # json
 { aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
   aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'json_agg_strict', aggtransfn => 'json_agg_strict_transfn',
+  aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
 { aggfnoid => 'json_object_agg', aggtransfn => 'json_object_agg_transfn',
   aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'json_object_agg_unique',
+  aggtransfn => 'json_object_agg_unique_transfn',
+  aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'json_object_agg_strict',
+  aggtransfn => 'json_object_agg_strict_transfn',
+  aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'json_object_agg_unique_strict',
+  aggtransfn => 'json_object_agg_unique_strict_transfn',
+  aggfinalfn => 'json_object_agg_finalfn', aggtranstype => 'internal' },
 
 # jsonb
 { aggfnoid => 'jsonb_agg', aggtransfn => 'jsonb_agg_transfn',
   aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'jsonb_agg_strict', aggtransfn => 'jsonb_agg_strict_transfn',
+  aggfinalfn => 'jsonb_agg_finalfn', aggtranstype => 'internal' },
 { aggfnoid => 'jsonb_object_agg', aggtransfn => 'jsonb_object_agg_transfn',
   aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'jsonb_object_agg_unique',
+  aggtransfn => 'jsonb_object_agg_unique_transfn',
+  aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'jsonb_object_agg_strict',
+  aggtransfn => 'jsonb_object_agg_strict_transfn',
+  aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
+{ aggfnoid => 'jsonb_object_agg_unique_strict',
+  aggtransfn => 'jsonb_object_agg_unique_strict_transfn',
+  aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
 
 # ordered-set and hypothetical-set aggregates
 { aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
 
   proname => 'json_agg_transfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'internal', proargtypes => 'internal anyelement',
   prosrc => 'json_agg_transfn' },
+{ oid => '8950', descr => 'json aggregate transition function',
+  proname => 'json_agg_strict_transfn', proisstrict => 'f', provolatile => 's',
+  prorettype => 'internal', proargtypes => 'internal anyelement',
+  prosrc => 'json_agg_strict_transfn' },
 { oid => '3174', descr => 'json aggregate final function',
   proname => 'json_agg_finalfn', proisstrict => 'f', prorettype => 'json',
   proargtypes => 'internal', prosrc => 'json_agg_finalfn' },
   proname => 'json_agg', prokind => 'a', proisstrict => 'f', provolatile => 's',
   prorettype => 'json', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
+{ oid => '8951', descr => 'aggregate input into json',
+  proname => 'json_agg_strict', prokind => 'a', proisstrict => 'f',
+  provolatile => 's', prorettype => 'json', proargtypes => 'anyelement',
+  prosrc => 'aggregate_dummy' },
 { oid => '3180', descr => 'json object aggregate transition function',
   proname => 'json_object_agg_transfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'internal', proargtypes => 'internal any any',
   prosrc => 'json_object_agg_transfn' },
+{ oid => '8952', descr => 'json object aggregate transition function',
+  proname => 'json_object_agg_strict_transfn', proisstrict => 'f',
+  provolatile => 's', prorettype => 'internal',
+  proargtypes => 'internal any any',
+  prosrc => 'json_object_agg_strict_transfn' },
+{ oid => '8953', descr => 'json object aggregate transition function',
+  proname => 'json_object_agg_unique_transfn', proisstrict => 'f',
+  provolatile => 's', prorettype => 'internal',
+  proargtypes => 'internal any any',
+  prosrc => 'json_object_agg_unique_transfn' },
+{ oid => '8954', descr => 'json object aggregate transition function',
+  proname => 'json_object_agg_unique_strict_transfn', proisstrict => 'f',
+  provolatile => 's', prorettype => 'internal',
+  proargtypes => 'internal any any',
+  prosrc => 'json_object_agg_unique_strict_transfn' },
 { oid => '3196', descr => 'json object aggregate final function',
   proname => 'json_object_agg_finalfn', proisstrict => 'f',
   prorettype => 'json', proargtypes => 'internal',
   proname => 'json_object_agg', prokind => 'a', proisstrict => 'f',
   provolatile => 's', prorettype => 'json', proargtypes => 'any any',
   prosrc => 'aggregate_dummy' },
+{ oid => '8955', descr => 'aggregate non-NULL input into a json object',
+  proname => 'json_object_agg_strict', prokind => 'a', proisstrict => 'f',
+  provolatile => 's', prorettype => 'json', proargtypes => 'any any',
+  prosrc => 'aggregate_dummy' },
+{ oid => '8956',
+  descr => 'aggregate input into a json object with unique keys',
+  proname => 'json_object_agg_unique', prokind => 'a', proisstrict => 'f',
+  provolatile => 's', prorettype => 'json', proargtypes => 'any any',
+  prosrc => 'aggregate_dummy' },
+{ oid => '8957',
+  descr => 'aggregate non-NULL input into a json object with unique keys',
+  proname => 'json_object_agg_unique_strict', prokind => 'a',
+  proisstrict => 'f', provolatile => 's', prorettype => 'json',
+  proargtypes => 'any any', prosrc => 'aggregate_dummy' },
 { oid => '3198', descr => 'build a json array from any inputs',
   proname => 'json_build_array', provariadic => 'any', proisstrict => 'f',
   provolatile => 's', prorettype => 'json', proargtypes => 'any',
   proname => 'jsonb_agg_transfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'internal', proargtypes => 'internal anyelement',
   prosrc => 'jsonb_agg_transfn' },
+{ oid => '8958', descr => 'jsonb aggregate transition function',
+  proname => 'jsonb_agg_strict_transfn', proisstrict => 'f', provolatile => 's',
+  prorettype => 'internal', proargtypes => 'internal anyelement',
+  prosrc => 'jsonb_agg_strict_transfn' },
 { oid => '3266', descr => 'jsonb aggregate final function',
   proname => 'jsonb_agg_finalfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'jsonb', proargtypes => 'internal',
   proname => 'jsonb_agg', prokind => 'a', proisstrict => 'f',
   provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
+{ oid => '8959', descr => 'aggregate input into jsonb skipping nulls',
+  proname => 'jsonb_agg_strict', prokind => 'a', proisstrict => 'f',
+  provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
+  prosrc => 'aggregate_dummy' },
 { oid => '3268', descr => 'jsonb object aggregate transition function',
   proname => 'jsonb_object_agg_transfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'internal', proargtypes => 'internal any any',
   prosrc => 'jsonb_object_agg_transfn' },
+{ oid => '8960', descr => 'jsonb object aggregate transition function',
+  proname => 'jsonb_object_agg_strict_transfn', proisstrict => 'f',
+  provolatile => 's', prorettype => 'internal',
+  proargtypes => 'internal any any',
+  prosrc => 'jsonb_object_agg_strict_transfn' },
+{ oid => '8961', descr => 'jsonb object aggregate transition function',
+  proname => 'jsonb_object_agg_unique_transfn', proisstrict => 'f',
+  provolatile => 's', prorettype => 'internal',
+  proargtypes => 'internal any any',
+  prosrc => 'jsonb_object_agg_unique_transfn' },
+{ oid => '8962', descr => 'jsonb object aggregate transition function',
+  proname => 'jsonb_object_agg_unique_strict_transfn', proisstrict => 'f',
+  provolatile => 's', prorettype => 'internal',
+  proargtypes => 'internal any any',
+  prosrc => 'jsonb_object_agg_unique_strict_transfn' },
 { oid => '3269', descr => 'jsonb object aggregate final function',
   proname => 'jsonb_object_agg_finalfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'jsonb', proargtypes => 'internal',
   proname => 'jsonb_object_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'jsonb', proargtypes => 'any any',
   prosrc => 'aggregate_dummy' },
+{ oid => '8963', descr => 'aggregate non-NULL inputs into jsonb object',
+  proname => 'jsonb_object_agg_strict', prokind => 'a', proisstrict => 'f',
+  prorettype => 'jsonb', proargtypes => 'any any',
+  prosrc => 'aggregate_dummy' },
+{ oid => '8964',
+  descr => 'aggregate inputs into jsonb object checking key uniqueness',
+  proname => 'jsonb_object_agg_unique', prokind => 'a', proisstrict => 'f',
+  prorettype => 'jsonb', proargtypes => 'any any',
+  prosrc => 'aggregate_dummy' },
+{ oid => '8965',
+  descr => 'aggregate non-NULL inputs into jsonb object checking key uniqueness',
+  proname => 'jsonb_object_agg_unique_strict', prokind => 'a',
+  proisstrict => 'f', prorettype => 'jsonb', proargtypes => 'any any',
+  prosrc => 'aggregate_dummy' },
 { oid => '3271', descr => 'build a jsonb array from any inputs',
   proname => 'jsonb_build_array', provariadic => 'any', proisstrict => 'f',
   provolatile => 's', prorettype => 'jsonb', proargtypes => 'any',
 
 struct ExprEvalStep;
 struct SubscriptingRefState;
 struct ScalarArrayOpExprHashTable;
+struct JsonConstructorExprState;
 
 /* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
 /* expression's interpreter has been initialized */
    EEOP_SCALARARRAYOP,
    EEOP_HASHED_SCALARARRAYOP,
    EEOP_XMLEXPR,
+   EEOP_JSON_CONSTRUCTOR,
    EEOP_AGGREF,
    EEOP_GROUPING_FUNC,
    EEOP_WINDOW_FUNC,
            bool       *argnull;
        }           xmlexpr;
 
+       /* for EEOP_JSON_CONSTRUCTOR */
+       struct
+       {
+           struct JsonConstructorExprState *jcstate;
+       }           json_constructor;
+
        /* for EEOP_AGGREF */
        struct
        {
            int         transno;
            int         setoff;
        }           agg_trans;
+
    }           d;
 } ExprEvalStep;
 
    ExecEvalSubroutine sbs_fetch_old;   /* fetch old value for assignment */
 } SubscriptExecSteps;
 
+/* EEOP_JSON_CONSTRUCTOR state, too big to inline */
+typedef struct JsonConstructorExprState
+{
+   JsonConstructorExpr *constructor;
+   Datum      *arg_values;
+   bool       *arg_nulls;
+   Oid        *arg_types;
+   struct
+   {
+       int         category;
+       Oid         outfuncid;
+   }          *arg_type_cache; /* cache for datum_to_json[b]() */
+   int         nargs;
+} JsonConstructorExprState;
+
 
 /* functions in execExpr.c */
 extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
                                ExprContext *econtext);
 extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op,
                           ExprContext *econtext, TupleTableSlot *slot);
+extern void ExecEvalJsonConstructor(ExprState *state, ExprEvalStep *op,
+                                   ExprContext *econtext);
 
 extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup,
                             ExprContext *aggcontext);
 
 
 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 Node *makeJsonKeyValue(Node *key, Node *value);
+extern JsonEncoding makeJsonEncoding(char *name);
+
 #endif                         /* MAKEFUNC_H */
 
    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;
+
+/*
+ * JsonKeyValue -
+ *     untransformed representation of JSON object key-value pair for
+ *     JSON_OBJECT() and JSON_OBJECTAGG()
+ */
+typedef struct JsonKeyValue
+{
+   NodeTag     type;
+   Expr       *key;            /* key expression */
+   JsonValueExpr *value;       /* JSON value expression */
+} JsonKeyValue;
+
+/*
+ * JsonObjectConstructor -
+ *     untransformed representation of JSON_OBJECT() constructor
+ */
+typedef struct JsonObjectConstructor
+{
+   NodeTag     type;
+   List       *exprs;          /* list of JsonKeyValue pairs */
+   JsonOutput *output;         /* RETURNING clause, if specified  */
+   bool        absent_on_null; /* skip NULL values? */
+   bool        unique;         /* check key uniqueness? */
+   int         location;       /* token location, or -1 if unknown */
+} JsonObjectConstructor;
+
+/*
+ * JsonArrayConstructor -
+ *     untransformed representation of JSON_ARRAY(element,...) constructor
+ */
+typedef struct JsonArrayConstructor
+{
+   NodeTag     type;
+   List       *exprs;          /* list of JsonValueExpr elements */
+   JsonOutput *output;         /* RETURNING clause, if specified  */
+   bool        absent_on_null; /* skip NULL elements? */
+   int         location;       /* token location, or -1 if unknown */
+} JsonArrayConstructor;
+
+/*
+ * JsonArrayQueryConstructor -
+ *     untransformed representation of JSON_ARRAY(subquery) constructor
+ */
+typedef struct JsonArrayQueryConstructor
+{
+   NodeTag     type;
+   Node       *query;          /* subquery */
+   JsonOutput *output;         /* RETURNING clause, if specified  */
+   JsonFormat *format;         /* FORMAT clause for subquery, if specified */
+   bool        absent_on_null; /* skip NULL elements? */
+   int         location;       /* token location, or -1 if unknown */
+} JsonArrayQueryConstructor;
+
+/*
+ * JsonAggConstructor -
+ *     common fields of untransformed representation of
+ *     JSON_ARRAYAGG() and JSON_OBJECTAGG()
+ */
+typedef struct JsonAggConstructor
+{
+   NodeTag     type;
+   JsonOutput *output;         /* RETURNING clause, if any */
+   Node       *agg_filter;     /* FILTER clause, if any */
+   List       *agg_order;      /* ORDER BY clause, if any */
+   struct WindowDef *over;     /* OVER clause, if any */
+   int         location;       /* token location, or -1 if unknown */
+} JsonAggConstructor;
+
+/*
+ * JsonObjectAgg -
+ *     untransformed representation of JSON_OBJECTAGG()
+ */
+typedef struct JsonObjectAgg
+{
+   NodeTag     type;
+   JsonAggConstructor *constructor;    /* common fields */
+   JsonKeyValue *arg;          /* object key-value pair */
+   bool        absent_on_null; /* skip NULL values? */
+   bool        unique;         /* check key uniqueness? */
+} JsonObjectAgg;
+
+/*
+ * JsonArrayAgg -
+ *     untransformed representation of JSON_ARRRAYAGG()
+ */
+typedef struct JsonArrayAgg
+{
+   NodeTag     type;
+   JsonAggConstructor *constructor;    /* common fields */
+   JsonValueExpr *arg;         /* array element expression */
+   bool        absent_on_null; /* skip NULL elements? */
+} JsonArrayAgg;
+
+
 /*****************************************************************************
  *     Raw Grammar Output Statements
  *****************************************************************************/
 
    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;
+
+typedef enum JsonConstructorType
+{
+   JSCTOR_JSON_OBJECT = 1,
+   JSCTOR_JSON_ARRAY = 2,
+   JSCTOR_JSON_OBJECTAGG = 3,
+   JSCTOR_JSON_ARRAYAGG = 4
+} JsonConstructorType;
+
+/*
+ * JsonConstructorExpr -
+ *     wrapper over FuncExpr/Aggref/WindowFunc for SQL/JSON constructors
+ */
+typedef struct JsonConstructorExpr
+{
+   Expr        xpr;
+   JsonConstructorType type;   /* constructor type */
+   List       *args;
+   Expr       *func;           /* underlying json[b]_xxx() function call */
+   Expr       *coercion;       /* coercion to RETURNING type */
+   JsonReturning *returning;   /* RETURNING clause */
+   bool        absent_on_null; /* ABSENT ON NULL? */
+   bool        unique;         /* WITH UNIQUE KEYS? (JSON_OBJECT[AGG] only) */
+   int         location;
+} JsonConstructorExpr;
+
 /* ----------------
  * NullTest
  *
 
 
 /* name, value, category, is-bare-label */
 PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("absent", ABSENT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("action", ACTION, 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)
 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("json_array", JSON_ARRAY, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json_arrayagg", JSON_ARRAYAGG, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json_object", JSON_OBJECT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("json_objectagg", JSON_OBJECTAGG, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("keys", KEYS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD, BARE_LABEL)
 
 extern void escape_json(StringInfo buf, const char *str);
 extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
                                const int *tzp);
+extern bool to_json_is_immutable(Oid typoid);
+extern Datum json_build_object_worker(int nargs, Datum *args, bool *nulls,
+                                     Oid *types, bool absent_on_null,
+                                     bool unique_keys);
+extern Datum json_build_array_worker(int nargs, Datum *args, bool *nulls,
+                                    Oid *types, bool absent_on_null);
 
 #endif                         /* JSON_H */
 
    JsonbValue  contVal;
    Size        size;
    struct JsonbParseState *next;
+   bool        unique_keys;    /* Check object key uniqueness */
+   bool        skip_nulls;     /* Skip null object fields */
 } JsonbParseState;
 
 /*
                               JsonbValue *newval);
 extern Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
                               bool *isnull, bool as_text);
+extern bool to_jsonb_is_immutable(Oid typoid);
+extern Datum jsonb_build_object_worker(int nargs, Datum *args, bool *nulls,
+                                      Oid *types, bool absent_on_null,
+                                      bool unique_keys);
+extern Datum jsonb_build_array_worker(int nargs, Datum *args, bool *nulls,
+                                     Oid *types, bool absent_on_null);
+
 #endif                         /* __JSONB_H__ */
 
 
 # or in the block
 my %replace_string = (
+   'FORMAT_LA'      => 'format',
    'NOT_LA'         => 'not',
    'NULLS_LA'       => 'nulls',
    'WITH_LA'        => 'with',
+   'WITHOUT_LA'     => 'without',
    'TYPECAST'       => '::',
    'DOT_DOT'        => '..',
    'COLON_EQUALS'   => ':=',
 
     */
    switch (cur_token)
    {
+       case FORMAT:
        case NOT:
        case NULLS_P:
        case WITH:
+       case WITHOUT:
        case UIDENT:
        case USCONST:
            break;
    /* Replace cur_token if needed, based on lookahead */
    switch (cur_token)
    {
+       case FORMAT:
+           /* Replace FORMAT by FORMAT_LA if it's followed by JSON */
+           switch (next_token)
+           {
+               case JSON:
+                   cur_token = FORMAT_LA;
+                   break;
+           }
+           break;
+
        case NOT:
            /* Replace NOT by NOT_LA if it's followed by BETWEEN, IN, etc */
            switch (next_token)
                    break;
            }
            break;
+
+       case WITHOUT:
+           /* Replace WITHOUT by WITHOUT_LA if it's followed by UNIQUE */
+           switch (next_token)
+           {
+               case UNIQUE:
+                   cur_token = WITHOUT_LA;
+                   break;
+           }
+           break;
        case UIDENT:
        case USCONST:
            /* Look ahead for UESCAPE */
 
 test: sql/oldexec
 test: sql/quote
 test: sql/show
+test: sql/sqljson
 test: sql/insupd
 test: sql/parser
 test: sql/prepareas
 
--- /dev/null
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "sqljson.pgc"
+#include <stdio.h>
+
+
+#line 1 "sqlca.h"
+#ifndef POSTGRES_SQLCA_H
+#define POSTGRES_SQLCA_H
+
+#ifndef PGDLLIMPORT
+#if  defined(WIN32) || defined(__CYGWIN__)
+#define PGDLLIMPORT __declspec (dllimport)
+#else
+#define PGDLLIMPORT
+#endif                         /* __CYGWIN__ */
+#endif                         /* PGDLLIMPORT */
+
+#define SQLERRMC_LEN   150
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct sqlca_t
+{
+   char        sqlcaid[8];
+   long        sqlabc;
+   long        sqlcode;
+   struct
+   {
+       int         sqlerrml;
+       char        sqlerrmc[SQLERRMC_LEN];
+   }           sqlerrm;
+   char        sqlerrp[8];
+   long        sqlerrd[6];
+   /* Element 0: empty                     */
+   /* 1: OID of processed tuple if applicable          */
+   /* 2: number of rows processed              */
+   /* after an INSERT, UPDATE or               */
+   /* DELETE statement                 */
+   /* 3: empty                     */
+   /* 4: empty                     */
+   /* 5: empty                     */
+   char        sqlwarn[8];
+   /* Element 0: set to 'W' if at least one other is 'W'   */
+   /* 1: if 'W' at least one character string      */
+   /* value was truncated when it was          */
+   /* stored into a host variable.             */
+
+   /*
+    * 2: if 'W' a (hopefully) non-fatal notice occurred
+    */ /* 3: empty */
+   /* 4: empty                     */
+   /* 5: empty                     */
+   /* 6: empty                     */
+   /* 7: empty                     */
+
+   char        sqlstate[5];
+};
+
+struct sqlca_t *ECPGget_sqlca(void);
+
+#ifndef POSTGRES_ECPG_INTERNAL
+#define sqlca (*ECPGget_sqlca())
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#line 3 "sqljson.pgc"
+
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 4 "sqljson.pgc"
+
+
+/* exec sql whenever sqlerror  sqlprint ; */
+#line 6 "sqljson.pgc"
+
+
+int
+main ()
+{
+/* exec sql begin declare section */
+   
+
+#line 12 "sqljson.pgc"
+ char json [ 1024 ] ;
+/* exec sql end declare section */
+#line 13 "sqljson.pgc"
+
+
+  ECPGdebug (1, stderr);
+
+  { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0); 
+#line 17 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 17 "sqljson.pgc"
+
+  { ECPGsetcommit(__LINE__, "on", NULL);
+#line 18 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 18 "sqljson.pgc"
+
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_object ( returning text )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 20 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 20 "sqljson.pgc"
+
+  printf("Found json=%s\n", json);
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_object ( returning text format json )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 23 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 23 "sqljson.pgc"
+
+  printf("Found json=%s\n", json);
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_array ( returning jsonb )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 26 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 26 "sqljson.pgc"
+
+  printf("Found json=%s\n", json);
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_array ( returning jsonb format json )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 29 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 29 "sqljson.pgc"
+
+  printf("Found json=%s\n", json);
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_object ( 1 : 1 , '1' : null with unique )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 32 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 32 "sqljson.pgc"
+
+  // error
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_object ( 1 : 1 , '2' : null , 1 : '2' absent on null without unique keys )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 35 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 35 "sqljson.pgc"
+
+  printf("Found json=%s\n", json);
+
+  { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select json_object ( 1 : 1 , '2' : null absent on null without unique returning jsonb )", ECPGt_EOIT, 
+   ECPGt_char,(json),(long)1024,(long)1,(1024)*sizeof(char), 
+   ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 38 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 38 "sqljson.pgc"
+
+  printf("Found json=%s\n", json);
+
+  { ECPGdisconnect(__LINE__, "CURRENT");
+#line 41 "sqljson.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 41 "sqljson.pgc"
+
+
+  return 0;
+}
 
--- /dev/null
+[NO_PID]: ECPGdebug: set to 1
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>  
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ECPGsetcommit on line 18: action "on"; connection "ecpg1_regression"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 20: query: select json_object ( returning text ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 20: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 20: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 20: RESULT: {} offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 23: query: select json_object ( returning text format json ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 23: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 23: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 23: RESULT: {} offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 26: query: select json_array ( returning jsonb ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 26: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 26: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_is_type_an_array on line 26: type (3802); C (1); array (no)
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 26: RESULT: [] offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 29: query: select json_array ( returning jsonb format json ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 29: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 29: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 29: RESULT: [] offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 32: query: select json_object ( 1 : 1 , '1' : null with unique ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 32: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_check_PQresult on line 32: bad response - ERROR:  duplicate JSON key "1"
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: raising sqlstate 22030 (sqlcode -400): duplicate JSON key "1" on line 32
+[NO_PID]: sqlca: code: -400, state: 22030
+SQL error: duplicate JSON key "1" on line 32
+[NO_PID]: ecpg_execute on line 35: query: select json_object ( 1 : 1 , '2' : null , 1 : '2' absent on null without unique keys ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 35: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 35: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_is_type_an_array on line 35: type (114); C (1); array (no)
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 35: RESULT: {"1" : 1, "1" : "2"} offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 38: query: select json_object ( 1 : 1 , '2' : null absent on null without unique returning jsonb ); with 0 parameter(s) on connection ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 38: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 38: correctly got 1 tuples with 1 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 38: RESULT: {"1": 1} offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_finish: connection ecpg1_regression closed
+[NO_PID]: sqlca: code: 0, state: 00000
 
--- /dev/null
+Found json={}
+Found json={}
+Found json=[]
+Found json=[]
+Found json={"1" : 1, "1" : "2"}
+Found json={"1": 1}
 
         parser parser.c \
         quote quote.c \
         show show.c \
+        sqljson sqljson.c \
         insupd insupd.c \
         twophase twophase.c \
         insupd insupd.c \
 
   'quote',
   'show',
   'sqlda',
+  'sqljson',
   'twophase',
 ]
 
 
--- /dev/null
+#include <stdio.h>
+
+EXEC SQL INCLUDE sqlca;
+exec sql include ../regression;
+
+EXEC SQL WHENEVER SQLERROR sqlprint;
+
+int
+main ()
+{
+EXEC SQL BEGIN DECLARE SECTION;
+  char json[1024];
+EXEC SQL END DECLARE SECTION;
+
+  ECPGdebug (1, stderr);
+
+  EXEC SQL CONNECT TO REGRESSDB1;
+  EXEC SQL SET AUTOCOMMIT = ON;
+
+  EXEC SQL SELECT JSON_OBJECT(RETURNING text) INTO :json;
+  printf("Found json=%s\n", json);
+
+  EXEC SQL SELECT JSON_OBJECT(RETURNING text FORMAT JSON) INTO :json;
+  printf("Found json=%s\n", json);
+
+  EXEC SQL SELECT JSON_ARRAY(RETURNING jsonb) INTO :json;
+  printf("Found json=%s\n", json);
+
+  EXEC SQL SELECT JSON_ARRAY(RETURNING jsonb FORMAT JSON) INTO :json;
+  printf("Found json=%s\n", json);
+
+  EXEC SQL SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE) INTO :json;
+  // error
+
+  EXEC SQL SELECT JSON_OBJECT(1: 1, '2': NULL, 1: '2' ABSENT ON NULL WITHOUT UNIQUE KEYS) INTO :json;
+  printf("Found json=%s\n", json);
+
+  EXEC SQL SELECT JSON_OBJECT(1: 1, '2': NULL ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb) INTO :json;
+  printf("Found json=%s\n", json);
+
+  EXEC SQL DISCONNECT;
+
+  return 0;
+}
 
 ERROR:  argument list must have even number of elements
 HINT:  The arguments of json_build_object() must consist of alternating keys and values.
 SELECT json_build_object(NULL, 'a'); -- error, key cannot be NULL
-ERROR:  argument 1 cannot be null
-HINT:  Object keys should be text.
+ERROR:  null value not allowed for object key
 SELECT json_build_object('a', NULL); -- ok
  json_build_object 
 -------------------
 (1 row)
 
 SELECT json_build_object(VARIADIC ARRAY[NULL, 'a']::text[]); -- error, key cannot be NULL
-ERROR:  argument 1 cannot be null
-HINT:  Object keys should be text.
+ERROR:  null value not allowed for object key
 SELECT json_build_object(VARIADIC '{1,2,3,4}'::text[]); -- ok
    json_build_object    
 ------------------------
 
 -- keys must be scalar and not null
 SELECT json_build_object(null,2);
-ERROR:  argument 1 cannot be null
-HINT:  Object keys should be text.
+ERROR:  null value not allowed for object key
 SELECT json_build_object(r,2) FROM (SELECT 1 AS a, 2 AS b) r;
 ERROR:  key value must be scalar, not array, composite, or json
 SELECT json_build_object(json '{"a":1,"b":2}', 3);
 
 INSERT INTO foo VALUES (999999, NULL, 'bar');
 SELECT json_object_agg(name, type) FROM foo;
-ERROR:  field name must not be null
+ERROR:  null value not allowed for object key
 -- json_object
 -- empty object, one dimension
 SELECT json_object('{}');
 
          NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
      OR (p.pronargs > 2 AND
          NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
-     -- we could carry the check further, but 3 args is enough for now
-     OR (p.pronargs > 3)
+     OR (p.pronargs > 3 AND
+         NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4]))
+     -- we could carry the check further, but 4 args is enough for now
+     OR (p.pronargs > 4)
     );
  aggfnoid | proname | oid | proname 
 ----------+---------+-----+---------
 
--- /dev/null
+-- JSON_OBJECT()
+SELECT JSON_OBJECT();
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING json);
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING json FORMAT JSON);
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING jsonb);
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING jsonb FORMAT JSON);
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING text);
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING text FORMAT JSON);
+ json_object 
+-------------
+ {}
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8);
+ERROR:  cannot set JSON encoding for non-bytea output types
+LINE 1: SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8)...
+                                          ^
+SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
+ERROR:  unrecognized JSON encoding: invalid_encoding
+SELECT JSON_OBJECT(RETURNING bytea);
+ json_object 
+-------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON);
+ json_object 
+-------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8);
+ json_object 
+-------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF16);
+ERROR:  unsupported JSON encoding
+LINE 1: SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF1...
+                                           ^
+HINT:  Only UTF8 JSON encoding is supported.
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF32);
+ERROR:  unsupported JSON encoding
+LINE 1: SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF3...
+                                           ^
+HINT:  Only UTF8 JSON encoding is supported.
+SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
+ERROR:  cannot use non-string types with explicit FORMAT JSON clause
+LINE 1: SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
+                                            ^
+SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF8);
+ERROR:  JSON ENCODING clause is only allowed for bytea input type
+LINE 1: SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF...
+                                            ^
+SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
+WARNING:  FORMAT JSON has no effect for json and jsonb types
+LINE 1: SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
+                                             ^
+  json_object   
+----------------
+ {"foo" : null}
+(1 row)
+
+SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UTF8);
+ERROR:  JSON ENCODING clause is only allowed for bytea input type
+LINE 1: SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UT...
+                                             ^
+SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
+WARNING:  FORMAT JSON has no effect for json and jsonb types
+LINE 1: SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
+                                              ^
+  json_object  
+---------------
+ {"foo": null}
+(1 row)
+
+SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING UTF8);
+ERROR:  JSON ENCODING clause is only allowed for bytea input type
+LINE 1: SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING U...
+                                              ^
+SELECT JSON_OBJECT(NULL: 1);
+ERROR:  null value not allowed for object key
+SELECT JSON_OBJECT('a': 2 + 3);
+ json_object 
+-------------
+ {"a" : 5}
+(1 row)
+
+SELECT JSON_OBJECT('a' VALUE 2 + 3);
+ json_object 
+-------------
+ {"a" : 5}
+(1 row)
+
+--SELECT JSON_OBJECT(KEY 'a' VALUE 2 + 3);
+SELECT JSON_OBJECT('a' || 2: 1);
+ json_object 
+-------------
+ {"a2" : 1}
+(1 row)
+
+SELECT JSON_OBJECT(('a' || 2) VALUE 1);
+ json_object 
+-------------
+ {"a2" : 1}
+(1 row)
+
+--SELECT JSON_OBJECT('a' || 2 VALUE 1);
+--SELECT JSON_OBJECT(KEY 'a' || 2 VALUE 1);
+SELECT JSON_OBJECT('a': 2::text);
+ json_object 
+-------------
+ {"a" : "2"}
+(1 row)
+
+SELECT JSON_OBJECT('a' VALUE 2::text);
+ json_object 
+-------------
+ {"a" : "2"}
+(1 row)
+
+--SELECT JSON_OBJECT(KEY 'a' VALUE 2::text);
+SELECT JSON_OBJECT(1::text: 2);
+ json_object 
+-------------
+ {"1" : 2}
+(1 row)
+
+SELECT JSON_OBJECT((1::text) VALUE 2);
+ json_object 
+-------------
+ {"1" : 2}
+(1 row)
+
+--SELECT JSON_OBJECT(1::text VALUE 2);
+--SELECT JSON_OBJECT(KEY 1::text VALUE 2);
+SELECT JSON_OBJECT(json '[1]': 123);
+ERROR:  key value must be scalar, not array, composite, or json
+SELECT JSON_OBJECT(ARRAY[1,2,3]: 'aaa');
+ERROR:  key value must be scalar, not array, composite, or json
+SELECT JSON_OBJECT(
+   'a': '123',
+   1.23: 123,
+   'c': json '[ 1,true,{ } ]',
+   'd': jsonb '{ "x" : 123.45 }'
+);
+                            json_object                            
+-------------------------------------------------------------------
+ {"a": "123", "c": [1, true, {}], "d": {"x": 123.45}, "1.23": 123}
+(1 row)
+
+SELECT JSON_OBJECT(
+   'a': '123',
+   1.23: 123,
+   'c': json '[ 1,true,{ } ]',
+   'd': jsonb '{ "x" : 123.45 }'
+   RETURNING jsonb
+);
+                            json_object                            
+-------------------------------------------------------------------
+ {"a": "123", "c": [1, true, {}], "d": {"x": 123.45}, "1.23": 123}
+(1 row)
+
+/*
+SELECT JSON_OBJECT(
+   'a': '123',
+   KEY 1.23 VALUE 123,
+   'c' VALUE json '[1, true, {}]'
+);
+*/
+SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa'));
+                  json_object                  
+-----------------------------------------------
+ {"a" : "123", "b" : {"a" : 111, "b" : "aaa"}}
+(1 row)
+
+SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa' RETURNING jsonb));
+                json_object                
+-------------------------------------------
+ {"a": "123", "b": {"a": 111, "b": "aaa"}}
+(1 row)
+
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text));
+      json_object      
+-----------------------
+ {"a" : "{\"b\" : 1}"}
+(1 row)
+
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text) FORMAT JSON);
+    json_object    
+-------------------
+ {"a" : {"b" : 1}}
+(1 row)
+
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea));
+           json_object           
+---------------------------------
+ {"a" : "\\x7b226222203a20317d"}
+(1 row)
+
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea) FORMAT JSON);
+    json_object    
+-------------------
+ {"a" : {"b" : 1}}
+(1 row)
+
+SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2);
+           json_object            
+----------------------------------
+ {"a" : "1", "b" : null, "c" : 2}
+(1 row)
+
+SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 NULL ON NULL);
+           json_object            
+----------------------------------
+ {"a" : "1", "b" : null, "c" : 2}
+(1 row)
+
+SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 ABSENT ON NULL);
+     json_object      
+----------------------
+ {"a" : "1", "c" : 2}
+(1 row)
+
+SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE);
+ERROR:  duplicate JSON key "1"
+SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE);
+ERROR:  duplicate JSON key "1"
+SELECT JSON_OBJECT(1: 1, '1': NULL NULL ON NULL WITH UNIQUE RETURNING jsonb);
+ERROR:  duplicate JSON object key
+SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+ERROR:  duplicate JSON object key
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 NULL ON NULL WITH UNIQUE);
+ERROR:  duplicate JSON key "1"
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE);
+ERROR:  duplicate JSON key "1"
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE);
+    json_object     
+--------------------
+ {"1" : 1, "1" : 1}
+(1 row)
+
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+ERROR:  duplicate JSON object key
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb);
+ json_object 
+-------------
+ {"1": 1}
+(1 row)
+
+SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+        json_object         
+----------------------------
+ {"1": 1, "3": 1, "5": "a"}
+(1 row)
+
+-- JSON_ARRAY()
+SELECT JSON_ARRAY();
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING json);
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING json FORMAT JSON);
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING jsonb);
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING jsonb FORMAT JSON);
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING text);
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING text FORMAT JSON);
+ json_array 
+------------
+ []
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
+ERROR:  cannot set JSON encoding for non-bytea output types
+LINE 1: SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
+                                         ^
+SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
+ERROR:  unrecognized JSON encoding: invalid_encoding
+SELECT JSON_ARRAY(RETURNING bytea);
+ json_array 
+------------
+ \x5b5d
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON);
+ json_array 
+------------
+ \x5b5d
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF8);
+ json_array 
+------------
+ \x5b5d
+(1 row)
+
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16);
+ERROR:  unsupported JSON encoding
+LINE 1: SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16...
+                                          ^
+HINT:  Only UTF8 JSON encoding is supported.
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32);
+ERROR:  unsupported JSON encoding
+LINE 1: SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32...
+                                          ^
+HINT:  Only UTF8 JSON encoding is supported.
+SELECT JSON_ARRAY('aaa', 111, true, array[1,2,3], NULL, json '{"a": [1]}', jsonb '["a",3]');
+                     json_array                      
+-----------------------------------------------------
+ ["aaa", 111, true, [1, 2, 3], {"a": [1]}, ["a", 3]]
+(1 row)
+
+SELECT JSON_ARRAY('a',  NULL, 'b' NULL   ON NULL);
+    json_array    
+------------------
+ ["a", null, "b"]
+(1 row)
+
+SELECT JSON_ARRAY('a',  NULL, 'b' ABSENT ON NULL);
+ json_array 
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL);
+ json_array 
+------------
+ ["b"]
+(1 row)
+
+SELECT JSON_ARRAY('a',  NULL, 'b' NULL   ON NULL RETURNING jsonb);
+    json_array    
+------------------
+ ["a", null, "b"]
+(1 row)
+
+SELECT JSON_ARRAY('a',  NULL, 'b' ABSENT ON NULL RETURNING jsonb);
+ json_array 
+------------
+ ["a", "b"]
+(1 row)
+
+SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL RETURNING jsonb);
+ json_array 
+------------
+ ["b"]
+(1 row)
+
+SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' RETURNING text));
+          json_array           
+-------------------------------
+ ["[\"{ \\\"a\\\" : 123 }\"]"]
+(1 row)
+
+SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text));
+      json_array       
+-----------------------
+ ["[{ \"a\" : 123 }]"]
+(1 row)
+
+SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT JSON);
+    json_array     
+-------------------
+ [[{ "a" : 123 }]]
+(1 row)
+
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
+ json_array 
+------------
+ [1, 2, 4]
+(1 row)
+
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
+ json_array 
+------------
+ [[1,2],   +
+  [3,4]]
+(1 row)
+
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
+    json_array    
+------------------
+ [[1, 2], [3, 4]]
+(1 row)
+
+--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
+--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
+ json_array 
+------------
+ [1, 2, 3]
+(1 row)
+
+-- Should fail
+SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
+ERROR:  subquery must return only one column
+LINE 1: SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
+               ^
+SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
+ERROR:  subquery must return only one column
+LINE 1: SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
+               ^
+SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
+ERROR:  subquery must return only one column
+LINE 1: SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
+               ^
+-- JSON_ARRAYAGG()
+SELECT JSON_ARRAYAGG(i) IS NULL,
+       JSON_ARRAYAGG(i RETURNING jsonb) IS NULL
+FROM generate_series(1, 0) i;
+ ?column? | ?column? 
+----------+----------
+ t        | t
+(1 row)
+
+SELECT JSON_ARRAYAGG(i),
+       JSON_ARRAYAGG(i RETURNING jsonb)
+FROM generate_series(1, 5) i;
+  json_arrayagg  |  json_arrayagg  
+-----------------+-----------------
+ [1, 2, 3, 4, 5] | [1, 2, 3, 4, 5]
+(1 row)
+
+SELECT JSON_ARRAYAGG(i ORDER BY i DESC)
+FROM generate_series(1, 5) i;
+  json_arrayagg  
+-----------------
+ [5, 4, 3, 2, 1]
+(1 row)
+
+SELECT JSON_ARRAYAGG(i::text::json)
+FROM generate_series(1, 5) i;
+  json_arrayagg  
+-----------------
+ [1, 2, 3, 4, 5]
+(1 row)
+
+SELECT JSON_ARRAYAGG(JSON_ARRAY(i, i + 1 RETURNING text) FORMAT JSON)
+FROM generate_series(1, 5) i;
+              json_arrayagg               
+------------------------------------------
+ [[1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]
+(1 row)
+
+SELECT JSON_ARRAYAGG(NULL),
+       JSON_ARRAYAGG(NULL RETURNING jsonb)
+FROM generate_series(1, 5);
+ json_arrayagg | json_arrayagg 
+---------------+---------------
+ []            | []
+(1 row)
+
+SELECT JSON_ARRAYAGG(NULL NULL ON NULL),
+       JSON_ARRAYAGG(NULL NULL ON NULL RETURNING jsonb)
+FROM generate_series(1, 5);
+         json_arrayagg          |         json_arrayagg          
+--------------------------------+--------------------------------
+ [null, null, null, null, null] | [null, null, null, null, null]
+(1 row)
+
+\x
+SELECT
+   JSON_ARRAYAGG(bar) as no_options,
+   JSON_ARRAYAGG(bar RETURNING jsonb) as returning_jsonb,
+   JSON_ARRAYAGG(bar ABSENT ON NULL) as absent_on_null,
+   JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb) as absentonnull_returning_jsonb,
+   JSON_ARRAYAGG(bar NULL ON NULL) as null_on_null,
+   JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb) as nullonnull_returning_jsonb,
+   JSON_ARRAYAGG(foo) as row_no_options,
+   JSON_ARRAYAGG(foo RETURNING jsonb) as row_returning_jsonb,
+   JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2) as row_filtered_agg,
+   JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2) as row_filtered_agg_returning_jsonb
+FROM
+   (VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
+-[ RECORD 1 ]--------------------+-------------------------------------------------------------------------------------------------------------------------
+no_options                       | [1, 2, 3, 4, 5]
+returning_jsonb                  | [1, 2, 3, 4, 5]
+absent_on_null                   | [1, 2, 3, 4, 5]
+absentonnull_returning_jsonb     | [1, 2, 3, 4, 5]
+null_on_null                     | [1, 2, 3, 4, 5, null, null, null, null]
+nullonnull_returning_jsonb       | [1, 2, 3, 4, 5, null, null, null, null]
+row_no_options                   | [{"bar":1},                                                                                                             +
+                                 |  {"bar":2},                                                                                                             +
+                                 |  {"bar":3},                                                                                                             +
+                                 |  {"bar":4},                                                                                                             +
+                                 |  {"bar":5},                                                                                                             +
+                                 |  {"bar":null},                                                                                                          +
+                                 |  {"bar":null},                                                                                                          +
+                                 |  {"bar":null},                                                                                                          +
+                                 |  {"bar":null}]
+row_returning_jsonb              | [{"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 5}, {"bar": null}, {"bar": null}, {"bar": null}, {"bar": null}]
+row_filtered_agg                 | [{"bar":3},                                                                                                             +
+                                 |  {"bar":4},                                                                                                             +
+                                 |  {"bar":5}]
+row_filtered_agg_returning_jsonb | [{"bar": 3}, {"bar": 4}, {"bar": 5}]
+
+\x
+SELECT
+   bar, JSON_ARRAYAGG(bar) FILTER (WHERE bar > 2) OVER (PARTITION BY foo.bar % 2)
+FROM
+   (VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL), (5), (4)) foo(bar);
+ bar | json_arrayagg 
+-----+---------------
+   4 | [4, 4]
+   4 | [4, 4]
+   2 | [4, 4]
+   5 | [5, 3, 5]
+   3 | [5, 3, 5]
+   1 | [5, 3, 5]
+   5 | [5, 3, 5]
+     | 
+     | 
+     | 
+     | 
+(11 rows)
+
+-- JSON_OBJECTAGG()
+SELECT JSON_OBJECTAGG('key': 1) IS NULL,
+       JSON_OBJECTAGG('key': 1 RETURNING jsonb) IS NULL
+WHERE FALSE;
+ ?column? | ?column? 
+----------+----------
+ t        | t
+(1 row)
+
+SELECT JSON_OBJECTAGG(NULL: 1);
+ERROR:  null value not allowed for object key
+SELECT JSON_OBJECTAGG(NULL: 1 RETURNING jsonb);
+ERROR:  field name must not be null
+SELECT
+   JSON_OBJECTAGG(i: i),
+-- JSON_OBJECTAGG(i VALUE i),
+-- JSON_OBJECTAGG(KEY i VALUE i),
+   JSON_OBJECTAGG(i: i RETURNING jsonb)
+FROM
+   generate_series(1, 5) i;
+                 json_objectagg                  |              json_objectagg              
+-------------------------------------------------+------------------------------------------
+ { "1" : 1, "2" : 2, "3" : 3, "4" : 4, "5" : 5 } | {"1": 1, "2": 2, "3": 3, "4": 4, "5": 5}
+(1 row)
+
+SELECT
+   JSON_OBJECTAGG(k: v),
+   JSON_OBJECTAGG(k: v NULL ON NULL),
+   JSON_OBJECTAGG(k: v ABSENT ON NULL),
+   JSON_OBJECTAGG(k: v RETURNING jsonb),
+   JSON_OBJECTAGG(k: v NULL ON NULL RETURNING jsonb),
+   JSON_OBJECTAGG(k: v ABSENT ON NULL RETURNING jsonb)
+FROM
+   (VALUES (1, 1), (1, NULL), (2, NULL), (3, 3)) foo(k, v);
+                json_objectagg                |                json_objectagg                |    json_objectagg    |         json_objectagg         |         json_objectagg         |  json_objectagg  
+----------------------------------------------+----------------------------------------------+----------------------+--------------------------------+--------------------------------+------------------
+ { "1" : 1, "1" : null, "2" : null, "3" : 3 } | { "1" : 1, "1" : null, "2" : null, "3" : 3 } | { "1" : 1, "3" : 3 } | {"1": null, "2": null, "3": 3} | {"1": null, "2": null, "3": 3} | {"1": 1, "3": 3}
+(1 row)
+
+SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+ERROR:  duplicate JSON key "1"
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+ERROR:  duplicate JSON key "1"
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
+FROM (VALUES (1, 1), (0, NULL), (3, NULL), (2, 2), (4, NULL)) foo(k, v);
+    json_objectagg    
+----------------------
+ { "1" : 1, "2" : 2 }
+(1 row)
+
+SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS RETURNING jsonb)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+ERROR:  duplicate JSON object key
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+ERROR:  duplicate JSON object key
+-- Test JSON_OBJECT deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
+                                  QUERY PLAN                                  
+------------------------------------------------------------------------------
+ Result
+   Output: JSON_OBJECT('foo' : '1'::json, 'bar' : 'baz'::text RETURNING json)
+(2 rows)
+
+CREATE VIEW json_object_view AS
+SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
+\sv json_object_view
+CREATE OR REPLACE VIEW public.json_object_view AS
+ SELECT JSON_OBJECT('foo' : '1'::text FORMAT JSON, 'bar' : 'baz'::text RETURNING json) AS "json_object"
+DROP VIEW json_object_view;
+-- Test JSON_ARRAY deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
+                    QUERY PLAN                     
+---------------------------------------------------
+ Result
+   Output: JSON_ARRAY('1'::json, 2 RETURNING json)
+(2 rows)
+
+CREATE VIEW json_array_view AS
+SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
+\sv json_array_view
+CREATE OR REPLACE VIEW public.json_array_view AS
+ SELECT JSON_ARRAY('1'::text FORMAT JSON, 2 RETURNING json) AS "json_array"
+DROP VIEW json_array_view;
+-- Test JSON_OBJECTAGG deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+                                                              QUERY PLAN                                                              
+--------------------------------------------------------------------------------------------------------------------------------------
+ Aggregate
+   Output: JSON_OBJECTAGG(i : (('111'::text || (i)::text))::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE (i > 3))
+   ->  Function Scan on pg_catalog.generate_series i
+         Output: i
+         Function Call: generate_series(1, 5)
+(5 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
+FROM generate_series(1,5) i;
+                                                            QUERY PLAN                                                             
+-----------------------------------------------------------------------------------------------------------------------------------
+ WindowAgg
+   Output: JSON_OBJECTAGG(i : (('111'::text || (i)::text))::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) OVER (?), ((i % 2))
+   ->  Sort
+         Output: ((i % 2)), i
+         Sort Key: ((i.i % 2))
+         ->  Function Scan on pg_catalog.generate_series i
+               Output: (i % 2), i
+               Function Call: generate_series(1, 5)
+(8 rows)
+
+CREATE VIEW json_objectagg_view AS
+SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+\sv json_objectagg_view
+CREATE OR REPLACE VIEW public.json_objectagg_view AS
+ SELECT JSON_OBJECTAGG(i : ('111'::text || i)::bytea FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i > 3) AS "json_objectagg"
+   FROM generate_series(1, 5) i(i)
+DROP VIEW json_objectagg_view;
+-- Test JSON_ARRAYAGG deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+                                                         QUERY PLAN                                                          
+-----------------------------------------------------------------------------------------------------------------------------
+ Aggregate
+   Output: JSON_ARRAYAGG((('111'::text || (i)::text))::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE (i > 3))
+   ->  Function Scan on pg_catalog.generate_series i
+         Output: i
+         Function Call: generate_series(1, 5)
+(5 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
+FROM generate_series(1,5) i;
+                                                        QUERY PLAN                                                        
+--------------------------------------------------------------------------------------------------------------------------
+ WindowAgg
+   Output: JSON_ARRAYAGG((('111'::text || (i)::text))::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (?), ((i % 2))
+   ->  Sort
+         Output: ((i % 2)), i
+         Sort Key: ((i.i % 2))
+         ->  Function Scan on pg_catalog.generate_series i
+               Output: (i % 2), i
+               Function Call: generate_series(1, 5)
+(8 rows)
+
+CREATE VIEW json_arrayagg_view AS
+SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+\sv json_arrayagg_view
+CREATE OR REPLACE VIEW public.json_arrayagg_view AS
+ SELECT JSON_ARRAYAGG(('111'::text || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3) AS "json_arrayagg"
+   FROM generate_series(1, 5) i(i)
+DROP VIEW json_arrayagg_view;
+-- Test JSON_ARRAY(subquery) deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
+                             QUERY PLAN                              
+---------------------------------------------------------------------
+ Result
+   Output: $0
+   InitPlan 1 (returns $0)
+     ->  Aggregate
+           Output: JSON_ARRAYAGG("*VALUES*".column1 RETURNING jsonb)
+           ->  Values Scan on "*VALUES*"
+                 Output: "*VALUES*".column1
+(7 rows)
+
+CREATE VIEW json_array_subquery_view AS
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
+\sv json_array_subquery_view
+CREATE OR REPLACE VIEW public.json_array_subquery_view AS
+ SELECT ( SELECT JSON_ARRAYAGG(q.a RETURNING jsonb) AS "json_arrayagg"
+           FROM ( SELECT foo.i
+                   FROM ( VALUES (1), (2), (NULL::integer), (4)) foo(i)) q(a)) AS "json_array"
+DROP VIEW json_array_subquery_view;
 
 # ----------
 # Another group of parallel tests (JSON related)
 # ----------
-test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath
+test: json jsonb json_encoding jsonpath jsonpath_encoding jsonb_jsonpath sqljson
 
 # ----------
 # Another group of parallel tests
 
          NOT binary_coercible(p.proargtypes[1], ptr.proargtypes[2]))
      OR (p.pronargs > 2 AND
          NOT binary_coercible(p.proargtypes[2], ptr.proargtypes[3]))
-     -- we could carry the check further, but 3 args is enough for now
-     OR (p.pronargs > 3)
+     OR (p.pronargs > 3 AND
+         NOT binary_coercible(p.proargtypes[3], ptr.proargtypes[4]))
+     -- we could carry the check further, but 4 args is enough for now
+     OR (p.pronargs > 4)
     );
 
 -- Cross-check finalfn (if present) against its entry in pg_proc.
 
--- /dev/null
+-- JSON_OBJECT()
+SELECT JSON_OBJECT();
+SELECT JSON_OBJECT(RETURNING json);
+SELECT JSON_OBJECT(RETURNING json FORMAT JSON);
+SELECT JSON_OBJECT(RETURNING jsonb);
+SELECT JSON_OBJECT(RETURNING jsonb FORMAT JSON);
+SELECT JSON_OBJECT(RETURNING text);
+SELECT JSON_OBJECT(RETURNING text FORMAT JSON);
+SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8);
+SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
+SELECT JSON_OBJECT(RETURNING bytea);
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON);
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF8);
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF16);
+SELECT JSON_OBJECT(RETURNING bytea FORMAT JSON ENCODING UTF32);
+
+SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON);
+SELECT JSON_OBJECT('foo': NULL::int FORMAT JSON ENCODING UTF8);
+SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON);
+SELECT JSON_OBJECT('foo': NULL::json FORMAT JSON ENCODING UTF8);
+SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON);
+SELECT JSON_OBJECT('foo': NULL::jsonb FORMAT JSON ENCODING UTF8);
+
+SELECT JSON_OBJECT(NULL: 1);
+SELECT JSON_OBJECT('a': 2 + 3);
+SELECT JSON_OBJECT('a' VALUE 2 + 3);
+--SELECT JSON_OBJECT(KEY 'a' VALUE 2 + 3);
+SELECT JSON_OBJECT('a' || 2: 1);
+SELECT JSON_OBJECT(('a' || 2) VALUE 1);
+--SELECT JSON_OBJECT('a' || 2 VALUE 1);
+--SELECT JSON_OBJECT(KEY 'a' || 2 VALUE 1);
+SELECT JSON_OBJECT('a': 2::text);
+SELECT JSON_OBJECT('a' VALUE 2::text);
+--SELECT JSON_OBJECT(KEY 'a' VALUE 2::text);
+SELECT JSON_OBJECT(1::text: 2);
+SELECT JSON_OBJECT((1::text) VALUE 2);
+--SELECT JSON_OBJECT(1::text VALUE 2);
+--SELECT JSON_OBJECT(KEY 1::text VALUE 2);
+SELECT JSON_OBJECT(json '[1]': 123);
+SELECT JSON_OBJECT(ARRAY[1,2,3]: 'aaa');
+
+SELECT JSON_OBJECT(
+   'a': '123',
+   1.23: 123,
+   'c': json '[ 1,true,{ } ]',
+   'd': jsonb '{ "x" : 123.45 }'
+);
+
+SELECT JSON_OBJECT(
+   'a': '123',
+   1.23: 123,
+   'c': json '[ 1,true,{ } ]',
+   'd': jsonb '{ "x" : 123.45 }'
+   RETURNING jsonb
+);
+
+/*
+SELECT JSON_OBJECT(
+   'a': '123',
+   KEY 1.23 VALUE 123,
+   'c' VALUE json '[1, true, {}]'
+);
+*/
+
+SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa'));
+SELECT JSON_OBJECT('a': '123', 'b': JSON_OBJECT('a': 111, 'b': 'aaa' RETURNING jsonb));
+
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text));
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING text) FORMAT JSON);
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea));
+SELECT JSON_OBJECT('a': JSON_OBJECT('b': 1 RETURNING bytea) FORMAT JSON);
+
+SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2);
+SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 NULL ON NULL);
+SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 ABSENT ON NULL);
+
+SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE);
+SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE);
+SELECT JSON_OBJECT(1: 1, '1': NULL NULL ON NULL WITH UNIQUE RETURNING jsonb);
+SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 NULL ON NULL WITH UNIQUE);
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE);
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE);
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb);
+SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+
+
+-- JSON_ARRAY()
+SELECT JSON_ARRAY();
+SELECT JSON_ARRAY(RETURNING json);
+SELECT JSON_ARRAY(RETURNING json FORMAT JSON);
+SELECT JSON_ARRAY(RETURNING jsonb);
+SELECT JSON_ARRAY(RETURNING jsonb FORMAT JSON);
+SELECT JSON_ARRAY(RETURNING text);
+SELECT JSON_ARRAY(RETURNING text FORMAT JSON);
+SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
+SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
+SELECT JSON_ARRAY(RETURNING bytea);
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON);
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF8);
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF16);
+SELECT JSON_ARRAY(RETURNING bytea FORMAT JSON ENCODING UTF32);
+
+SELECT JSON_ARRAY('aaa', 111, true, array[1,2,3], NULL, json '{"a": [1]}', jsonb '["a",3]');
+
+SELECT JSON_ARRAY('a',  NULL, 'b' NULL   ON NULL);
+SELECT JSON_ARRAY('a',  NULL, 'b' ABSENT ON NULL);
+SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL);
+SELECT JSON_ARRAY('a',  NULL, 'b' NULL   ON NULL RETURNING jsonb);
+SELECT JSON_ARRAY('a',  NULL, 'b' ABSENT ON NULL RETURNING jsonb);
+SELECT JSON_ARRAY(NULL, NULL, 'b' ABSENT ON NULL RETURNING jsonb);
+
+SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' RETURNING text));
+SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text));
+SELECT JSON_ARRAY(JSON_ARRAY('{ "a" : 123 }' FORMAT JSON RETURNING text) FORMAT JSON);
+
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i));
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i));
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) RETURNING jsonb);
+--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL);
+--SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb);
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i);
+-- Should fail
+SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i));
+SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i));
+SELECT JSON_ARRAY(SELECT * FROM (VALUES (1, 2)) foo(i, j));
+
+-- JSON_ARRAYAGG()
+SELECT JSON_ARRAYAGG(i) IS NULL,
+       JSON_ARRAYAGG(i RETURNING jsonb) IS NULL
+FROM generate_series(1, 0) i;
+
+SELECT JSON_ARRAYAGG(i),
+       JSON_ARRAYAGG(i RETURNING jsonb)
+FROM generate_series(1, 5) i;
+
+SELECT JSON_ARRAYAGG(i ORDER BY i DESC)
+FROM generate_series(1, 5) i;
+
+SELECT JSON_ARRAYAGG(i::text::json)
+FROM generate_series(1, 5) i;
+
+SELECT JSON_ARRAYAGG(JSON_ARRAY(i, i + 1 RETURNING text) FORMAT JSON)
+FROM generate_series(1, 5) i;
+
+SELECT JSON_ARRAYAGG(NULL),
+       JSON_ARRAYAGG(NULL RETURNING jsonb)
+FROM generate_series(1, 5);
+
+SELECT JSON_ARRAYAGG(NULL NULL ON NULL),
+       JSON_ARRAYAGG(NULL NULL ON NULL RETURNING jsonb)
+FROM generate_series(1, 5);
+
+\x
+SELECT
+   JSON_ARRAYAGG(bar) as no_options,
+   JSON_ARRAYAGG(bar RETURNING jsonb) as returning_jsonb,
+   JSON_ARRAYAGG(bar ABSENT ON NULL) as absent_on_null,
+   JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb) as absentonnull_returning_jsonb,
+   JSON_ARRAYAGG(bar NULL ON NULL) as null_on_null,
+   JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb) as nullonnull_returning_jsonb,
+   JSON_ARRAYAGG(foo) as row_no_options,
+   JSON_ARRAYAGG(foo RETURNING jsonb) as row_returning_jsonb,
+   JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2) as row_filtered_agg,
+   JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2) as row_filtered_agg_returning_jsonb
+FROM
+   (VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
+\x
+
+SELECT
+   bar, JSON_ARRAYAGG(bar) FILTER (WHERE bar > 2) OVER (PARTITION BY foo.bar % 2)
+FROM
+   (VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL), (5), (4)) foo(bar);
+
+-- JSON_OBJECTAGG()
+SELECT JSON_OBJECTAGG('key': 1) IS NULL,
+       JSON_OBJECTAGG('key': 1 RETURNING jsonb) IS NULL
+WHERE FALSE;
+
+SELECT JSON_OBJECTAGG(NULL: 1);
+
+SELECT JSON_OBJECTAGG(NULL: 1 RETURNING jsonb);
+
+SELECT
+   JSON_OBJECTAGG(i: i),
+-- JSON_OBJECTAGG(i VALUE i),
+-- JSON_OBJECTAGG(KEY i VALUE i),
+   JSON_OBJECTAGG(i: i RETURNING jsonb)
+FROM
+   generate_series(1, 5) i;
+
+SELECT
+   JSON_OBJECTAGG(k: v),
+   JSON_OBJECTAGG(k: v NULL ON NULL),
+   JSON_OBJECTAGG(k: v ABSENT ON NULL),
+   JSON_OBJECTAGG(k: v RETURNING jsonb),
+   JSON_OBJECTAGG(k: v NULL ON NULL RETURNING jsonb),
+   JSON_OBJECTAGG(k: v ABSENT ON NULL RETURNING jsonb)
+FROM
+   (VALUES (1, 1), (1, NULL), (2, NULL), (3, 3)) foo(k, v);
+
+SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS)
+FROM (VALUES (1, 1), (0, NULL), (3, NULL), (2, 2), (4, NULL)) foo(k, v);
+
+SELECT JSON_OBJECTAGG(k: v WITH UNIQUE KEYS RETURNING jsonb)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+
+SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb)
+FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v);
+
+-- Test JSON_OBJECT deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
+
+CREATE VIEW json_object_view AS
+SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json);
+
+\sv json_object_view
+
+DROP VIEW json_object_view;
+
+-- Test JSON_ARRAY deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
+
+CREATE VIEW json_array_view AS
+SELECT JSON_ARRAY('1' FORMAT JSON, 2 RETURNING json);
+
+\sv json_array_view
+
+DROP VIEW json_array_view;
+
+-- Test JSON_OBJECTAGG deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) OVER (PARTITION BY i % 2)
+FROM generate_series(1,5) i;
+
+CREATE VIEW json_objectagg_view AS
+SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+
+\sv json_objectagg_view
+
+DROP VIEW json_objectagg_view;
+
+-- Test JSON_ARRAYAGG deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) OVER (PARTITION BY i % 2)
+FROM generate_series(1,5) i;
+
+CREATE VIEW json_arrayagg_view AS
+SELECT JSON_ARRAYAGG(('111' || i)::bytea FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i > 3)
+FROM generate_series(1,5) i;
+
+\sv json_arrayagg_view
+
+DROP VIEW json_arrayagg_view;
+
+-- Test JSON_ARRAY(subquery) deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
+
+CREATE VIEW json_array_subquery_view AS
+SELECT JSON_ARRAY(SELECT i FROM (VALUES (1), (2), (NULL), (4)) foo(i) RETURNING jsonb);
+
+\sv json_array_subquery_view
+
+DROP VIEW json_array_subquery_view;
 
 JsonCoercion
 JsonCommon
 JsonConstructorExpr
+JsonConstructorExprState
 JsonConstructorType
 JsonEncoding
 JsonExpr