pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
To: Oleg Bartunov <obartunov@postgrespro.ru>
To: PostgreSQL mailing lists <pgsql-hackers@postgresql.org>
Cc: Alexander Korotkov <a.korotkov@postgrespro.ru>
Cc: Andres Freund <andres@anarazel.de>
Cc: Michael Paquier <michael@paquier.xyz>
Cc: Simon Riggs <simon@2ndquadrant.com>
Cc: Andrew Dunstan <andrew.dunstan@2ndquadrant.com>
Cc: Pavel Stehule <pavel.stehule@gmail.com>
Subject: Re: SQL/JSON: functions
Date: Thu, 28 Jun 2018 03:18:03 +0300
Message-ID: <e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru> (raw)
In-Reply-To: <6862ba8f-70bf-6dd8-5651-5d9050c8f4de@postgrespro.ru>
References: <cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru>
	<c8270c17-6fbb-8d59-5203-53c7b69dff0f@postgrespro.ru>
	<0efb4b8a-bf9e-0411-c0d2-8758e07c4341@postgrespro.ru>
	<ad6b0d6f-d789-869e-d508-a114e8ddff23@postgrespro.ru>
	<25cd89db-68d1-426c-19b0-b4cc99b32acd@postgrespro.ru>
	<CANP8+j+-LLfChyoEoWmrgZuMVLWTZzjzWSPBJtCCp7paGO1FHg@mail.gmail.com>
	<CAF4Au4z2NxVQQG1YjqWM0+9iZjAso0O=GTF4HQKMkfOZitNhuA@mail.gmail.com>
	<20180313225435.GA1150@paquier.xyz>
	<20180313231058.rxjdfwn3ysarsdqr@alap3.anarazel.de>
	<CAPpHfduco9w_Q+rHHcBDBxKUW1QqS_hFEijON2RpLb37o_6SZA@mail.gmail.com>
	<CAF4Au4z3SBWu9APYjk0Ge+_5aZBbOM-SwxXXs0frh-Y-RDJ0Sw@mail.gmail.com>
	<6862ba8f-70bf-6dd8-5651-5d9050c8f4de@postgrespro.ru>

On 15.03.2018 20:04, Nikita Glukhov wrote:
> Attached 13th version of the patches:
>
> * Subtransactions in PG_TRY/CATCH in ExecEvalJsonExpr() were made unconditional,
>   regardless of the volatility of expressions.
>
> * PG_TRY/CATCH in ExecEvalExprPassingCaseValue() was removed along with the
>   entire function.


Attached 15th version of the patches:
  * disabled parallel execution of SQL/JSON query functions when internal
    subtransactions are used (if ERROR ON ERROR is not specified)
  * added experimental optimization of internal subtransactions (see below)


The new patch #14 is an experimental attempt to reduce overhead of
subtransaction start/commit which can result in 2x-slowdown in the simplest
cases.  By the idea of Alexander Korotkov, subtransaction is not really
committed if it has not touched the database and its XID has not been assigned
(DB modification is not expected in type casts functions) and then can be reused
when the next subtransaction is started.  So, all rows in JsonExpr can be
executed in the single cached subtransaction.  This optimization really helps
to reduce overhead from 100% to 5-10%:

-- without subtransactions
=# EXPLAIN ANALYZE
    SELECT JSON_VALUE('true'::jsonb, '$' RETURNING boolean ERROR ON ERROR)
    FROM generate_series(1, 10000000) i;
  ...
  Execution Time: 2785.410 ms

-- cached subtransactions
=# EXPLAIN ANALYZE
    SELECT JSON_VALUE('true'::jsonb, '$' RETURNING boolean)
    FROM generate_series(1, 10000000) i;
  ...
  Execution Time: 2939.363 ms

-- ordinary subtransactions
=# EXPLAIN ANALYZE
    SELECT JSON_VALUE('true'::jsonb, '$' RETURNING boolean)
    FROM generate_series(1, 10000000) i;
  ...
  Execution Time: 5417.268 ms


But, unfortunately, I don't believe that this patch is completely correct,
mainly because the behavior of subtransaction callbacks (and their expectations
about subtransaction's lifecycle too) seems unpredictable to me.



Even with this optimization, internal subtransactions still have one major
drawback -- they disallow parallel query execution, because background
workers do not support subtransactions now.  Example:

=# CREATE TABLE test_parallel_json_value AS
    SELECT i::text::jsonb AS js FROM generate_series(1, 5000000) i;
CREATE TABLE

=# EXPLAIN ANALYZE
    SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR))
    FROM test_parallel_json_value;
                                                                QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
  Finalize Aggregate  (cost=79723.15..79723.16 rows=1 width=32) (actual time=455.062..455.062 rows=1 loops=1)
    ->  Gather  (cost=79722.93..79723.14 rows=2 width=32) (actual time=455.052..455.055 rows=3 loops=1)
          Workers Planned: 2
          Workers Launched: 2
          ->  Partial Aggregate  (cost=78722.93..78722.94 rows=1 width=32) (actual time=446.000..446.000 rows=1 loops=3)
                ->  Parallel Seq Scan on t  (cost=0.00..52681.30 rows=2083330 width=18) (actual time=0.023..104.779 rows=1666667 loops=3)
  Planning Time: 0.044 ms
  Execution Time: 456.460 ms
(8 rows)

=# EXPLAIN ANALYZE
    SELECT sum(JSON_VALUE(js, '$' RETURNING numeric))
    FROM test_parallel_json_value;
                                                      QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
  Aggregate  (cost=144347.82..144347.83 rows=1 width=32) (actual time=1381.938..1381.938 rows=1 loops=1)
    ->  Seq Scan on t  (cost=0.00..81847.92 rows=4999992 width=18) (actual time=0.076..309.676 rows=5000000 loops=1)
  Planning Time: 0.082 ms
  Execution Time: 1384.133 ms
(4 rows)


-- 
Nikita Glukhov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company

Attachments:

  [text/x-patch] 0010-add-invisible-coercion-form-v15.patch (6.7K, ../e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru/2-0010-add-invisible-coercion-form-v15.patch)
  download | inline diff:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index d272719..62f9d8f 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -2583,7 +2583,8 @@ deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context)
 	 * If the function call came from an implicit coercion, then just show the
 	 * first argument.
 	 */
-	if (node->funcformat == COERCE_IMPLICIT_CAST)
+	if (node->funcformat == COERCE_IMPLICIT_CAST ||
+		node->funcformat == COERCE_INTERNAL_CAST)
 	{
 		deparseExpr((Expr *) linitial(node->args), context);
 		return;
@@ -2780,7 +2781,8 @@ static void
 deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
 {
 	deparseExpr(node->arg, context);
-	if (node->relabelformat != COERCE_IMPLICIT_CAST)
+	if (node->relabelformat != COERCE_IMPLICIT_CAST &&
+		node->relabelformat == COERCE_INTERNAL_CAST)
 		appendStringInfo(context->buf, "::%s",
 						 deparse_type_name(node->resulttype,
 										   node->resulttypmod));
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 065238b..6bc78e8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7496,8 +7496,10 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 						CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
 
 						if (type == COERCE_EXPLICIT_CAST ||
-							type == COERCE_IMPLICIT_CAST)
+							type == COERCE_IMPLICIT_CAST ||
+							type == COERCE_INTERNAL_CAST)
 							return false;
+
 						return true;	/* own parentheses */
 					}
 				case T_BoolExpr:	/* lower precedence */
@@ -7547,7 +7549,8 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 						CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
 
 						if (type == COERCE_EXPLICIT_CAST ||
-							type == COERCE_IMPLICIT_CAST)
+							type == COERCE_IMPLICIT_CAST ||
+							type == COERCE_INTERNAL_CAST)
 							return false;
 						return true;	/* own parentheses */
 					}
@@ -7672,6 +7675,25 @@ get_rule_expr_paren(Node *node, deparse_context *context,
 }
 
 
+/*
+ * get_coercion				- Parse back a coercion
+ */
+static void
+get_coercion(Expr *arg, deparse_context *context, bool showimplicit,
+			 Node *node, CoercionForm format, Oid typid, int32 typmod)
+{
+	if (format == COERCE_INTERNAL_CAST ||
+		(format == COERCE_IMPLICIT_CAST && !showimplicit))
+	{
+		/* don't show the implicit cast */
+		get_rule_expr_paren((Node *) arg, context, false, node);
+	}
+	else
+	{
+		get_coercion_expr((Node *) arg, context, typid, typmod, node);
+	}
+}
+
 /* ----------
  * get_rule_expr			- Parse back an expression
  *
@@ -8052,83 +8074,38 @@ get_rule_expr(Node *node, deparse_context *context,
 		case T_RelabelType:
 			{
 				RelabelType *relabel = (RelabelType *) node;
-				Node	   *arg = (Node *) relabel->arg;
 
-				if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
-					!showimplicit)
-				{
-					/* don't show the implicit cast */
-					get_rule_expr_paren(arg, context, false, node);
-				}
-				else
-				{
-					get_coercion_expr(arg, context,
-									  relabel->resulttype,
-									  relabel->resulttypmod,
-									  node);
-				}
+				get_coercion(relabel->arg, context, showimplicit, node,
+							 relabel->relabelformat, relabel->resulttype,
+							 relabel->resulttypmod);
 			}
 			break;
 
 		case T_CoerceViaIO:
 			{
 				CoerceViaIO *iocoerce = (CoerceViaIO *) node;
-				Node	   *arg = (Node *) iocoerce->arg;
 
-				if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
-					!showimplicit)
-				{
-					/* don't show the implicit cast */
-					get_rule_expr_paren(arg, context, false, node);
-				}
-				else
-				{
-					get_coercion_expr(arg, context,
-									  iocoerce->resulttype,
-									  -1,
-									  node);
-				}
+				get_coercion(iocoerce->arg, context, showimplicit, node,
+							 iocoerce->coerceformat, iocoerce->resulttype, -1);
 			}
 			break;
 
 		case T_ArrayCoerceExpr:
 			{
 				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
-				Node	   *arg = (Node *) acoerce->arg;
 
-				if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
-					!showimplicit)
-				{
-					/* don't show the implicit cast */
-					get_rule_expr_paren(arg, context, false, node);
-				}
-				else
-				{
-					get_coercion_expr(arg, context,
-									  acoerce->resulttype,
-									  acoerce->resulttypmod,
-									  node);
-				}
+				get_coercion(acoerce->arg, context, showimplicit, node,
+							 acoerce->coerceformat, acoerce->resulttype,
+							 acoerce->resulttypmod);
 			}
 			break;
 
 		case T_ConvertRowtypeExpr:
 			{
 				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
-				Node	   *arg = (Node *) convert->arg;
 
-				if (convert->convertformat == COERCE_IMPLICIT_CAST &&
-					!showimplicit)
-				{
-					/* don't show the implicit cast */
-					get_rule_expr_paren(arg, context, false, node);
-				}
-				else
-				{
-					get_coercion_expr(arg, context,
-									  convert->resulttype, -1,
-									  node);
-				}
+				get_coercion(convert->arg, context, showimplicit, node,
+							 convert->convertformat, convert->resulttype, -1);
 			}
 			break;
 
@@ -8681,21 +8658,10 @@ get_rule_expr(Node *node, deparse_context *context,
 		case T_CoerceToDomain:
 			{
 				CoerceToDomain *ctest = (CoerceToDomain *) node;
-				Node	   *arg = (Node *) ctest->arg;
 
-				if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
-					!showimplicit)
-				{
-					/* don't show the implicit cast */
-					get_rule_expr(arg, context, false);
-				}
-				else
-				{
-					get_coercion_expr(arg, context,
-									  ctest->resulttype,
-									  ctest->resulttypmod,
-									  node);
-				}
+				get_coercion(ctest->arg, context, showimplicit, node,
+							 ctest->coercionformat, ctest->resulttype,
+							 ctest->resulttypmod);
 			}
 			break;
 
@@ -9027,7 +8993,8 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 	 * If the function call came from an implicit coercion, then just show the
 	 * first argument --- unless caller wants to see implicit coercions.
 	 */
-	if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
+	if (expr->funcformat == COERCE_INTERNAL_CAST ||
+		(expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit))
 	{
 		get_rule_expr_paren((Node *) linitial(expr->args), context,
 							false, (Node *) expr);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 1b4b0d7..41330b2 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -437,7 +437,8 @@ typedef enum CoercionForm
 {
 	COERCE_EXPLICIT_CALL,		/* display as a function call */
 	COERCE_EXPLICIT_CAST,		/* display as an explicit cast */
-	COERCE_IMPLICIT_CAST		/* implicit cast, so hide it */
+	COERCE_IMPLICIT_CAST,		/* implicit cast, so hide it */
+	COERCE_INTERNAL_CAST		/* internal cast, so hide it always */
 } CoercionForm;
 
 /*

  [text/x-patch] 0011-add-function-formats-v15.patch (10.1K, ../e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru/3-0011-add-function-formats-v15.patch)
  download | inline diff:
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index cc9efab..758b86f 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2481,11 +2481,13 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
 				Aggref	   *expr = (Aggref *) node;
 
 				APP_JUMB(expr->aggfnoid);
+				APP_JUMB(expr->aggformat);
 				JumbleExpr(jstate, (Node *) expr->aggdirectargs);
 				JumbleExpr(jstate, (Node *) expr->args);
 				JumbleExpr(jstate, (Node *) expr->aggorder);
 				JumbleExpr(jstate, (Node *) expr->aggdistinct);
 				JumbleExpr(jstate, (Node *) expr->aggfilter);
+				JumbleExpr(jstate, (Node *) expr->aggformatopts);
 			}
 			break;
 		case T_GroupingFunc:
@@ -2501,8 +2503,10 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
 
 				APP_JUMB(expr->winfnoid);
 				APP_JUMB(expr->winref);
+				APP_JUMB(expr->winformat);
 				JumbleExpr(jstate, (Node *) expr->args);
 				JumbleExpr(jstate, (Node *) expr->aggfilter);
+				JumbleExpr(jstate, (Node *) expr->winformatopts);
 			}
 			break;
 		case T_ArrayRef:
@@ -2520,7 +2524,9 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
 				FuncExpr   *expr = (FuncExpr *) node;
 
 				APP_JUMB(expr->funcid);
+				APP_JUMB(expr->funcformat2);
 				JumbleExpr(jstate, (Node *) expr->args);
+				JumbleExpr(jstate, (Node *) expr->funcformatopts);
 			}
 			break;
 		case T_NamedArgExpr:
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1c12075..f05adf4 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1432,6 +1432,8 @@ _copyAggref(const Aggref *from)
 	COPY_SCALAR_FIELD(aggkind);
 	COPY_SCALAR_FIELD(agglevelsup);
 	COPY_SCALAR_FIELD(aggsplit);
+	COPY_SCALAR_FIELD(aggformat);
+	COPY_NODE_FIELD(aggformatopts);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
@@ -1471,6 +1473,8 @@ _copyWindowFunc(const WindowFunc *from)
 	COPY_SCALAR_FIELD(winref);
 	COPY_SCALAR_FIELD(winstar);
 	COPY_SCALAR_FIELD(winagg);
+	COPY_SCALAR_FIELD(winformat);
+	COPY_NODE_FIELD(winformatopts);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
@@ -1512,6 +1516,8 @@ _copyFuncExpr(const FuncExpr *from)
 	COPY_SCALAR_FIELD(funccollid);
 	COPY_SCALAR_FIELD(inputcollid);
 	COPY_NODE_FIELD(args);
+	COPY_SCALAR_FIELD(funcformat2);
+	COPY_NODE_FIELD(funcformatopts);
 	COPY_LOCATION_FIELD(location);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6a971d0..35c13a5 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -226,6 +226,8 @@ _equalAggref(const Aggref *a, const Aggref *b)
 	COMPARE_SCALAR_FIELD(aggkind);
 	COMPARE_SCALAR_FIELD(agglevelsup);
 	COMPARE_SCALAR_FIELD(aggsplit);
+	COMPARE_SCALAR_FIELD(aggformat);
+	COMPARE_NODE_FIELD(aggformatopts);
 	COMPARE_LOCATION_FIELD(location);
 
 	return true;
@@ -258,6 +260,8 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
 	COMPARE_SCALAR_FIELD(winref);
 	COMPARE_SCALAR_FIELD(winstar);
 	COMPARE_SCALAR_FIELD(winagg);
+	COMPARE_SCALAR_FIELD(winformat);
+	COMPARE_NODE_FIELD(winformatopts);
 	COMPARE_LOCATION_FIELD(location);
 
 	return true;
@@ -289,6 +293,8 @@ _equalFuncExpr(const FuncExpr *a, const FuncExpr *b)
 	COMPARE_SCALAR_FIELD(funccollid);
 	COMPARE_SCALAR_FIELD(inputcollid);
 	COMPARE_NODE_FIELD(args);
+	COMPARE_SCALAR_FIELD(funcformat2);
+	COMPARE_NODE_FIELD(funcformatopts);
 	COMPARE_LOCATION_FIELD(location);
 
 	return true;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 979d523..1b4e0ed 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1207,6 +1207,8 @@ _outAggref(StringInfo str, const Aggref *node)
 	WRITE_CHAR_FIELD(aggkind);
 	WRITE_UINT_FIELD(agglevelsup);
 	WRITE_ENUM_FIELD(aggsplit, AggSplit);
+	WRITE_ENUM_FIELD(aggformat, FuncFormat);
+	WRITE_NODE_FIELD(aggformatopts);
 	WRITE_LOCATION_FIELD(location);
 }
 
@@ -1236,6 +1238,8 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
 	WRITE_UINT_FIELD(winref);
 	WRITE_BOOL_FIELD(winstar);
 	WRITE_BOOL_FIELD(winagg);
+	WRITE_ENUM_FIELD(winformat, FuncFormat);
+	WRITE_NODE_FIELD(winformatopts);
 	WRITE_LOCATION_FIELD(location);
 }
 
@@ -1267,6 +1271,8 @@ _outFuncExpr(StringInfo str, const FuncExpr *node)
 	WRITE_OID_FIELD(funccollid);
 	WRITE_OID_FIELD(inputcollid);
 	WRITE_NODE_FIELD(args);
+	WRITE_ENUM_FIELD(funcformat2, FuncFormat);
+	WRITE_NODE_FIELD(funcformatopts);
 	WRITE_LOCATION_FIELD(location);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 42aff7f..c59bef3 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -600,6 +600,8 @@ _readAggref(void)
 	READ_CHAR_FIELD(aggkind);
 	READ_UINT_FIELD(agglevelsup);
 	READ_ENUM_FIELD(aggsplit, AggSplit);
+	READ_ENUM_FIELD(aggformat, FuncFormat);
+	READ_NODE_FIELD(aggformatopts);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -639,6 +641,8 @@ _readWindowFunc(void)
 	READ_UINT_FIELD(winref);
 	READ_BOOL_FIELD(winstar);
 	READ_BOOL_FIELD(winagg);
+	READ_ENUM_FIELD(winformat, FuncFormat);
+	READ_NODE_FIELD(winformatopts);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
@@ -680,6 +684,8 @@ _readFuncExpr(void)
 	READ_OID_FIELD(funccollid);
 	READ_OID_FIELD(inputcollid);
 	READ_NODE_FIELD(args);
+	READ_ENUM_FIELD(funcformat2, FuncFormat);
+	READ_NODE_FIELD(funcformatopts);
 	READ_LOCATION_FIELD(location);
 
 	READ_DONE();
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 505ae0a..69f49a5 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2653,6 +2653,8 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->winref = expr->winref;
 				newexpr->winstar = expr->winstar;
 				newexpr->winagg = expr->winagg;
+				newexpr->winformat = expr->winformat;
+				newexpr->winformatopts = copyObject(expr->winformatopts);
 				newexpr->location = expr->location;
 
 				return (Node *) newexpr;
@@ -2699,6 +2701,8 @@ eval_const_expressions_mutator(Node *node,
 				newexpr->funccollid = expr->funccollid;
 				newexpr->inputcollid = expr->inputcollid;
 				newexpr->args = args;
+				newexpr->funcformat2 = expr->funcformat2;
+				newexpr->funcformatopts = copyObject(expr->funcformatopts);
 				newexpr->location = expr->location;
 				return (Node *) newexpr;
 			}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 6bc78e8..ad4e315 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -8974,6 +8974,16 @@ get_oper_expr(OpExpr *expr, deparse_context *context)
 		appendStringInfoChar(buf, ')');
 }
 
+static void
+get_func_opts(FuncFormat aggformat, Node *aggformatopts, deparse_context *context)
+{
+	switch (aggformat)
+	{
+		default:
+			break;
+	}
+}
+
 /*
  * get_func_expr			- Parse back a FuncExpr node
  */
@@ -8988,6 +8998,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 	List	   *argnames;
 	bool		use_variadic;
 	ListCell   *l;
+	const char *funcname;
 
 	/*
 	 * If the function call came from an implicit coercion, then just show the
@@ -9042,12 +9053,19 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 		nargs++;
 	}
 
-	appendStringInfo(buf, "%s(",
-					 generate_function_name(funcoid, nargs,
-											argnames, argtypes,
-											expr->funcvariadic,
-											&use_variadic,
-											context->special_exprkind));
+	switch (expr->funcformat2)
+	{
+		default:
+			funcname = generate_function_name(funcoid, nargs,
+											  argnames, argtypes,
+											  expr->funcvariadic,
+											  &use_variadic,
+											  context->special_exprkind);
+			break;
+	}
+
+	appendStringInfo(buf, "%s(", funcname);
+
 	nargs = 0;
 	foreach(l, expr->args)
 	{
@@ -9057,6 +9075,9 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 			appendStringInfoString(buf, "VARIADIC ");
 		get_rule_expr((Node *) lfirst(l), context, true);
 	}
+
+	get_func_opts(expr->funcformat2, expr->funcformatopts, context);
+
 	appendStringInfoChar(buf, ')');
 }
 
@@ -9155,6 +9176,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
 		}
 	}
 
+	get_func_opts(aggref->aggformat, aggref->aggformatopts, context);
+
 	if (aggref->aggfilter != NULL)
 	{
 		appendStringInfoString(buf, ") FILTER (WHERE ");
@@ -9221,6 +9244,8 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
 	else
 		get_rule_expr((Node *) wfunc->args, context, true);
 
+	get_func_opts(wfunc->winformat, wfunc->winformatopts, context);
+
 	if (wfunc->aggfilter != NULL)
 	{
 		appendStringInfoString(buf, ") FILTER (WHERE ");
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 41330b2..641500e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -249,6 +249,11 @@ typedef struct Param
 	int			location;		/* token location, or -1 if unknown */
 } Param;
 
+typedef enum FuncFormat
+{
+	FUNCFMT_REGULAR = 0,
+} FuncFormat;
+
 /*
  * Aggref
  *
@@ -308,6 +313,8 @@ typedef struct Aggref
 	char		aggkind;		/* aggregate kind (see pg_aggregate.h) */
 	Index		agglevelsup;	/* > 0 if agg belongs to outer query */
 	AggSplit	aggsplit;		/* expected agg-splitting mode of parent Agg */
+	FuncFormat	aggformat;		/* how to display this aggregate */
+	Node	   *aggformatopts;	/* display options, if any */
 	int			location;		/* token location, or -1 if unknown */
 } Aggref;
 
@@ -361,6 +368,8 @@ typedef struct WindowFunc
 	Index		winref;			/* index of associated WindowClause */
 	bool		winstar;		/* true if argument list was really '*' */
 	bool		winagg;			/* is function a simple aggregate? */
+	FuncFormat	winformat;		/* how to display this window function */
+	Node	   *winformatopts;	/* display options, if any */
 	int			location;		/* token location, or -1 if unknown */
 } WindowFunc;
 
@@ -456,6 +465,8 @@ typedef struct FuncExpr
 	Oid			funccollid;		/* OID of collation of result */
 	Oid			inputcollid;	/* OID of collation that function should use */
 	List	   *args;			/* arguments to the function */
+	FuncFormat	funcformat2;	/* how to display this function call */
+	Node	   *funcformatopts;	/* display options, if any */
 	int			location;		/* token location, or -1 if unknown */
 } FuncExpr;
 

  [text/x-patch] 0012-sqljson-v15.patch (295.6K, ../e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru/4-0012-sqljson-v15.patch)
  download | inline diff:
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 758b86f..1f0e581 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2812,6 +2812,47 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
 				JumbleExpr(jstate, (Node *) conf->exclRelTlist);
 			}
 			break;
+		case T_JsonValueExpr:
+			{
+				JsonValueExpr *expr = (JsonValueExpr *) node;
+
+				JumbleExpr(jstate, (Node *) expr->expr);
+				APP_JUMB(expr->format.type);
+				APP_JUMB(expr->format.encoding);
+			}
+			break;
+		case T_JsonCtorOpts:
+			{
+				JsonCtorOpts *opts = (JsonCtorOpts *) node;
+
+				APP_JUMB(opts->returning.format.type);
+				APP_JUMB(opts->returning.format.encoding);
+				APP_JUMB(opts->returning.typid);
+				APP_JUMB(opts->returning.typmod);
+				APP_JUMB(opts->unique);
+				APP_JUMB(opts->absent_on_null);
+			}
+			break;
+		case T_JsonIsPredicateOpts:
+			{
+				JsonIsPredicateOpts *opts = (JsonIsPredicateOpts *) node;
+
+				APP_JUMB(opts->unique_keys);
+				APP_JUMB(opts->value_type);
+			}
+			break;
+		case T_JsonExpr:
+			{
+				JsonExpr    *jexpr = (JsonExpr *) node;
+
+				APP_JUMB(jexpr->op);
+				JumbleExpr(jstate, jexpr->raw_expr);
+				JumbleExpr(jstate, jexpr->path_spec);
+				JumbleExpr(jstate, (Node *) jexpr->passing.values);
+				JumbleExpr(jstate, jexpr->on_empty.default_expr);
+				JumbleExpr(jstate, jexpr->on_error.default_expr);
+			}
+			break;
 		case T_List:
 			foreach(temp, (List *) node)
 			{
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e284fd7..5b84312 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -45,6 +45,7 @@
 #include "pgstat.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/jsonpath.h"
 #include "utils/lsyscache.h"
 #include "utils/typcache.h"
 
@@ -80,6 +81,40 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
 					  int transno, int setno, int setoff, bool ishash);
 
 
+static ExprState *
+ExecInitExprInternal(Expr *node, PlanState *parent, ParamListInfo ext_params,
+					 Datum *caseval, bool *casenull)
+{
+	ExprState  *state;
+	ExprEvalStep scratch = {0};
+
+	/* Special case: NULL expression produces a NULL ExprState pointer */
+	if (node == NULL)
+		return NULL;
+
+	/* Initialize ExprState with empty step list */
+	state = makeNode(ExprState);
+	state->expr = node;
+	state->parent = parent;
+	state->ext_params = ext_params;
+	state->innermost_caseval = caseval;
+	state->innermost_casenull = casenull;
+
+	/* Insert EEOP_*_FETCHSOME steps as needed */
+	ExecInitExprSlots(state, (Node *) node);
+
+	/* Compile the expression proper */
+	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+
+	/* Finally, append a DONE step */
+	scratch.opcode = EEOP_DONE;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecReadyExpr(state);
+
+	return state;
+}
+
 /*
  * ExecInitExpr: prepare an expression tree for execution
  *
@@ -118,32 +153,7 @@ static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
 ExprState *
 ExecInitExpr(Expr *node, PlanState *parent)
 {
-	ExprState  *state;
-	ExprEvalStep scratch = {0};
-
-	/* Special case: NULL expression produces a NULL ExprState pointer */
-	if (node == NULL)
-		return NULL;
-
-	/* Initialize ExprState with empty step list */
-	state = makeNode(ExprState);
-	state->expr = node;
-	state->parent = parent;
-	state->ext_params = NULL;
-
-	/* Insert EEOP_*_FETCHSOME steps as needed */
-	ExecInitExprSlots(state, (Node *) node);
-
-	/* Compile the expression proper */
-	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
-
-	/* Finally, append a DONE step */
-	scratch.opcode = EEOP_DONE;
-	ExprEvalPushStep(state, &scratch);
-
-	ExecReadyExpr(state);
-
-	return state;
+	return ExecInitExprInternal(node, parent, NULL, NULL, NULL);
 }
 
 /*
@@ -155,32 +165,20 @@ ExecInitExpr(Expr *node, PlanState *parent)
 ExprState *
 ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
 {
-	ExprState  *state;
-	ExprEvalStep scratch = {0};
-
-	/* Special case: NULL expression produces a NULL ExprState pointer */
-	if (node == NULL)
-		return NULL;
-
-	/* Initialize ExprState with empty step list */
-	state = makeNode(ExprState);
-	state->expr = node;
-	state->parent = NULL;
-	state->ext_params = ext_params;
-
-	/* Insert EEOP_*_FETCHSOME steps as needed */
-	ExecInitExprSlots(state, (Node *) node);
-
-	/* Compile the expression proper */
-	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
-
-	/* Finally, append a DONE step */
-	scratch.opcode = EEOP_DONE;
-	ExprEvalPushStep(state, &scratch);
-
-	ExecReadyExpr(state);
+	return ExecInitExprInternal(node, NULL, ext_params, NULL, NULL);
+}
 
-	return state;
+/*
+ * ExecInitExprWithCaseValue: prepare an expression tree for execution
+ *
+ * This is the same as ExecInitExpr, except that a pointer to the value for
+ * CasTestExpr is passed here.
+ */
+ExprState *
+ExecInitExprWithCaseValue(Expr *node, PlanState *parent,
+						  Datum *caseval, bool *casenull)
+{
+	return ExecInitExprInternal(node, parent, NULL, caseval, casenull);
 }
 
 /*
@@ -2113,6 +2111,126 @@ ExecInitExprRec(Expr *node, ExprState *state,
 				break;
 			}
 
+		case T_JsonValueExpr:
+			ExecInitExprRec(((JsonValueExpr *) node)->expr, state, resv,
+							resnull);
+			break;
+
+		case T_JsonExpr:
+			{
+				JsonExpr   *jexpr = castNode(JsonExpr, node);
+				ListCell   *argexprlc;
+				ListCell   *argnamelc;
+
+				scratch.opcode = EEOP_JSONEXPR;
+				scratch.d.jsonexpr.jsexpr = jexpr;
+
+				scratch.d.jsonexpr.raw_expr =
+					palloc(sizeof(*scratch.d.jsonexpr.raw_expr));
+
+				ExecInitExprRec((Expr *) jexpr->raw_expr, state,
+								&scratch.d.jsonexpr.raw_expr->value,
+								&scratch.d.jsonexpr.raw_expr->isnull);
+
+				scratch.d.jsonexpr.pathspec =
+					palloc(sizeof(*scratch.d.jsonexpr.pathspec));
+
+				ExecInitExprRec((Expr *) jexpr->path_spec, state,
+								&scratch.d.jsonexpr.pathspec->value,
+								&scratch.d.jsonexpr.pathspec->isnull);
+
+				scratch.d.jsonexpr.formatted_expr =
+					ExecInitExprWithCaseValue((Expr *) jexpr->formatted_expr,
+											  state->parent,
+											  &scratch.d.jsonexpr.raw_expr->value,
+											  &scratch.d.jsonexpr.raw_expr->isnull);
+
+				scratch.d.jsonexpr.res_expr =
+					palloc(sizeof(*scratch.d.jsonexpr.res_expr));
+
+
+				scratch.d.jsonexpr.result_expr = jexpr->result_coercion
+					? ExecInitExprWithCaseValue((Expr *) jexpr->result_coercion->expr,
+												state->parent,
+												&scratch.d.jsonexpr.res_expr->value,
+												&scratch.d.jsonexpr.res_expr->isnull)
+					: NULL;
+
+				scratch.d.jsonexpr.default_on_empty =
+					ExecInitExpr((Expr *) jexpr->on_empty.default_expr,
+								 state->parent);
+
+				scratch.d.jsonexpr.default_on_error =
+					ExecInitExpr((Expr *) jexpr->on_error.default_expr,
+								 state->parent);
+
+				if (jexpr->omit_quotes ||
+					(jexpr->result_coercion && jexpr->result_coercion->via_io))
+				{
+					Oid			typinput;
+
+					/* lookup the result type's input function */
+					getTypeInputInfo(jexpr->returning.typid, &typinput,
+									 &scratch.d.jsonexpr.input.typioparam);
+					fmgr_info(typinput, &scratch.d.jsonexpr.input.func);
+				}
+
+				scratch.d.jsonexpr.args = NIL;
+
+				forboth(argexprlc, jexpr->passing.values,
+						argnamelc, jexpr->passing.names)
+				{
+					Expr	   *argexpr = (Expr *) lfirst(argexprlc);
+					Value	   *argname = (Value *) lfirst(argnamelc);
+					JsonPathVariableEvalContext *var = palloc(sizeof(*var));
+
+					var->var.varName = cstring_to_text(argname->val.str);
+					var->var.typid = exprType((Node *) argexpr);
+					var->var.typmod = exprTypmod((Node *) argexpr);
+					var->var.cb = EvalJsonPathVar;
+					var->var.cb_arg = var;
+					var->estate = ExecInitExpr(argexpr, state->parent);
+					var->econtext = NULL;
+					var->evaluated = false;
+					var->value = (Datum) 0;
+					var->isnull = true;
+
+					scratch.d.jsonexpr.args =
+						lappend(scratch.d.jsonexpr.args, var);
+				}
+
+				scratch.d.jsonexpr.cache = NULL;
+
+				if (jexpr->coercions)
+				{
+					JsonCoercion **coercion;
+					struct JsonCoercionState *cstate;
+					Datum	   *caseval;
+					bool	   *casenull;
+
+					scratch.d.jsonexpr.coercion_expr =
+						palloc(sizeof(*scratch.d.jsonexpr.coercion_expr));
+
+					caseval = &scratch.d.jsonexpr.coercion_expr->value;
+					casenull = &scratch.d.jsonexpr.coercion_expr->isnull;
+
+					for (cstate = &scratch.d.jsonexpr.coercions.null,
+						 coercion = &jexpr->coercions->null;
+						 coercion <= &jexpr->coercions->composite;
+						 coercion++, cstate++)
+					{
+						cstate->coercion = *coercion;
+						cstate->estate = *coercion ?
+							ExecInitExprWithCaseValue((Expr *)(*coercion)->expr,
+													  state->parent,
+													  caseval, casenull) : NULL;
+					}
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9d6e25a..c619a56 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -57,6 +57,8 @@
 #include "postgres.h"
 
 #include "access/tuptoaster.h"
+#include "access/xact.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/sequence.h"
 #include "executor/execExpr.h"
@@ -64,14 +66,20 @@
 #include "funcapi.h"
 #include "utils/memutils.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
+#include "parser/parse_expr.h"
 #include "pgstat.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/datum.h"
 #include "utils/expandedrecord.h"
+#include "utils/jsonapi.h"
+#include "utils/jsonb.h"
+#include "utils/jsonpath.h"
 #include "utils/lsyscache.h"
+#include "utils/resowner.h"
 #include "utils/timestamp.h"
 #include "utils/typcache.h"
 #include "utils/xml.h"
@@ -384,6 +392,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		&&CASE_EEOP_WINDOW_FUNC,
 		&&CASE_EEOP_SUBPLAN,
 		&&CASE_EEOP_ALTERNATIVE_SUBPLAN,
+		&&CASE_EEOP_JSONEXPR,
 		&&CASE_EEOP_AGG_STRICT_DESERIALIZE,
 		&&CASE_EEOP_AGG_DESERIALIZE,
 		&&CASE_EEOP_AGG_STRICT_INPUT_CHECK,
@@ -1748,7 +1757,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
 		{
 			/* too complex for an inline implementation */
 			ExecEvalAggOrderedTransTuple(state, op, econtext);
+			EEO_NEXT();
+		}
 
+		EEO_CASE(EEOP_JSONEXPR)
+		{
+			/* too complex for an inline implementation */
+			ExecEvalJson(state, op, econtext);
 			EEO_NEXT();
 		}
 
@@ -4125,3 +4140,392 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
 	ExecStoreVirtualTuple(pertrans->sortslot);
 	tuplesort_puttupleslot(pertrans->sortstates[setno], pertrans->sortslot);
 }
+
+/*
+ * Evaluate a JSON error/empty behavior result.
+ */
+static Datum
+ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
+					 ExprState *default_estate, bool *is_null)
+{
+	*is_null = false;
+
+	switch (behavior->btype)
+	{
+		case JSON_BEHAVIOR_EMPTY_ARRAY:
+			return JsonbPGetDatum(JsonbMakeEmptyArray());
+
+		case JSON_BEHAVIOR_EMPTY_OBJECT:
+			return JsonbPGetDatum(JsonbMakeEmptyObject());
+
+		case JSON_BEHAVIOR_TRUE:
+			return BoolGetDatum(true);
+
+		case JSON_BEHAVIOR_FALSE:
+			return BoolGetDatum(false);
+
+		case JSON_BEHAVIOR_NULL:
+		case JSON_BEHAVIOR_UNKNOWN:
+			*is_null = true;
+			return (Datum) 0;
+
+		case JSON_BEHAVIOR_DEFAULT:
+			return ExecEvalExpr(default_estate, econtext, is_null);
+
+		default:
+			elog(ERROR, "unrecognized SQL/JSON behavior %d", behavior->btype);
+			return (Datum) 0;
+	}
+}
+
+/*
+ * Evaluate a coercion of a JSON item to the target type.
+ */
+static Datum
+ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
+						 Datum res, bool *isNull)
+{
+	JsonExpr   *jexpr = op->d.jsonexpr.jsexpr;
+	JsonCoercion *coercion = jexpr->result_coercion;
+	Jsonb	   *jb = *isNull ? NULL : DatumGetJsonbP(res);
+
+	if ((coercion && coercion->via_io) ||
+		(jexpr->omit_quotes && !*isNull && JB_ROOT_IS_SCALAR(jb)))
+	{
+		/* strip quotes and call typinput function */
+		char *str = *isNull ? NULL : JsonbUnquote(jb);
+
+		res = InputFunctionCall(&op->d.jsonexpr.input.func, str,
+								op->d.jsonexpr.input.typioparam,
+								jexpr->returning.typmod);
+	}
+	else if (op->d.jsonexpr.result_expr)
+	{
+		op->d.jsonexpr.res_expr->value = res;
+		op->d.jsonexpr.res_expr->isnull = *isNull;
+
+		res = ExecEvalExpr(op->d.jsonexpr.result_expr, econtext, isNull);
+	}
+	else if (coercion && coercion->via_populate)
+		res = json_populate_type(res, JSONBOID,
+								 jexpr->returning.typid,
+								 jexpr->returning.typmod,
+								 &op->d.jsonexpr.cache,
+								 econtext->ecxt_per_query_memory,
+								 isNull);
+	/* else no coercion, simply return item */
+
+	return res;
+}
+
+/*
+ * Evaluate a JSON path variable caching computed value.
+ */
+Datum
+EvalJsonPathVar(void *cxt, bool *isnull)
+{
+	JsonPathVariableEvalContext *ecxt = cxt;
+
+	if (!ecxt->evaluated)
+	{
+		ecxt->value = ExecEvalExpr(ecxt->estate, ecxt->econtext, &ecxt->isnull);
+		ecxt->evaluated = true;
+	}
+
+	*isnull = ecxt->isnull;
+	return ecxt->value;
+}
+
+/*
+ * Prepare SQL/JSON item coercion to the output type. Returned a datum of the
+ * corresponding SQL type and a pointer to the coercion state.
+ */
+Datum
+ExecPrepareJsonItemCoercion(JsonbValue *item,
+							JsonReturning *returning,
+							struct JsonCoercionsState *coercions,
+							struct JsonCoercionState **pcoercion)
+{
+	struct JsonCoercionState *coercion;
+	Datum		res;
+	JsonbValue	jbvbuf;
+
+	if (item->type == jbvBinary && JsonContainerIsScalar(item->val.binary.data))
+		item = JsonbExtractScalar(item->val.binary.data, &jbvbuf);
+
+	/* get coercion state reference and datum of the corresponding SQL type */
+	switch (item->type)
+	{
+		case jbvNull:
+			coercion = &coercions->null;
+			res = (Datum) 0;
+			break;
+
+		case jbvString:
+			coercion = &coercions->string;
+			res = PointerGetDatum(
+				cstring_to_text_with_len(item->val.string.val,
+										 item->val.string.len));
+			break;
+
+		case jbvNumeric:
+			coercion = &coercions->numeric;
+			res = NumericGetDatum(item->val.numeric);
+			break;
+
+		case jbvBool:
+			coercion = &coercions->boolean;
+			res = BoolGetDatum(item->val.boolean);
+			break;
+
+		case jbvDatetime:
+			res = item->val.datetime.value;
+			switch (item->val.datetime.typid)
+			{
+				case DATEOID:
+					coercion = &coercions->date;
+					break;
+				case TIMEOID:
+					coercion = &coercions->time;
+					break;
+				case TIMETZOID:
+					coercion = &coercions->timetz;
+					break;
+				case TIMESTAMPOID:
+					coercion = &coercions->timestamp;
+					break;
+				case TIMESTAMPTZOID:
+					coercion = &coercions->timestamptz;
+					break;
+				default:
+					elog(ERROR, "unexpected jsonb datetime type oid %d",
+						 item->val.datetime.typid);
+					return (Datum) 0;
+			}
+			break;
+
+		case jbvArray:
+		case jbvObject:
+		case jbvBinary:
+			coercion = &coercions->composite;
+			res = JsonbPGetDatum(JsonbValueToJsonb(item));
+			break;
+
+		default:
+			elog(ERROR, "unexpected jsonb value type %d", item->type);
+			return (Datum) 0;
+	}
+
+	*pcoercion = coercion;
+
+	return res;
+}
+
+static Datum
+ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
+				 JsonExpr *jexpr, JsonPath *path, Datum item, bool *resnull)
+{
+	bool		empty = false;
+	Datum		res = (Datum) 0;
+
+	if (op->d.jsonexpr.formatted_expr)
+	{
+		bool		isnull;
+
+		op->d.jsonexpr.raw_expr->value = item;
+		op->d.jsonexpr.raw_expr->isnull = false;
+
+		item = ExecEvalExpr(op->d.jsonexpr.formatted_expr, econtext, &isnull);
+		if (isnull)
+		{
+			/* execute domain checks for NULLs */
+			(void) ExecEvalJsonExprCoercion(op, econtext, res, resnull);
+			*resnull = true;
+			return (Datum) 0;
+		}
+	}
+
+	switch (jexpr->op)
+	{
+		case IS_JSON_QUERY:
+			res = JsonbPathQuery(item, path, jexpr->wrapper, &empty,
+								 op->d.jsonexpr.args);
+			*resnull = !DatumGetPointer(res);
+			break;
+
+		case IS_JSON_VALUE:
+			{
+				JsonbValue *jbv = JsonbPathValue(item, path, &empty,
+												 op->d.jsonexpr.args);
+				struct JsonCoercionState *jcstate;
+
+				if (!jbv)
+					break;
+
+				*resnull = false;
+
+				res = ExecPrepareJsonItemCoercion(jbv,
+										&op->d.jsonexpr.jsexpr->returning,
+										&op->d.jsonexpr.coercions,
+										&jcstate);
+
+				/* coerce item datum to the output type */
+				if ((jcstate->coercion &&
+					(jcstate->coercion->via_io ||
+					 jcstate->coercion->via_populate)) || /* ignored for scalars jsons */
+					jexpr->returning.typid == JSONOID ||
+					jexpr->returning.typid == JSONBOID)
+				{
+					/* use coercion via I/O from json[b] to the output type */
+					res = JsonbPGetDatum(JsonbValueToJsonb(jbv));
+					res = ExecEvalJsonExprCoercion(op, econtext, res, resnull);
+				}
+				else if (jcstate->estate)
+				{
+					op->d.jsonexpr.coercion_expr->value = res;
+					op->d.jsonexpr.coercion_expr->isnull = false;
+
+					res = ExecEvalExpr(jcstate->estate, econtext, resnull);
+				}
+				/* else no coercion */
+			}
+			break;
+
+		case IS_JSON_EXISTS:
+			res = BoolGetDatum(JsonbPathExists(item, path, op->d.jsonexpr.args));
+			*resnull = false;
+			break;
+
+		default:
+			elog(ERROR, "unrecognized SQL/JSON expression op %d",
+				 jexpr->op);
+			return (Datum) 0;
+	}
+
+	if (empty)
+	{
+		if (jexpr->on_empty.btype == JSON_BEHAVIOR_ERROR)
+			ereport(ERROR,
+					(errcode(ERRCODE_NO_JSON_ITEM),
+					 errmsg("no SQL/JSON item")));
+
+		/* execute ON EMPTY behavior */
+		res = ExecEvalJsonBehavior(econtext, &jexpr->on_empty,
+								   op->d.jsonexpr.default_on_empty, resnull);
+	}
+
+	if (jexpr->op != IS_JSON_EXISTS &&
+		(!empty ? jexpr->op != IS_JSON_VALUE :
+		 /* result is already coerced in DEFAULT behavior case */
+		 jexpr->on_empty.btype != JSON_BEHAVIOR_DEFAULT))
+		res = ExecEvalJsonExprCoercion(op, econtext, res, resnull);
+
+	return res;
+}
+
+bool
+ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr)
+{
+	return jsexpr->on_error.btype != JSON_BEHAVIOR_ERROR;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecEvalJson
+ * ----------------------------------------------------------------
+ */
+void
+ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+	JsonExpr   *jexpr = op->d.jsonexpr.jsexpr;
+	Datum		item;
+	Datum		res = (Datum) 0;
+	JsonPath   *path;
+	ListCell   *lc;
+
+	*op->resnull = true;		/* until we get a result */
+	*op->resvalue = (Datum) 0;
+
+	if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
+	{
+		/* execute domain checks for NULLs */
+		(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
+
+		Assert(*op->resnull);
+		*op->resnull = true;
+
+		return;
+	}
+
+	item = op->d.jsonexpr.raw_expr->value;
+	path = DatumGetJsonPathP(op->d.jsonexpr.pathspec->value);
+
+	/* reset JSON path variable contexts */
+	foreach(lc, op->d.jsonexpr.args)
+	{
+		JsonPathVariableEvalContext *var = lfirst(lc);
+
+		var->econtext = econtext;
+		var->evaluated = false;
+	}
+
+	if (!ExecEvalJsonNeedsSubTransaction(jexpr))
+	{
+		/* No need to use PG_TRY/PG_CATCH with subtransactions. */
+		res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
+							   op->resnull);
+	}
+	else
+	{
+		/*
+		 * We should catch exceptions of category ERRCODE_DATA_EXCEPTION and
+		 * execute corresponding ON ERROR behavior.
+		 */
+		MemoryContext oldcontext = CurrentMemoryContext;
+		ResourceOwner oldowner = CurrentResourceOwner;
+
+		BeginInternalSubTransaction(NULL);
+		/* Want to execute expressions inside function's memory context */
+		MemoryContextSwitchTo(oldcontext);
+
+		PG_TRY();
+		{
+			res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
+								   op->resnull);
+
+			/* Commit the inner transaction, return to outer xact context */
+			ReleaseCurrentSubTransaction();
+			MemoryContextSwitchTo(oldcontext);
+			CurrentResourceOwner = oldowner;
+		}
+		PG_CATCH();
+		{
+			ErrorData  *edata;
+
+			/* Save error info in oldcontext */
+			MemoryContextSwitchTo(oldcontext);
+			edata = CopyErrorData();
+			FlushErrorState();
+
+			/* Abort the inner transaction */
+			RollbackAndReleaseCurrentSubTransaction();
+			MemoryContextSwitchTo(oldcontext);
+			CurrentResourceOwner = oldowner;
+
+			if (ERRCODE_TO_CATEGORY(edata->sqlerrcode) != ERRCODE_DATA_EXCEPTION)
+				ReThrowError(edata);
+
+			/* Execute ON ERROR behavior. */
+			res = ExecEvalJsonBehavior(econtext, &jexpr->on_error,
+									   op->d.jsonexpr.default_on_error,
+									   op->resnull);
+
+			if (jexpr->op != IS_JSON_EXISTS &&
+				/* result is already coerced in DEFAULT behavior case */
+				jexpr->on_error.btype != JSON_BEHAVIOR_DEFAULT)
+				res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
+		}
+		PG_END_TRY();
+	}
+
+	*op->resvalue = res;
+}
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 36c5f7d..401f569 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2505,6 +2505,13 @@ llvm_compile_expr(ExprState *state)
 				LLVMBuildBr(b, opblocks[i + 1]);
 				break;
 
+
+			case EEOP_JSONEXPR:
+				build_EvalXFunc(b, mod, "ExecEvalJson",
+								v_state, v_econtext, op);
+				LLVMBuildBr(b, opblocks[i + 1]);
+				break;
+
 			case EEOP_LAST:
 				Assert(false);
 				break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f05adf4..8f51a0e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2192,6 +2192,319 @@ _copyOnConflictExpr(const OnConflictExpr *from)
 	return newnode;
 }
 
+/*
+ * _copyJsonValueExpr
+ */
+static JsonValueExpr *
+_copyJsonValueExpr(const JsonValueExpr *from)
+{
+	JsonValueExpr  *newnode = makeNode(JsonValueExpr);
+
+	COPY_NODE_FIELD(expr);
+	COPY_SCALAR_FIELD(format);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonKeyValue
+ */
+static JsonKeyValue *
+_copyJsonKeyValue(const JsonKeyValue *from)
+{
+	JsonKeyValue *newnode = makeNode(JsonKeyValue);
+
+	COPY_NODE_FIELD(key);
+	COPY_NODE_FIELD(value);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonObjectCtor
+ */
+static JsonObjectCtor *
+_copyJsonObjectCtor(const JsonObjectCtor *from)
+{
+	JsonObjectCtor *newnode = makeNode(JsonObjectCtor);
+
+	COPY_NODE_FIELD(exprs);
+	COPY_NODE_FIELD(output);
+	COPY_SCALAR_FIELD(absent_on_null);
+	COPY_SCALAR_FIELD(unique);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonCtorOpts
+ */
+static JsonCtorOpts *
+_copyJsonCtorOpts(const JsonCtorOpts *from)
+{
+	JsonCtorOpts *newnode = makeNode(JsonCtorOpts);
+
+	COPY_SCALAR_FIELD(returning.format.type);
+	COPY_SCALAR_FIELD(returning.format.encoding);
+	COPY_LOCATION_FIELD(returning.format.location);
+	COPY_SCALAR_FIELD(returning.typid);
+	COPY_SCALAR_FIELD(returning.typmod);
+	COPY_SCALAR_FIELD(unique);
+	COPY_SCALAR_FIELD(absent_on_null);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonObjectAgg
+ */
+static JsonObjectAgg *
+_copyJsonObjectAgg(const JsonObjectAgg *from)
+{
+	JsonObjectAgg *newnode = makeNode(JsonObjectAgg);
+
+	COPY_NODE_FIELD(ctor.output);
+	COPY_NODE_FIELD(ctor.agg_filter);
+	COPY_NODE_FIELD(ctor.agg_order);
+	COPY_NODE_FIELD(ctor.over);
+	COPY_LOCATION_FIELD(ctor.location);
+	COPY_NODE_FIELD(arg);
+	COPY_SCALAR_FIELD(absent_on_null);
+	COPY_SCALAR_FIELD(unique);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonOutput
+ */
+static JsonOutput *
+_copyJsonOutput(const JsonOutput *from)
+{
+	JsonOutput	   *newnode = makeNode(JsonOutput);
+
+	COPY_NODE_FIELD(typename);
+	COPY_SCALAR_FIELD(returning);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonArrayCtor
+ */
+static JsonArrayCtor *
+_copyJsonArrayCtor(const JsonArrayCtor *from)
+{
+	JsonArrayCtor *newnode = makeNode(JsonArrayCtor);
+
+	COPY_NODE_FIELD(exprs);
+	COPY_NODE_FIELD(output);
+	COPY_SCALAR_FIELD(absent_on_null);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonArrayAgg
+ */
+static JsonArrayAgg *
+_copyJsonArrayAgg(const JsonArrayAgg *from)
+{
+	JsonArrayAgg *newnode = makeNode(JsonArrayAgg);
+
+	COPY_NODE_FIELD(ctor.output);
+	COPY_NODE_FIELD(ctor.agg_filter);
+	COPY_NODE_FIELD(ctor.agg_order);
+	COPY_NODE_FIELD(ctor.over);
+	COPY_LOCATION_FIELD(ctor.location);
+	COPY_NODE_FIELD(arg);
+	COPY_SCALAR_FIELD(absent_on_null);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonArrayQueryCtor
+ */
+static JsonArrayQueryCtor *
+_copyJsonArrayQueryCtor(const JsonArrayQueryCtor *from)
+{
+	JsonArrayQueryCtor *newnode = makeNode(JsonArrayQueryCtor);
+
+	COPY_NODE_FIELD(query);
+	COPY_NODE_FIELD(output);
+	COPY_SCALAR_FIELD(format);
+	COPY_SCALAR_FIELD(absent_on_null);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonExpr
+ */
+static JsonExpr *
+_copyJsonExpr(const JsonExpr *from)
+{
+	JsonExpr    *newnode = makeNode(JsonExpr);
+
+	COPY_SCALAR_FIELD(op);
+	COPY_NODE_FIELD(raw_expr);
+	COPY_NODE_FIELD(formatted_expr);
+	COPY_NODE_FIELD(result_coercion);
+	COPY_SCALAR_FIELD(format);
+	COPY_NODE_FIELD(path_spec);
+	COPY_NODE_FIELD(passing.values);
+	COPY_NODE_FIELD(passing.names);
+	COPY_SCALAR_FIELD(returning);
+	COPY_SCALAR_FIELD(on_error);
+	COPY_NODE_FIELD(on_error.default_expr);
+	COPY_SCALAR_FIELD(on_empty);
+	COPY_NODE_FIELD(on_empty.default_expr);
+	COPY_NODE_FIELD(coercions);
+	COPY_SCALAR_FIELD(wrapper);
+	COPY_SCALAR_FIELD(omit_quotes);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonCoercion
+ */
+static JsonCoercion *
+_copyJsonCoercion(const JsonCoercion *from)
+{
+	JsonCoercion *newnode = makeNode(JsonCoercion);
+
+	COPY_NODE_FIELD(expr);
+	COPY_SCALAR_FIELD(via_populate);
+	COPY_SCALAR_FIELD(via_io);
+	COPY_SCALAR_FIELD(collation);
+
+	return newnode;
+}
+
+/*
+ * _copylJsonItemCoercions
+ */
+static JsonItemCoercions *
+_copyJsonItemCoercions(const JsonItemCoercions *from)
+{
+	JsonItemCoercions *newnode = makeNode(JsonItemCoercions);
+
+	COPY_NODE_FIELD(null);
+	COPY_NODE_FIELD(string);
+	COPY_NODE_FIELD(numeric);
+	COPY_NODE_FIELD(boolean);
+	COPY_NODE_FIELD(date);
+	COPY_NODE_FIELD(time);
+	COPY_NODE_FIELD(timetz);
+	COPY_NODE_FIELD(timestamp);
+	COPY_NODE_FIELD(timestamptz);
+	COPY_NODE_FIELD(composite);
+
+	return newnode;
+}
+
+
+/*
+ * _copyJsonFuncExpr
+ */
+static JsonFuncExpr *
+_copyJsonFuncExpr(const JsonFuncExpr *from)
+{
+	JsonFuncExpr   *newnode = makeNode(JsonFuncExpr);
+
+	COPY_SCALAR_FIELD(op);
+	COPY_NODE_FIELD(common);
+	COPY_NODE_FIELD(output);
+	COPY_NODE_FIELD(on_empty);
+	COPY_NODE_FIELD(on_error);
+	COPY_SCALAR_FIELD(wrapper);
+	COPY_SCALAR_FIELD(omit_quotes);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonIsPredicate
+ */
+static JsonIsPredicate *
+_copyJsonIsPredicate(const JsonIsPredicate *from)
+{
+	JsonIsPredicate *newnode = makeNode(JsonIsPredicate);
+
+	COPY_NODE_FIELD(expr);
+	COPY_SCALAR_FIELD(format);
+	COPY_SCALAR_FIELD(vtype);
+	COPY_SCALAR_FIELD(unique_keys);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonIsPredicateOpts
+ */
+static JsonIsPredicateOpts *
+_copyJsonIsPredicateOpts(const JsonIsPredicateOpts *from)
+{
+	JsonIsPredicateOpts *newnode = makeNode(JsonIsPredicateOpts);
+
+	COPY_SCALAR_FIELD(value_type);
+	COPY_SCALAR_FIELD(unique_keys);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonBehavior
+ */
+static JsonBehavior *
+_copyJsonBehavior(const JsonBehavior *from)
+{
+	JsonBehavior   *newnode = makeNode(JsonBehavior);
+
+	COPY_SCALAR_FIELD(btype);
+	COPY_NODE_FIELD(default_expr);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonCommon
+ */
+static JsonCommon *
+_copyJsonCommon(const JsonCommon *from)
+{
+	JsonCommon	   *newnode = makeNode(JsonCommon);
+
+	COPY_NODE_FIELD(expr);
+	COPY_NODE_FIELD(pathspec);
+	COPY_STRING_FIELD(pathname);
+	COPY_NODE_FIELD(passing);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
+/*
+ * _copyJsonArgument
+ */
+static JsonArgument *
+_copyJsonArgument(const JsonArgument *from)
+{
+	JsonArgument   *newnode = makeNode(JsonArgument);
+
+	COPY_NODE_FIELD(val);
+	COPY_STRING_FIELD(name);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *						relation.h copy functions
  *
@@ -5079,6 +5392,60 @@ copyObjectImpl(const void *from)
 		case T_OnConflictExpr:
 			retval = _copyOnConflictExpr(from);
 			break;
+		case T_JsonValueExpr:
+			retval = _copyJsonValueExpr(from);
+			break;
+		case T_JsonKeyValue:
+			retval = _copyJsonKeyValue(from);
+			break;
+		case T_JsonCtorOpts:
+			retval = _copyJsonCtorOpts(from);
+			break;
+		case T_JsonObjectCtor:
+			retval = _copyJsonObjectCtor(from);
+			break;
+		case T_JsonObjectAgg:
+			retval = _copyJsonObjectAgg(from);
+			break;
+		case T_JsonOutput:
+			retval = _copyJsonOutput(from);
+			break;
+		case T_JsonArrayCtor:
+			retval = _copyJsonArrayCtor(from);
+			break;
+		case T_JsonArrayQueryCtor:
+			retval = _copyJsonArrayQueryCtor(from);
+			break;
+		case T_JsonArrayAgg:
+			retval = _copyJsonArrayAgg(from);
+			break;
+		case T_JsonIsPredicate:
+			retval = _copyJsonIsPredicate(from);
+			break;
+		case T_JsonIsPredicateOpts:
+			retval = _copyJsonIsPredicateOpts(from);
+			break;
+		case T_JsonFuncExpr:
+			retval = _copyJsonFuncExpr(from);
+			break;
+		case T_JsonExpr:
+			retval = _copyJsonExpr(from);
+			break;
+		case T_JsonCommon:
+			retval = _copyJsonCommon(from);
+			break;
+		case T_JsonBehavior:
+			retval = _copyJsonBehavior(from);
+			break;
+		case T_JsonArgument:
+			retval = _copyJsonArgument(from);
+			break;
+		case T_JsonCoercion:
+			retval = _copyJsonCoercion(from);
+			break;
+		case T_JsonItemCoercions:
+			retval = _copyJsonItemCoercions(from);
+			break;
 
 			/*
 			 * RELATION NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 35c13a5..cd3ee22 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -818,6 +818,108 @@ _equalOnConflictExpr(const OnConflictExpr *a, const OnConflictExpr *b)
 	return true;
 }
 
+static bool
+_equalJsonValueExpr(const JsonValueExpr *a, const JsonValueExpr *b)
+{
+	COMPARE_NODE_FIELD(expr);
+	COMPARE_SCALAR_FIELD(format.type);
+	COMPARE_SCALAR_FIELD(format.encoding);
+	COMPARE_LOCATION_FIELD(format.location);
+
+	return true;
+}
+
+static bool
+_equalJsonCtorOpts(const JsonCtorOpts *a, const JsonCtorOpts *b)
+{
+	COMPARE_SCALAR_FIELD(returning.format.type);
+	COMPARE_SCALAR_FIELD(returning.format.encoding);
+	COMPARE_LOCATION_FIELD(returning.format.location);
+	COMPARE_SCALAR_FIELD(returning.typid);
+	COMPARE_SCALAR_FIELD(returning.typmod);
+	COMPARE_SCALAR_FIELD(absent_on_null);
+	COMPARE_SCALAR_FIELD(unique);
+
+	return true;
+}
+
+static bool
+_equalJsonIsPredicateOpts(const JsonIsPredicateOpts *a,
+						  const JsonIsPredicateOpts *b)
+{
+	COMPARE_SCALAR_FIELD(value_type);
+	COMPARE_SCALAR_FIELD(unique_keys);
+
+	return true;
+}
+
+/*
+ * _equalJsonExpr
+ */
+static bool
+_equalJsonExpr(const JsonExpr *a, const JsonExpr *b)
+{
+	COMPARE_SCALAR_FIELD(op);
+	COMPARE_NODE_FIELD(raw_expr);
+	COMPARE_NODE_FIELD(formatted_expr);
+	COMPARE_NODE_FIELD(result_coercion);
+	COMPARE_SCALAR_FIELD(format.type);
+	COMPARE_SCALAR_FIELD(format.encoding);
+	COMPARE_LOCATION_FIELD(format.location);
+	COMPARE_NODE_FIELD(path_spec);
+	COMPARE_NODE_FIELD(passing.values);
+	COMPARE_NODE_FIELD(passing.names);
+	COMPARE_SCALAR_FIELD(returning.format.type);
+	COMPARE_SCALAR_FIELD(returning.format.encoding);
+	COMPARE_LOCATION_FIELD(returning.format.location);
+	COMPARE_SCALAR_FIELD(returning.typid);
+	COMPARE_SCALAR_FIELD(returning.typmod);
+	COMPARE_SCALAR_FIELD(on_error.btype);
+	COMPARE_NODE_FIELD(on_error.default_expr);
+	COMPARE_SCALAR_FIELD(on_empty.btype);
+	COMPARE_NODE_FIELD(on_empty.default_expr);
+	COMPARE_NODE_FIELD(coercions);
+	COMPARE_SCALAR_FIELD(wrapper);
+	COMPARE_SCALAR_FIELD(omit_quotes);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
+/*
+ * _equalJsonCoercion
+ */
+static bool
+_equalJsonCoercion(const JsonCoercion *a, const JsonCoercion *b)
+{
+	COMPARE_NODE_FIELD(expr);
+	COMPARE_SCALAR_FIELD(via_populate);
+	COMPARE_SCALAR_FIELD(via_io);
+	COMPARE_SCALAR_FIELD(collation);
+
+	return true;
+}
+
+/*
+ * _equalJsonItemCoercions
+ */
+static bool
+_equalJsonItemCoercions(const JsonItemCoercions *a, const JsonItemCoercions *b)
+{
+	COMPARE_NODE_FIELD(null);
+	COMPARE_NODE_FIELD(string);
+	COMPARE_NODE_FIELD(numeric);
+	COMPARE_NODE_FIELD(boolean);
+	COMPARE_NODE_FIELD(date);
+	COMPARE_NODE_FIELD(time);
+	COMPARE_NODE_FIELD(timetz);
+	COMPARE_NODE_FIELD(timestamp);
+	COMPARE_NODE_FIELD(timestamptz);
+	COMPARE_NODE_FIELD(composite);
+
+	return true;
+}
+
 /*
  * Stuff from relation.h
  */
@@ -3166,6 +3268,24 @@ equal(const void *a, const void *b)
 		case T_JoinExpr:
 			retval = _equalJoinExpr(a, b);
 			break;
+		case T_JsonValueExpr:
+			retval = _equalJsonValueExpr(a, b);
+			break;
+		case T_JsonCtorOpts:
+			retval = _equalJsonCtorOpts(a, b);
+			break;
+		case T_JsonIsPredicateOpts:
+			retval = _equalJsonIsPredicateOpts(a, b);
+			break;
+		case T_JsonExpr:
+			retval = _equalJsonExpr(a, b);
+			break;
+		case T_JsonCoercion:
+			retval = _equalJsonCoercion(a, b);
+			break;
+		case T_JsonItemCoercions:
+			retval = _equalJsonItemCoercions(a, b);
+			break;
 
 			/*
 			 * RELATION NODES
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 1bd2599..ebc41ea 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -20,6 +20,7 @@
 #include "fmgr.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "utils/errcodes.h"
 #include "utils/lsyscache.h"
 
 
@@ -628,3 +629,87 @@ makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
 	v->va_cols = va_cols;
 	return v;
 }
+
+/*
+ * makeJsonValueExpr -
+ *	  creates a JsonValueExpr node
+ */
+JsonValueExpr *
+makeJsonValueExpr(Expr *expr, JsonFormat format)
+{
+	JsonValueExpr *jve = makeNode(JsonValueExpr);
+
+	jve->expr = expr;
+	jve->format = format;
+
+	return jve;
+}
+
+/*
+ * makeJsonBehavior -
+ *	  creates a JsonBehavior node
+ */
+JsonBehavior *
+makeJsonBehavior(JsonBehaviorType type, Node *default_expr)
+{
+	JsonBehavior *behavior = makeNode(JsonBehavior);
+
+	behavior->btype = type;
+	behavior->default_expr = default_expr;
+
+	return behavior;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * makeJsonIsPredicate -
+ *	  creates a JsonIsPredicate node
+ */
+Node *
+makeJsonIsPredicate(Node *expr, JsonFormat format, JsonValueType vtype,
+					bool unique_keys)
+{
+	JsonIsPredicate *n = makeNode(JsonIsPredicate);
+
+	n->expr = expr;
+	n->format = format;
+	n->vtype = vtype;
+	n->unique_keys = unique_keys;
+
+	return (Node *) n;
+}
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index a10014f..f699977 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -259,6 +259,15 @@ exprType(const Node *expr)
 		case T_PlaceHolderVar:
 			type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_JsonValueExpr:
+			type = exprType((Node *) ((const JsonValueExpr *) expr)->expr);
+			break;
+		case T_JsonExpr:
+			type = ((const JsonExpr *) expr)->returning.typid;
+			break;
+		case T_JsonCoercion:
+			type = exprType(((const JsonCoercion *) expr)->expr);
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			type = InvalidOid;	/* keep compiler quiet */
@@ -492,6 +501,12 @@ exprTypmod(const Node *expr)
 			return ((const SetToDefault *) expr)->typeMod;
 		case T_PlaceHolderVar:
 			return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+		case T_JsonValueExpr:
+			return exprTypmod((Node *) ((const JsonValueExpr *) expr)->expr);
+		case T_JsonExpr:
+			return ((JsonExpr *) expr)->returning.typmod;
+		case T_JsonCoercion:
+			return exprTypmod(((const JsonCoercion *) expr)->expr);
 		default:
 			break;
 	}
@@ -903,6 +918,24 @@ exprCollation(const Node *expr)
 		case T_PlaceHolderVar:
 			coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_JsonValueExpr:
+			coll = exprCollation((Node *) ((const JsonValueExpr *) expr)->expr);
+			break;
+		case T_JsonExpr:
+			{
+				JsonExpr *jexpr = (JsonExpr *) expr;
+				JsonCoercion *coercion = jexpr->result_coercion;
+
+				if (!coercion)
+					coll = InvalidOid;
+				else if (coercion->expr)
+					coll = exprCollation(coercion->expr);
+				else if (coercion->via_io || coercion->via_populate)
+					coll = coercion->collation;
+				else
+					coll = InvalidOid;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			coll = InvalidOid;	/* keep compiler quiet */
@@ -1104,6 +1137,25 @@ exprSetCollation(Node *expr, Oid collation)
 			Assert(!OidIsValid(collation)); /* result is always an integer
 											 * type */
 			break;
+		case T_JsonValueExpr:
+			exprSetCollation((Node *) ((const JsonValueExpr *) expr)->expr,
+							 collation);
+			break;
+		case T_JsonExpr:
+			{
+				JsonExpr *jexpr = (JsonExpr *) expr;
+				JsonCoercion *coercion = jexpr->result_coercion;
+
+				if (!coercion)
+					Assert(!OidIsValid(collation));
+				else if (coercion->expr)
+					exprSetCollation(coercion->expr, collation);
+				else if (coercion->via_io || coercion->via_populate)
+					coercion->collation = collation;
+				else
+					Assert(!OidIsValid(collation));
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			break;
@@ -1544,6 +1596,18 @@ exprLocation(const Node *expr)
 		case T_PartitionRangeDatum:
 			loc = ((const PartitionRangeDatum *) expr)->location;
 			break;
+		case T_JsonValueExpr:
+			loc = exprLocation((Node *) ((const JsonValueExpr *) expr)->expr);
+			break;
+		case T_JsonExpr:
+			{
+				const JsonExpr *jsexpr = (const JsonExpr *) expr;
+
+				/* consider both function name and leftmost arg */
+				loc = leftmostLoc(jsexpr->location,
+								  exprLocation(jsexpr->raw_expr));
+			}
+			break;
 		default:
 			/* for any other node type it's just unknown... */
 			loc = -1;
@@ -2229,6 +2293,57 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
+		case T_JsonValueExpr:
+			return walker(((JsonValueExpr *) node)->expr, context);
+		case T_JsonExpr:
+			{
+				JsonExpr    *jexpr = (JsonExpr *) node;
+
+				if (walker(jexpr->raw_expr, context))
+					return true;
+				if (walker(jexpr->formatted_expr, context))
+					return true;
+				if (walker(jexpr->result_coercion, context))
+					return true;
+				if (walker(jexpr->passing.values, context))
+					return true;
+				/* we assume walker doesn't care about passing.names */
+				if (walker(jexpr->on_empty.default_expr, context))
+					return true;
+				if (walker(jexpr->on_error.default_expr, context))
+					return true;
+				if (walker(jexpr->coercions, context))
+					return true;
+			}
+			break;
+		case T_JsonCoercion:
+			return walker(((JsonCoercion *) node)->expr, context);
+		case T_JsonItemCoercions:
+			{
+				JsonItemCoercions *coercions = (JsonItemCoercions *) node;
+
+				if (walker(coercions->null, context))
+					return true;
+				if (walker(coercions->string, context))
+					return true;
+				if (walker(coercions->numeric, context))
+					return true;
+				if (walker(coercions->boolean, context))
+					return true;
+				if (walker(coercions->date, context))
+					return true;
+				if (walker(coercions->time, context))
+					return true;
+				if (walker(coercions->timetz, context))
+					return true;
+				if (walker(coercions->timestamp, context))
+					return true;
+				if (walker(coercions->timestamptz, context))
+					return true;
+				if (walker(coercions->composite, context))
+					return true;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -3060,6 +3175,65 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_JsonValueExpr:
+			{
+				JsonValueExpr *jve = (JsonValueExpr *) node;
+				JsonValueExpr *newnode;
+
+				FLATCOPY(newnode, jve, JsonValueExpr);
+				MUTATE(newnode->expr, jve->expr, Expr *);
+
+				return (Node *) newnode;
+			}
+			break;
+		case T_JsonExpr:
+			{
+				JsonExpr    *jexpr = (JsonExpr *) node;
+				JsonExpr    *newnode;
+
+				FLATCOPY(newnode, jexpr, JsonExpr);
+				MUTATE(newnode->raw_expr, jexpr->path_spec, Node *);
+				MUTATE(newnode->raw_expr, jexpr->raw_expr, Node *);
+				MUTATE(newnode->formatted_expr, jexpr->formatted_expr, Node *);
+				MUTATE(newnode->result_coercion, jexpr->result_coercion, JsonCoercion *);
+				MUTATE(newnode->passing.values, jexpr->passing.values, List *);
+				/* assume mutator does not care about passing.names */
+				MUTATE(newnode->on_empty.default_expr,
+					   jexpr->on_empty.default_expr, Node *);
+				MUTATE(newnode->on_error.default_expr,
+					   jexpr->on_error.default_expr, Node *);
+				return (Node *) newnode;
+			}
+			break;
+		case T_JsonCoercion:
+			{
+				JsonCoercion *coercion = (JsonCoercion *) node;
+				JsonCoercion *newnode;
+
+				FLATCOPY(newnode, coercion, JsonCoercion);
+				MUTATE(newnode->expr, coercion->expr, Node *);
+				return (Node *) newnode;
+			}
+			break;
+		case T_JsonItemCoercions:
+			{
+				JsonItemCoercions *coercions = (JsonItemCoercions *) node;
+				JsonItemCoercions *newnode;
+
+				FLATCOPY(newnode, coercions, JsonItemCoercions);
+				MUTATE(newnode->null, coercions->null, JsonCoercion *);
+				MUTATE(newnode->string, coercions->string, JsonCoercion *);
+				MUTATE(newnode->numeric, coercions->numeric, JsonCoercion *);
+				MUTATE(newnode->boolean, coercions->boolean, JsonCoercion *);
+				MUTATE(newnode->date, coercions->date, JsonCoercion *);
+				MUTATE(newnode->time, coercions->time, JsonCoercion *);
+				MUTATE(newnode->timetz, coercions->timetz, JsonCoercion *);
+				MUTATE(newnode->timestamp, coercions->timestamp, JsonCoercion *);
+				MUTATE(newnode->timestamptz, coercions->timestamptz, JsonCoercion *);
+				MUTATE(newnode->composite, coercions->composite, JsonCoercion *);
+				return (Node *) newnode;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -3704,6 +3878,121 @@ raw_expression_tree_walker(Node *node,
 			break;
 		case T_CommonTableExpr:
 			return walker(((CommonTableExpr *) node)->ctequery, context);
+		case T_JsonValueExpr:
+			return walker(((JsonValueExpr *) node)->expr, context);
+		case T_JsonOutput:
+			return walker(((JsonOutput *) node)->typename, context);
+		case T_JsonKeyValue:
+			{
+				JsonKeyValue *jkv = (JsonKeyValue *) node;
+
+				if (walker(jkv->key, context))
+					return true;
+				if (walker(jkv->value, context))
+					return true;
+			}
+			break;
+		case T_JsonObjectCtor:
+			{
+				JsonObjectCtor *joc = (JsonObjectCtor *) node;
+
+				if (walker(joc->output, context))
+					return true;
+				if (walker(joc->exprs, context))
+					return true;
+			}
+			break;
+		case T_JsonArrayCtor:
+			{
+				JsonArrayCtor *jac = (JsonArrayCtor *) node;
+
+				if (walker(jac->output, context))
+					return true;
+				if (walker(jac->exprs, context))
+					return true;
+			}
+			break;
+		case T_JsonObjectAgg:
+			{
+				JsonObjectAgg *joa = (JsonObjectAgg *) node;
+
+				if (walker(joa->ctor.output, context))
+					return true;
+				if (walker(joa->ctor.agg_order, context))
+					return true;
+				if (walker(joa->ctor.agg_filter, context))
+					return true;
+				if (walker(joa->ctor.over, context))
+					return true;
+				if (walker(joa->arg, context))
+					return true;
+			}
+			break;
+		case T_JsonArrayAgg:
+			{
+				JsonArrayAgg *jaa = (JsonArrayAgg *) node;
+
+				if (walker(jaa->ctor.output, context))
+					return true;
+				if (walker(jaa->ctor.agg_order, context))
+					return true;
+				if (walker(jaa->ctor.agg_filter, context))
+					return true;
+				if (walker(jaa->ctor.over, context))
+					return true;
+				if (walker(jaa->arg, context))
+					return true;
+			}
+			break;
+		case T_JsonArrayQueryCtor:
+			{
+				JsonArrayQueryCtor *jaqc = (JsonArrayQueryCtor *) node;
+
+				if (walker(jaqc->output, context))
+					return true;
+				if (walker(jaqc->query, context))
+					return true;
+			}
+			break;
+		case T_JsonIsPredicate:
+			return walker(((JsonIsPredicate *) node)->expr, context);
+		case T_JsonArgument:
+			return walker(((JsonArgument *) node)->val, context);
+		case T_JsonCommon:
+			{
+				JsonCommon *jc = (JsonCommon *) node;
+
+				if (walker(jc->expr, context))
+					return true;
+				if (walker(jc->pathspec, context))
+					return true;
+				if (walker(jc->passing, context))
+					return true;
+			}
+			break;
+		case T_JsonBehavior:
+			{
+				JsonBehavior *jb = (JsonBehavior *) node;
+
+				if (jb->btype == JSON_BEHAVIOR_DEFAULT &&
+					walker(jb->default_expr, context))
+					return true;
+			}
+			break;
+		case T_JsonFuncExpr:
+			{
+				JsonFuncExpr *jfe = (JsonFuncExpr *) node;
+
+				if (walker(jfe->common, context))
+					return true;
+				if (jfe->output && walker(jfe->output, context))
+					return true;
+				if (walker(jfe->on_empty, context))
+					return true;
+				if (walker(jfe->on_error, context))
+					return true;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1b4e0ed..9529717 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1767,6 +1767,98 @@ _outOnConflictExpr(StringInfo str, const OnConflictExpr *node)
 	WRITE_NODE_FIELD(exclRelTlist);
 }
 
+static void
+_outJsonValueExpr(StringInfo str, const JsonValueExpr *node)
+{
+	WRITE_NODE_TYPE("JSONVALUEEXPR");
+
+	WRITE_NODE_FIELD(expr);
+	WRITE_ENUM_FIELD(format.type, JsonFormatType);
+	WRITE_ENUM_FIELD(format.encoding, JsonEncoding);
+	WRITE_LOCATION_FIELD(format.location);
+}
+
+static void
+_outJsonCtorOpts(StringInfo str, const JsonCtorOpts *node)
+{
+	WRITE_NODE_TYPE("JSONCTOROPTS");
+
+	WRITE_ENUM_FIELD(returning.format.type, JsonFormatType);
+	WRITE_ENUM_FIELD(returning.format.encoding, JsonEncoding);
+	WRITE_LOCATION_FIELD(returning.format.location);
+	WRITE_OID_FIELD(returning.typid);
+	WRITE_INT_FIELD(returning.typmod);
+	WRITE_BOOL_FIELD(unique);
+	WRITE_BOOL_FIELD(absent_on_null);
+}
+
+static void
+_outJsonExpr(StringInfo str, const JsonExpr *node)
+{
+	WRITE_NODE_TYPE("JSONEXPR");
+
+	WRITE_ENUM_FIELD(op, JsonExprOp);
+	WRITE_NODE_FIELD(raw_expr);
+	WRITE_NODE_FIELD(formatted_expr);
+	WRITE_NODE_FIELD(result_coercion);
+	WRITE_ENUM_FIELD(format.type, JsonFormatType);
+	WRITE_ENUM_FIELD(format.encoding, JsonEncoding);
+	WRITE_LOCATION_FIELD(format.location);
+	WRITE_NODE_FIELD(path_spec);
+	WRITE_NODE_FIELD(passing.values);
+	WRITE_NODE_FIELD(passing.names);
+	WRITE_ENUM_FIELD(returning.format.type, JsonFormatType);
+	WRITE_ENUM_FIELD(returning.format.encoding, JsonEncoding);
+	WRITE_LOCATION_FIELD(returning.format.location);
+	WRITE_OID_FIELD(returning.typid);
+	WRITE_INT_FIELD(returning.typmod);
+	WRITE_ENUM_FIELD(on_error.btype, JsonBehaviorType);
+	WRITE_NODE_FIELD(on_error.default_expr);
+	WRITE_ENUM_FIELD(on_empty.btype, JsonBehaviorType);
+	WRITE_NODE_FIELD(on_empty.default_expr);
+	WRITE_NODE_FIELD(coercions);
+	WRITE_ENUM_FIELD(wrapper, JsonWrapper);
+	WRITE_BOOL_FIELD(omit_quotes);
+	WRITE_LOCATION_FIELD(location);
+}
+
+static void
+_outJsonCoercion(StringInfo str, const JsonCoercion *node)
+{
+	WRITE_NODE_TYPE("JSONCOERCION");
+
+	WRITE_NODE_FIELD(expr);
+	WRITE_BOOL_FIELD(via_populate);
+	WRITE_BOOL_FIELD(via_io);
+	WRITE_OID_FIELD(collation);
+}
+
+static void
+_outJsonItemCoercions(StringInfo str, const JsonItemCoercions *node)
+{
+	WRITE_NODE_TYPE("JSONITEMCOERCIONS");
+
+	WRITE_NODE_FIELD(null);
+	WRITE_NODE_FIELD(string);
+	WRITE_NODE_FIELD(numeric);
+	WRITE_NODE_FIELD(boolean);
+	WRITE_NODE_FIELD(date);
+	WRITE_NODE_FIELD(time);
+	WRITE_NODE_FIELD(timetz);
+	WRITE_NODE_FIELD(timestamp);
+	WRITE_NODE_FIELD(timestamptz);
+	WRITE_NODE_FIELD(composite);
+}
+
+static void
+_outJsonIsPredicateOpts(StringInfo str, const JsonIsPredicateOpts *node)
+{
+	WRITE_NODE_TYPE("JSONISOPTS");
+
+	WRITE_ENUM_FIELD(value_type, JsonValueType);
+	WRITE_BOOL_FIELD(unique_keys);
+}
+
 /*****************************************************************************
  *
  *	Stuff from relation.h.
@@ -4322,6 +4414,24 @@ outNode(StringInfo str, const void *obj)
 			case T_PartitionRangeDatum:
 				_outPartitionRangeDatum(str, obj);
 				break;
+			case T_JsonValueExpr:
+				_outJsonValueExpr(str, obj);
+				break;
+			case T_JsonCtorOpts:
+				_outJsonCtorOpts(str, obj);
+				break;
+			case T_JsonIsPredicateOpts:
+				_outJsonIsPredicateOpts(str, obj);
+				break;
+			case T_JsonExpr:
+				_outJsonExpr(str, obj);
+				break;
+			case T_JsonCoercion:
+				_outJsonCoercion(str, obj);
+				break;
+			case T_JsonItemCoercions:
+				_outJsonItemCoercions(str, obj);
+				break;
 
 			default:
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index c59bef3..6954854 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1335,6 +1335,127 @@ _readOnConflictExpr(void)
 }
 
 /*
+ * _readJsonValueExpr
+ */
+static JsonValueExpr *
+_readJsonValueExpr(void)
+{
+	READ_LOCALS(JsonValueExpr);
+
+	READ_NODE_FIELD(expr);
+	READ_ENUM_FIELD(format.type, JsonFormatType);
+	READ_ENUM_FIELD(format.encoding, JsonEncoding);
+	READ_LOCATION_FIELD(format.location);
+
+	READ_DONE();
+}
+
+/*
+ * _readJsonCtorOpts
+ */
+static JsonCtorOpts *
+_readJsonCtorOpts(void)
+{
+	READ_LOCALS(JsonCtorOpts);
+	READ_ENUM_FIELD(returning.format.type, JsonFormatType);
+	READ_ENUM_FIELD(returning.format.encoding, JsonEncoding);
+	READ_LOCATION_FIELD(returning.format.location);
+	READ_OID_FIELD(returning.typid);
+	READ_INT_FIELD(returning.typmod);
+	READ_BOOL_FIELD(unique);
+	READ_BOOL_FIELD(absent_on_null);
+
+	READ_DONE();
+}
+
+/*
+ * _readJsonExpr
+ */
+static JsonExpr *
+_readJsonExpr(void)
+{
+	READ_LOCALS(JsonExpr);
+
+	READ_ENUM_FIELD(op, JsonExprOp);
+	READ_NODE_FIELD(raw_expr);
+	READ_NODE_FIELD(formatted_expr);
+	READ_NODE_FIELD(result_coercion);
+	READ_ENUM_FIELD(format.type, JsonFormatType);
+	READ_ENUM_FIELD(format.encoding, JsonEncoding);
+	READ_LOCATION_FIELD(format.location);
+	READ_NODE_FIELD(path_spec);
+	READ_NODE_FIELD(passing.values);
+	READ_NODE_FIELD(passing.names);
+	READ_ENUM_FIELD(returning.format.type, JsonFormatType);
+	READ_ENUM_FIELD(returning.format.encoding, JsonEncoding);
+	READ_LOCATION_FIELD(returning.format.location);
+	READ_OID_FIELD(returning.typid);
+	READ_INT_FIELD(returning.typmod);
+	READ_ENUM_FIELD(on_error.btype, JsonBehaviorType);
+	READ_NODE_FIELD(on_error.default_expr);
+	READ_ENUM_FIELD(on_empty.btype, JsonBehaviorType);
+	READ_NODE_FIELD(on_empty.default_expr);
+	READ_NODE_FIELD(coercions);
+	READ_ENUM_FIELD(wrapper, JsonWrapper);
+	READ_BOOL_FIELD(omit_quotes);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+/*
+ * _readJsonCoercion
+ */
+static JsonCoercion *
+_readJsonCoercion(void)
+{
+	READ_LOCALS(JsonCoercion);
+
+	READ_NODE_FIELD(expr);
+	READ_BOOL_FIELD(via_populate);
+	READ_BOOL_FIELD(via_io);
+	READ_OID_FIELD(collation);
+
+	READ_DONE();
+}
+
+/*
+ * _readJsonItemCoercions
+ */
+static JsonItemCoercions *
+_readJsonItemCoercions(void)
+{
+	READ_LOCALS(JsonItemCoercions);
+
+	READ_NODE_FIELD(null);
+	READ_NODE_FIELD(string);
+	READ_NODE_FIELD(numeric);
+	READ_NODE_FIELD(boolean);
+	READ_NODE_FIELD(date);
+	READ_NODE_FIELD(time);
+	READ_NODE_FIELD(timetz);
+	READ_NODE_FIELD(timestamp);
+	READ_NODE_FIELD(timestamptz);
+	READ_NODE_FIELD(composite);
+
+	READ_DONE();
+}
+
+/*
+ * _readJsonIsPredicateOpts
+ */
+static JsonIsPredicateOpts *
+_readJsonIsPredicateOpts()
+{
+	READ_LOCALS(JsonIsPredicateOpts);
+
+	READ_ENUM_FIELD(value_type, JsonValueType);
+	READ_BOOL_FIELD(unique_keys);
+
+	READ_DONE();
+}
+
+/*
  *	Stuff from parsenodes.h.
  */
 
@@ -2747,6 +2868,18 @@ parseNodeString(void)
 		return_value = _readPartitionBoundSpec();
 	else if (MATCH("PARTITIONRANGEDATUM", 19))
 		return_value = _readPartitionRangeDatum();
+	else if (MATCH("JSONVALUEEXPR", 13))
+		return_value = _readJsonValueExpr();
+	else if (MATCH("JSONCTOROPTS", 12))
+		return_value = _readJsonCtorOpts();
+	else if (MATCH("JSONISOPTS", 10))
+		return_value = _readJsonIsPredicateOpts();
+	else if (MATCH("JSONEXPR", 8))
+		return_value = _readJsonExpr();
+	else if (MATCH("JSONCOERCION", 12))
+		return_value = _readJsonCoercion();
+	else if (MATCH("JSONITEMCOERCIONS", 17))
+		return_value = _readJsonItemCoercions();
 	else
 	{
 		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index a2a7e0c..e16a179 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -3912,7 +3912,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
 			 IsA(node, SQLValueFunction) ||
 			 IsA(node, XmlExpr) ||
 			 IsA(node, CoerceToDomain) ||
-			 IsA(node, NextValueExpr))
+			 IsA(node, NextValueExpr) ||
+			 IsA(node, JsonExpr))
 	{
 		/* Treat all these as having cost 1 */
 		context->total.per_tuple += cpu_operator_cost;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 69f49a5..84bf694 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -28,6 +28,7 @@
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
 #include "executor/functions.h"
+#include "executor/execExpr.h"
 #include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -1288,6 +1289,16 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
 								 context, 0);
 	}
 
+	/* JsonExpr is parallel-unsafe if subtransactions can be used. */
+	else if (IsA(node, JsonExpr))
+	{
+		JsonExpr  *jsexpr = (JsonExpr *) node;
+
+		if (ExecEvalJsonNeedsSubTransaction(jsexpr))
+			context->max_hazard = PROPARALLEL_UNSAFE;
+			return true;
+	}
+
 	/* Recurse to check arguments */
 	return expression_tree_walker(node,
 								  max_parallel_hazard_walker,
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 90dfac2..13ab627 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -211,6 +211,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	JoinType			jtype;
 	DropBehavior		dbehavior;
 	OnCommitAction		oncommit;
+	JsonFormat			jsformat;
 	List				*list;
 	Node				*node;
 	Value				*value;
@@ -241,6 +242,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	PartitionSpec		*partspec;
 	PartitionBoundSpec	*partboundspec;
 	RoleSpec			*rolespec;
+	JsonBehavior		*jsbehavior;
+	struct {
+		JsonBehavior		*on_empty;
+		JsonBehavior		*on_error;
+	} 					on_behavior;
+	JsonQuotes			js_quotes;
 }
 
 %type <node>	stmt schema_stmt
@@ -585,6 +592,72 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <list>		hash_partbound partbound_datum_list range_datum_list
 %type <defelt>		hash_partbound_elem
 
+%type <node>		json_value_expr
+					json_func_expr
+					json_value_func_expr
+					json_query_expr
+					json_exists_predicate
+					json_api_common_syntax
+					json_context_item
+					json_argument
+					json_output_clause_opt
+					json_value_constructor
+					json_object_constructor
+					json_object_constructor_args_opt
+					json_object_args
+					json_object_ctor_args_opt
+					json_object_func_args
+					json_array_constructor
+					json_name_and_value
+					json_aggregate_func
+					json_object_aggregate_constructor
+					json_array_aggregate_constructor
+					json_path_specification
+
+%type <list>		json_arguments
+					json_passing_clause_opt
+					json_name_and_value_list
+					json_value_expr_list
+					json_array_aggregate_order_by_clause_opt
+
+%type <typnam>		json_returning_clause_opt
+
+%type <str>			json_table_path_name
+					json_as_path_name_clause_opt
+
+%type <ival>		json_encoding
+					json_encoding_clause_opt
+					json_wrapper_clause_opt
+					json_wrapper_behavior
+					json_conditional_or_unconditional_opt
+					json_predicate_type_constraint_opt
+
+%type <jsformat>	json_format_clause_opt
+					json_representation
+
+%type <jsbehavior>	json_behavior_error
+					json_behavior_null
+					json_behavior_true
+					json_behavior_false
+					json_behavior_unknown
+					json_behavior_empty_array
+					json_behavior_empty_object
+					json_behavior_default
+					json_value_behavior
+					json_query_behavior
+					json_exists_error_behavior
+					json_exists_error_clause_opt
+
+%type <on_behavior> json_value_on_behavior_clause_opt
+					json_query_on_behavior_clause_opt
+
+%type <js_quotes>	json_quotes_behavior
+					json_quotes_clause_opt
+
+%type <boolean>		json_key_uniqueness_constraint_opt
+					json_object_constructor_null_clause_opt
+					json_array_constructor_null_clause_opt
+
 /*
  * 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
@@ -607,7 +680,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  */
 
 /* 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
 	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
 
@@ -617,8 +690,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
 	CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
-	COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
-	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
+	COMMITTED CONCURRENTLY CONDITIONAL CONFIGURATION CONFLICT CONNECTION
+	CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
 	CROSS CSV CUBE CURRENT_P
 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -628,12 +701,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
 	DOUBLE_P DROP
 
-	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
-	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+	EACH ELSE EMPTY_P ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ERROR_P ESCAPE
+	EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
 	EXTENSION EXTERNAL EXTRACT
 
 	FALSE_P FAMILY FETCH FILTER 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
 
@@ -644,9 +717,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
 	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
 
-	JOIN
+	JOIN JSON JSON_ARRAY JSON_ARRAYAGG JSON_EXISTS JSON_OBJECT JSON_OBJECTAGG
+	JSON_QUERY JSON_VALUE
 
-	KEY
+	KEY KEYS KEEP
 
 	LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
 	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
@@ -658,7 +732,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
 	NULLS_P NUMERIC
 
-	OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
+	OBJECT_P OF OFF OFFSET OIDS OLD OMIT ON ONLY OPERATOR OPTION OPTIONS OR
 	ORDER ORDINALITY OTHERS OUT_P OUTER_P
 	OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
 
@@ -666,17 +740,17 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
 	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
 
-	QUOTE
+	QUOTE QUOTES
 
 	RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING
 	REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
 	RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
 	ROUTINE ROUTINES ROW ROWS RULE
 
-	SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
-	SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
-	SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
-	START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P
+	SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT
+	SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF
+	SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P
+	START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRING STRIP_P
 	SUBSCRIPTION SUBSTRING SYMMETRIC SYSID SYSTEM_P
 
 	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
@@ -684,8 +758,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	TREAT TRIGGER TRIM TRUE_P
 	TRUNCATE TRUSTED TYPE_P TYPES_P
 
-	UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
-	UNTIL UPDATE USER USING
+	UNBOUNDED UNCOMMITTED UNCONDITIONAL UNENCRYPTED UNION UNIQUE UNKNOWN
+	UNLISTEN UNLOGGED UNTIL UPDATE USER USING
 
 	VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
 	VERBOSE VERSION_P VIEW VIEWS VOLATILE
@@ -709,11 +783,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  * as NOT, at least with respect to their left-hand subexpression.
  * NULLS_LA and WITH_LA are needed to make the grammar LALR(1).
  */
-%token		NOT_LA NULLS_LA WITH_LA
-
+%token		NOT_LA NULLS_LA WITH_LA WITH_LA_UNIQUE WITHOUT_LA
 
 /* Precedence: lowest to highest */
 %nonassoc	SET				/* see relation_expr_opt_alias */
+%right		FORMAT
 %left		UNION EXCEPT
 %left		INTERSECT
 %left		OR
@@ -752,6 +826,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  * blame any funny behavior of UNBOUNDED on the SQL standard, though.
  */
 %nonassoc	UNBOUNDED		/* ideally should have same precedence as IDENT */
+%nonassoc	ERROR_P EMPTY_P DEFAULT ABSENT /* JSON error/empty behavior */
+%nonassoc	FALSE_P KEEP OMIT PASSING TRUE_P UNKNOWN
 %nonassoc	IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
 %left		Op OPERATOR		/* multi-character ops and user-defined operators */
 %left		'+' '-'
@@ -776,6 +852,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 /* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */
 %right		PRESERVE STRIP_P
 
+%nonassoc	empty_json_unique
+%left		WITHOUT WITH_LA_UNIQUE
+
 %%
 
 /*
@@ -12824,7 +12903,7 @@ ConstInterval:
 
 opt_timezone:
 			WITH_LA TIME ZONE						{ $$ = true; }
-			| WITHOUT TIME ZONE						{ $$ = false; }
+			| WITHOUT_LA TIME ZONE					{ $$ = false; }
 			| /*EMPTY*/								{ $$ = false; }
 		;
 
@@ -13325,6 +13404,48 @@ a_expr:		c_expr									{ $$ = $1; }
 												 list_make1($1), @2),
 									 @2);
 				}
+			| a_expr
+				IS JSON
+					json_predicate_type_constraint_opt
+					json_key_uniqueness_constraint_opt		%prec IS
+				{
+					JsonFormat format = { JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1 };
+					$$ = makeJsonIsPredicate($1, format, $4, $5);
+				}
+			/*
+			 * Required by standard, but it would conflict with expressions
+			 * like: 'str' || format(...)
+			| a_expr
+				FORMAT json_representation
+				IS JSON
+					json_predicate_type_constraint_opt
+					json_key_uniqueness_constraint_opt		%prec FORMAT
+				{
+					$3.location = @2;
+					$$ = makeJsonIsPredicate($1, $3, $6, $7);
+				}
+			*/
+			| a_expr
+				IS NOT JSON
+					json_predicate_type_constraint_opt
+					json_key_uniqueness_constraint_opt		%prec IS
+				{
+					JsonFormat format = { JS_FORMAT_DEFAULT, JS_ENC_DEFAULT, -1 };
+					$$ = makeNotExpr(makeJsonIsPredicate($1, format, $5, $6), @1);
+				}
+			/*
+			 * Required by standard, but it would conflict with expressions
+			 * like: 'str' || format(...)
+			| a_expr
+				FORMAT json_representation
+				IS NOT JSON
+					json_predicate_type_constraint_opt
+					json_key_uniqueness_constraint_opt		%prec FORMAT
+				{
+					$3.location = @2;
+					$$ = makeNotExpr(makeJsonIsPredicate($1, $3, $7, $8), @1);
+				}
+			*/
 			| DEFAULT
 				{
 					/*
@@ -13417,6 +13538,25 @@ b_expr:		c_expr
 				}
 		;
 
+json_predicate_type_constraint_opt:
+			VALUE_P									{ $$ = JS_TYPE_ANY; }
+			| ARRAY									{ $$ = JS_TYPE_ARRAY; }
+			| OBJECT_P								{ $$ = JS_TYPE_OBJECT; }
+			| SCALAR								{ $$ = JS_TYPE_SCALAR; }
+			| /* EMPTY */							{ $$ = JS_TYPE_ANY; }
+		;
+
+json_key_uniqueness_constraint_opt:
+			WITH_LA_UNIQUE UNIQUE opt_keys			{ $$ = true; }
+			| WITHOUT UNIQUE opt_keys				{ $$ = false; }
+			| /* EMPTY */ %prec empty_json_unique	{ $$ = false; }
+		;
+
+opt_keys:
+			KEYS									{ }
+			| /* EMPTY */							{ }
+		;
+
 /*
  * Productions that can be used in both a_expr and b_expr.
  *
@@ -13677,6 +13817,13 @@ func_expr: func_application within_group_clause filter_clause over_clause
 					n->over = $4;
 					$$ = (Node *) n;
 				}
+			| json_aggregate_func filter_clause over_clause
+				{
+					JsonAggCtor *n = (JsonAggCtor *) $1;
+					n->agg_filter = $2;
+					n->over = $3;
+					$$ = (Node *) $1;
+				}
 			| func_expr_common_subexpr
 				{ $$ = $1; }
 		;
@@ -13690,6 +13837,7 @@ func_expr: func_application within_group_clause filter_clause over_clause
 func_expr_windowless:
 			func_application						{ $$ = $1; }
 			| func_expr_common_subexpr				{ $$ = $1; }
+			| json_aggregate_func					{ $$ = $1; }
 		;
 
 /*
@@ -13911,6 +14059,8 @@ func_expr_common_subexpr:
 					n->location = @1;
 					$$ = (Node *)n;
 				}
+			| json_func_expr
+				{ $$ = $1; }
 		;
 
 /*
@@ -14599,6 +14749,495 @@ opt_asymmetric: ASYMMETRIC
 			| /*EMPTY*/
 		;
 
+/* SQL/JSON support */
+json_func_expr:
+			json_value_func_expr
+			| json_query_expr
+			| json_exists_predicate
+			| json_value_constructor
+		;
+
+
+json_value_func_expr:
+			JSON_VALUE '('
+				json_api_common_syntax
+				json_returning_clause_opt
+				json_value_on_behavior_clause_opt
+			')'
+				{
+					JsonFuncExpr *n = makeNode(JsonFuncExpr);
+					n->op = IS_JSON_VALUE;
+					n->common = (JsonCommon *) $3;
+					if ($4)
+					{
+						n->output = (JsonOutput *) makeNode(JsonOutput);
+						n->output->typename = $4;
+						n->output->returning.format.location = @4;
+						n->output->returning.format.type = JS_FORMAT_DEFAULT;
+						n->output->returning.format.encoding = JS_ENC_DEFAULT;
+					}
+					else
+						n->output = NULL;
+					n->on_empty = $5.on_empty;
+					n->on_error = $5.on_error;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_api_common_syntax:
+			json_context_item ',' json_path_specification
+			json_as_path_name_clause_opt
+			json_passing_clause_opt
+				{
+					JsonCommon *n = makeNode(JsonCommon);
+					n->expr = (JsonValueExpr *) $1;
+					n->pathspec = $3;
+					n->pathname = $4;
+					n->passing = $5;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_context_item:
+			json_value_expr							{ $$ = $1; }
+		;
+
+json_path_specification:
+			a_expr									{ $$ = $1; }
+		;
+
+json_as_path_name_clause_opt:
+			 AS json_table_path_name				{ $$ = $2; }
+			 | /* EMPTY */							{ $$ = NULL; }
+		;
+
+json_table_path_name:
+			name									{ $$ = $1; }
+		;
+
+json_passing_clause_opt:
+			PASSING json_arguments					{ $$ = $2; }
+			| /* EMPTY */							{ $$ = NIL; }
+		;
+
+json_arguments:
+			json_argument							{ $$ = list_make1($1); }
+			| json_arguments ',' json_argument		{ $$ = lappend($1, $3); }
+		;
+
+json_argument:
+			json_value_expr AS ColLabel
+			{
+				JsonArgument *n = makeNode(JsonArgument);
+				n->val = (JsonValueExpr *) $1;
+				n->name = $3;
+				$$ = (Node *) n;
+			}
+		;
+
+json_value_expr:
+			a_expr json_format_clause_opt
+			{
+				$$ = (Node *) makeJsonValueExpr((Expr *) $1, $2);
+			}
+		;
+
+json_format_clause_opt:
+			FORMAT json_representation
+				{
+					$$ = $2;
+					$$.location = @1;
+				}
+			| /* EMPTY */
+				{
+					$$.type = JS_FORMAT_DEFAULT;
+					$$.encoding = JS_ENC_DEFAULT;
+					$$.location = -1;
+				}
+		;
+
+json_representation:
+			JSON json_encoding_clause_opt
+				{
+					$$.type = JS_FORMAT_JSON;
+					$$.encoding = $2;
+					$$.location = @1;
+				}
+		/*	| implementation_defined_JSON_representation_option (BSON, AVRO etc) */
+		;
+
+json_encoding_clause_opt:
+			ENCODING json_encoding					{ $$ = $2; }
+			| /* EMPTY */							{ $$ = JS_ENC_DEFAULT; }
+		;
+
+json_encoding:
+			name									{ $$ = makeJsonEncoding($1); }
+	/*
+			| UTF8									{ $$ = JS_ENC_UTF8; }
+			| UTF16									{ $$ = JS_ENC_UTF16; }
+			| UTF32 								{ $$ = JS_ENC_UTF32; }
+	*/
+		;
+
+json_returning_clause_opt:
+			RETURNING Typename						{ $$ = $2; }
+			| /* EMPTY */							{ $$ = NULL; }
+		;
+
+json_behavior_error:
+			ERROR_P		{ $$ = makeJsonBehavior(JSON_BEHAVIOR_ERROR, NULL); }
+		;
+
+json_behavior_null:
+			NULL_P		{ $$ = makeJsonBehavior(JSON_BEHAVIOR_NULL, NULL); }
+		;
+
+json_behavior_true:
+			TRUE_P		{ $$ = makeJsonBehavior(JSON_BEHAVIOR_TRUE, NULL); }
+		;
+
+json_behavior_false:
+			FALSE_P		{ $$ = makeJsonBehavior(JSON_BEHAVIOR_FALSE, NULL); }
+		;
+
+json_behavior_unknown:
+			UNKNOWN		{ $$ = makeJsonBehavior(JSON_BEHAVIOR_UNKNOWN, NULL); }
+		;
+
+json_behavior_empty_array:
+			EMPTY_P ARRAY	{ $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_ARRAY, NULL); }
+		;
+
+json_behavior_empty_object:
+			EMPTY_P OBJECT_P	{ $$ = makeJsonBehavior(JSON_BEHAVIOR_EMPTY_OBJECT, NULL); }
+		;
+
+json_behavior_default:
+			DEFAULT a_expr	{ $$ = makeJsonBehavior(JSON_BEHAVIOR_DEFAULT, $2); }
+		;
+
+
+json_value_behavior:
+			json_behavior_null
+			| json_behavior_error
+			| json_behavior_default
+		;
+
+json_value_on_behavior_clause_opt:
+			json_value_behavior ON EMPTY_P
+									{ $$.on_empty = $1; $$.on_error = NULL; }
+			| json_value_behavior ON EMPTY_P json_value_behavior ON ERROR_P
+									{ $$.on_empty = $1; $$.on_error = $4; }
+			| json_value_behavior ON ERROR_P
+									{ $$.on_empty = NULL; $$.on_error = $1; }
+			|  /* EMPTY */
+									{ $$.on_empty = NULL; $$.on_error = NULL; }
+		;
+
+json_query_expr:
+			JSON_QUERY '('
+				json_api_common_syntax
+				json_output_clause_opt
+				json_wrapper_clause_opt
+				json_quotes_clause_opt
+				json_query_on_behavior_clause_opt
+			')'
+				{
+					JsonFuncExpr *n = makeNode(JsonFuncExpr);
+					n->op = IS_JSON_QUERY;
+					n->common = (JsonCommon *) $3;
+					n->output = (JsonOutput *) $4;
+					n->wrapper = $5;
+					if (n->wrapper != JSW_NONE && $6 != JS_QUOTES_UNSPEC)
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used"),
+								 parser_errposition(@6)));
+					n->omit_quotes = $6 == JS_QUOTES_OMIT;
+					n->on_empty = $7.on_empty;
+					n->on_error = $7.on_error;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_wrapper_clause_opt:
+			json_wrapper_behavior WRAPPER			{ $$ = $1; }
+			| /* EMPTY */							{ $$ = 0; }
+		;
+
+json_wrapper_behavior:
+			WITHOUT array_opt						{ $$ = JSW_NONE; }
+			| WITH json_conditional_or_unconditional_opt array_opt { $$ = $2; }
+		;
+
+array_opt:
+			ARRAY									{ }
+			| /* EMPTY */							{ }
+		;
+
+json_conditional_or_unconditional_opt:
+			CONDITIONAL								{ $$ = JSW_CONDITIONAL; }
+			| UNCONDITIONAL							{ $$ = JSW_UNCONDITIONAL; }
+			| /* EMPTY */							{ $$ = JSW_UNCONDITIONAL; }
+		;
+
+json_quotes_clause_opt:
+			json_quotes_behavior QUOTES json_on_scalar_string_opt { $$ = $1; }
+			| /* EMPTY */							{ $$ = JS_QUOTES_UNSPEC; }
+		;
+
+json_quotes_behavior:
+			KEEP									{ $$ = JS_QUOTES_KEEP; }
+			| OMIT									{ $$ = JS_QUOTES_OMIT; }
+		;
+
+json_on_scalar_string_opt:
+			ON SCALAR STRING						{ }
+			| /* EMPTY */							{ }
+		;
+
+json_query_behavior:
+			json_behavior_error
+			| json_behavior_null
+			| json_behavior_empty_array
+			| json_behavior_empty_object
+		;
+
+json_query_on_behavior_clause_opt:
+			json_query_behavior ON EMPTY_P
+									{ $$.on_empty = $1; $$.on_error = NULL; }
+			| json_query_behavior ON EMPTY_P json_query_behavior ON ERROR_P
+									{ $$.on_empty = $1; $$.on_error = $4; }
+			| json_query_behavior ON ERROR_P
+									{ $$.on_empty = NULL; $$.on_error = $1; }
+			|  /* EMPTY */
+									{ $$.on_empty = NULL; $$.on_error = NULL; }
+		;
+
+
+json_output_clause_opt:
+			RETURNING Typename json_format_clause_opt
+				{
+					JsonOutput *n = makeNode(JsonOutput);
+					n->typename = $2;
+					n->returning.format = $3;
+					$$ = (Node *) n;
+				}
+			| /* EMPTY */								{ $$ = NULL; }
+		;
+
+json_exists_predicate:
+			JSON_EXISTS '('
+				json_api_common_syntax
+				json_exists_error_clause_opt
+			')'
+				{
+					JsonFuncExpr *p = makeNode(JsonFuncExpr);
+					p->op = IS_JSON_EXISTS;
+					p->common = (JsonCommon *) $3;
+					p->on_error = $4;
+					p->location = @1;
+					$$ = (Node *) p;
+				}
+		;
+
+json_exists_error_clause_opt:
+			json_exists_error_behavior ON ERROR_P		{ $$ = $1; }
+			| /* EMPTY */								{ $$ = NULL; }
+		;
+
+json_exists_error_behavior:
+			json_behavior_error
+			| json_behavior_true
+			| json_behavior_false
+			| json_behavior_unknown
+		;
+
+json_value_constructor:
+			json_object_constructor
+			| json_array_constructor
+		;
+
+json_object_constructor:
+			JSON_OBJECT '(' json_object_args ')'
+				{
+					$$ = $3;
+				}
+		;
+
+json_object_args:
+			json_object_ctor_args_opt
+			| json_object_func_args
+		;
+
+json_object_func_args:
+			func_arg_list
+				{
+					List *func = list_make1(makeString("json_object"));
+					$$ = (Node *) makeFuncCall(func, $1, @1);
+				}
+		;
+
+json_object_ctor_args_opt:
+			json_object_constructor_args_opt json_output_clause_opt
+				{
+					JsonObjectCtor *n = (JsonObjectCtor *) $1;
+					n->output = (JsonOutput *) $2;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_object_constructor_args_opt:
+			json_name_and_value_list
+			json_object_constructor_null_clause_opt
+			json_key_uniqueness_constraint_opt
+				{
+					JsonObjectCtor *n = makeNode(JsonObjectCtor);
+					n->exprs = $1;
+					n->absent_on_null = $2;
+					n->unique = $3;
+					$$ = (Node *) n;
+				}
+			| /* EMPTY */
+				{
+					JsonObjectCtor *n = makeNode(JsonObjectCtor);
+					n->exprs = NULL;
+					n->absent_on_null = false;
+					n->unique = false;
+					$$ = (Node *) n;
+				}
+		;
+
+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:
+/* TODO
+			KEY c_expr VALUE_P json_value_expr %prec POSTFIXOP
+				{ $$ = makeJsonKeyValue($2, $4); }
+			|
+*/
+			c_expr VALUE_P json_value_expr
+				{ $$ = makeJsonKeyValue($1, $3); }
+			|
+			a_expr ':' json_value_expr
+				{ $$ = makeJsonKeyValue($1, $3); }
+		;
+
+json_object_constructor_null_clause_opt:
+			NULL_P ON NULL_P					{ $$ = false; }
+			| ABSENT ON NULL_P					{ $$ = true; }
+			| /* EMPTY */						{ $$ = false; }
+		;
+
+json_array_constructor:
+			JSON_ARRAY '('
+				json_value_expr_list
+				json_array_constructor_null_clause_opt
+				json_output_clause_opt
+			')'
+				{
+					JsonArrayCtor *n = makeNode(JsonArrayCtor);
+					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
+			')'
+				{
+					JsonArrayQueryCtor *n = makeNode(JsonArrayQueryCtor);
+					n->query = $3;
+					/* n->format = $4; */
+					n->absent_on_null = true /* $5 */;
+					n->output = (JsonOutput *) $4;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+			| JSON_ARRAY '('
+				json_output_clause_opt
+			')'
+				{
+					JsonArrayCtor *n = makeNode(JsonArrayCtor);
+					n->exprs = NIL;
+					n->absent_on_null = true;
+					n->output = (JsonOutput *) $3;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_value_expr_list:
+			json_value_expr								{ $$ = list_make1($1); }
+			| json_value_expr_list ',' json_value_expr	{ $$ = lappend($1, $3);}
+		;
+
+json_array_constructor_null_clause_opt:
+			NULL_P ON NULL_P						{ $$ = false; }
+			| ABSENT ON NULL_P						{ $$ = true; }
+			| /* EMPTY */							{ $$ = true; }
+		;
+
+json_aggregate_func:
+			json_object_aggregate_constructor
+			| json_array_aggregate_constructor
+		;
+
+json_object_aggregate_constructor:
+			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->ctor.output = (JsonOutput *) $6;
+					n->ctor.agg_order = NULL;
+					n->ctor.location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_array_aggregate_constructor:
+			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->ctor.agg_order = $4;
+					n->absent_on_null = $5;
+					n->ctor.output = (JsonOutput *) $6;
+					n->ctor.location = @1;
+					$$ = (Node *) n;
+				}
+		;
+
+json_array_aggregate_order_by_clause_opt:
+			ORDER BY sortby_list					{ $$ = $3; }
+			| /* EMPTY */							{ $$ = NIL; }
+		;
 
 /*****************************************************************************
  *
@@ -14995,6 +15634,7 @@ ColLabel:	IDENT									{ $$ = $1; }
  */
 unreserved_keyword:
 			  ABORT_P
+			| ABSENT
 			| ABSOLUTE_P
 			| ACCESS
 			| ACTION
@@ -15031,6 +15671,7 @@ unreserved_keyword:
 			| COMMENTS
 			| COMMIT
 			| COMMITTED
+			| CONDITIONAL
 			| CONFIGURATION
 			| CONFLICT
 			| CONNECTION
@@ -15066,10 +15707,12 @@ unreserved_keyword:
 			| DOUBLE_P
 			| DROP
 			| EACH
+			| EMPTY_P
 			| ENABLE_P
 			| ENCODING
 			| ENCRYPTED
 			| ENUM_P
+			| ERROR_P
 			| ESCAPE
 			| EVENT
 			| EXCLUDE
@@ -15115,7 +15758,10 @@ unreserved_keyword:
 			| INSTEAD
 			| INVOKER
 			| ISOLATION
+			| JSON
+			| KEEP
 			| KEY
+			| KEYS
 			| LABEL
 			| LANGUAGE
 			| LARGE_P
@@ -15153,6 +15799,7 @@ unreserved_keyword:
 			| OFF
 			| OIDS
 			| OLD
+			| OMIT
 			| OPERATOR
 			| OPTION
 			| OPTIONS
@@ -15182,6 +15829,7 @@ unreserved_keyword:
 			| PROGRAM
 			| PUBLICATION
 			| QUOTE
+			| QUOTES
 			| RANGE
 			| READ
 			| REASSIGN
@@ -15210,6 +15858,7 @@ unreserved_keyword:
 			| ROWS
 			| RULE
 			| SAVEPOINT
+			| SCALAR
 			| SCHEMA
 			| SCHEMAS
 			| SCROLL
@@ -15258,6 +15907,7 @@ unreserved_keyword:
 			| TYPES_P
 			| UNBOUNDED
 			| UNCOMMITTED
+			| UNCONDITIONAL
 			| UNENCRYPTED
 			| UNKNOWN
 			| UNLISTEN
@@ -15315,6 +15965,13 @@ col_name_keyword:
 			| INT_P
 			| INTEGER
 			| INTERVAL
+			| JSON_ARRAY
+			| JSON_ARRAYAGG
+			| JSON_EXISTS
+			| JSON_OBJECT
+			| JSON_OBJECTAGG
+			| JSON_QUERY
+			| JSON_VALUE
 			| LEAST
 			| NATIONAL
 			| NCHAR
@@ -15329,6 +15986,7 @@ col_name_keyword:
 			| ROW
 			| SETOF
 			| SMALLINT
+			| STRING
 			| SUBSTRING
 			| TIME
 			| TIMESTAMP
@@ -15366,6 +16024,7 @@ type_func_name_keyword:
 			| CONCURRENTLY
 			| CROSS
 			| CURRENT_SCHEMA
+			| FORMAT
 			| FREEZE
 			| FULL
 			| ILIKE
diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c
index 6d34245..e486e7c 100644
--- a/src/backend/parser/parse_collate.c
+++ b/src/backend/parser/parse_collate.c
@@ -667,6 +667,10 @@ assign_collations_walker(Node *node, assign_collations_context *context)
 															&loccontext);
 						}
 						break;
+					case T_JsonExpr:
+						/* Context item and PASSING arguments are already
+						 * marked with collations in parse_expr.c. */
+						break;
 					default:
 
 						/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 385e54a..9c66636 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -15,6 +15,8 @@
 
 #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"
@@ -35,6 +37,7 @@
 #include "parser/parse_agg.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"
@@ -121,6 +124,15 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
 static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
+static Node *transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor);
+static Node *transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor);
+static Node *transformJsonArrayQueryCtor(ParseState *pstate,
+										 JsonArrayQueryCtor *ctor);
+static Node *transformJsonObjectAgg(ParseState *pstate, JsonObjectAgg *agg);
+static Node *transformJsonArrayAgg(ParseState *pstate, JsonArrayAgg *agg);
+static Node *transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *p);
+static Node *transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *p);
+static Node *transformJsonValueExpr(ParseState *pstate, JsonValueExpr *jve);
 static Node *make_row_comparison_op(ParseState *pstate, List *opname,
 					   List *largs, List *rargs, int location);
 static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -369,6 +381,38 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 				break;
 			}
 
+		case T_JsonObjectCtor:
+			result = transformJsonObjectCtor(pstate, (JsonObjectCtor *) expr);
+			break;
+
+		case T_JsonArrayCtor:
+			result = transformJsonArrayCtor(pstate, (JsonArrayCtor *) expr);
+			break;
+
+		case T_JsonArrayQueryCtor:
+			result = transformJsonArrayQueryCtor(pstate, (JsonArrayQueryCtor *) expr);
+			break;
+
+		case T_JsonObjectAgg:
+			result = transformJsonObjectAgg(pstate, (JsonObjectAgg *) expr);
+			break;
+
+		case T_JsonArrayAgg:
+			result = transformJsonArrayAgg(pstate, (JsonArrayAgg *) expr);
+			break;
+
+		case T_JsonIsPredicate:
+			result = transformJsonIsPredicate(pstate, (JsonIsPredicate *) expr);
+			break;
+
+		case T_JsonFuncExpr:
+			result = transformJsonFuncExpr(pstate, (JsonFuncExpr *) expr);
+			break;
+
+		case T_JsonValueExpr:
+			result = transformJsonValueExpr(pstate, (JsonValueExpr *) expr);
+			break;
+
 		default:
 			/* should not reach here */
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -3485,3 +3529,1215 @@ ParseExprKindName(ParseExprKind exprKind)
 	}
 	return "unrecognized expression kind";
 }
+
+/*
+ * Make string Const node from JSON encoding name.
+ *
+ * UTF8 is default encoding.
+ */
+static Const *
+getJsonEncodingConst(JsonFormat *format)
+{
+	JsonEncoding encoding;
+	const char *enc;
+	Name		encname = palloc(sizeof(NameData));
+
+	if (!format ||
+		format->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:
+		default:
+			enc = "UTF8";
+			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_PG_CONVERT_FROM, TEXTOID,
+									 list_make2(expr, encoding),
+									 InvalidOid, InvalidOid,
+									 COERCE_INTERNAL_CAST);
+
+	fexpr->location = location;
+
+	return (Node *) fexpr;
+}
+
+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 *
+transformJsonValueExprExt(ParseState *pstate, JsonValueExpr *ve,
+						  JsonFormatType default_format, bool isarg,
+						  Node **rawexpr)
+{
+	Node	   *expr = transformExprRecurse(pstate, (Node *) ve->expr);
+	JsonFormatType format;
+	Oid			exprtype;
+	int			location;
+	char		typcategory;
+	bool		typispreferred;
+
+	if (exprType(expr) == UNKNOWNOID)
+		expr = coerce_to_specific_type(pstate, expr, TEXTOID, "JSON_VALUE_EXPR");
+
+	exprtype = exprType(expr);
+	location = exprLocation(expr);
+
+	get_type_category_preferred(exprtype, &typcategory, &typispreferred);
+
+	if (rawexpr)
+	{
+		/*
+		 * Save a raw context item expression if it is needed for the isolation
+		 * of error handling in the formatting stage.
+		 */
+		*rawexpr = expr;
+		assign_expr_collations(pstate, expr);
+		expr = makeCaseTestExpr(expr);
+	}
+
+	if (ve->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")));
+		}
+		else
+			format = ve->format.type;
+	}
+	else if (isarg)
+	{
+		/* Pass SQL/JSON item types directly without conversion to json[b]. */
+		switch (exprtype)
+		{
+			case TEXTOID:
+			case NUMERICOID:
+			case BOOLOID:
+			case INT2OID:
+			case INT4OID:
+			case INT8OID:
+			case FLOAT4OID:
+			case FLOAT8OID:
+			case DATEOID:
+			case TIMEOID:
+			case TIMETZOID:
+			case TIMESTAMPOID:
+			case TIMESTAMPTZOID:
+				return expr;
+
+			default:
+				if (typcategory == TYPCATEGORY_STRING)
+					return coerce_to_specific_type(pstate, expr, TEXTOID,
+												   "JSON_VALUE_EXPR");
+				/* else convert argument to json[b] type */
+				break;
+		}
+
+		format = default_format;
+	}
+	else if (exprtype == JSONOID || exprtype == JSONBOID)
+		format = JS_FORMAT_DEFAULT;	/* do not format json[b] types */
+	else
+		format = default_format;
+
+	if (format != JS_FORMAT_DEFAULT)
+	{
+		Oid			targettype = format == JS_FORMAT_JSONB ? JSONBOID : JSONOID;
+		Node	   *coerced;
+		FuncExpr   *fexpr;
+
+		if (!isarg && exprtype != BYTEAOID && typcategory != TYPCATEGORY_STRING)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg(ve->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_INTERNAL_CAST,
+										location);
+
+		if (coerced)
+			expr = coerced;
+		else
+		{
+
+			/* If coercion failed, use to_json()/to_jsonb() functions. */
+			fexpr = makeFuncExpr(targettype == JSONOID ? F_TO_JSON : F_TO_JSONB,
+								 targettype, list_make1(expr),
+								 InvalidOid, InvalidOid,
+								 COERCE_INTERNAL_CAST);
+			fexpr->location = location;
+
+			expr = (Node *) fexpr;
+		}
+
+		ve = copyObject(ve);
+		ve->expr = (Expr *) expr;
+
+		expr = (Node *) ve;
+	}
+
+	return expr;
+}
+
+/*
+ * Transform JSON value expression using FORMAT JSON by default.
+ */
+static Node *
+transformJsonValueExpr(ParseState *pstate, JsonValueExpr *jve)
+{
+	return transformJsonValueExprExt(pstate, jve, JS_FORMAT_JSON, false, NULL);
+}
+
+/*
+ * Transform JSON value expression using unspecified format by default.
+ */
+static Node *
+transformJsonValueExprDefault(ParseState *pstate, JsonValueExpr *jve)
+{
+	return transformJsonValueExprExt(pstate, jve, JS_FORMAT_DEFAULT, false, NULL);
+}
+
+/*
+ * 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->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->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 void
+transformJsonOutput(ParseState *pstate, const JsonOutput *output,
+					bool allow_format, JsonReturning *ret)
+{
+	/* if output clause is not specified, make default clause value */
+	if (!output)
+	{
+		ret->format.type = JS_FORMAT_DEFAULT;
+		ret->format.encoding = JS_ENC_DEFAULT;
+		ret->format.location = -1;
+		ret->typid = InvalidOid;
+		ret->typmod = -1;
+
+		return;
+	}
+
+	*ret = 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.type == JS_FORMAT_DEFAULT)
+		/* assign JSONB format when returning jsonb, or JSON format otherwise */
+		ret->format.type =
+			ret->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
+	else
+		checkJsonOutputFormat(pstate, &ret->format, ret->typid, allow_format);
+}
+
+/*
+ * Coerce json[b]-valued function expression to the output type.
+ */
+static Node *
+coerceJsonFuncExpr(ParseState *pstate, Node *expr, 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 ? returning->format.location : -1;
+
+	/* special case for RETURNING bytea FORMAT json */
+	if (returning->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_PG_CONVERT_TO, BYTEAOID,
+										 list_make2(texpr, enc),
+										 InvalidOid, InvalidOid,
+										 COERCE_INTERNAL_CAST);
+		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_INTERNAL_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 JsonCtorOpts *
+makeJsonCtorOpts(const JsonReturning *returning, bool unique,
+				 bool absent_on_null)
+{
+	JsonCtorOpts *opts = makeNode(JsonCtorOpts);
+
+	opts->returning  = *returning;
+	opts->unique = unique;
+	opts->absent_on_null = absent_on_null;
+
+	return opts;
+}
+
+/*
+ * 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 *
+transformJsonObjectCtor(ParseState *pstate, JsonObjectCtor *ctor)
+{
+	JsonReturning returning;
+	FuncExpr   *fexpr;
+	List	   *args = NIL;
+	Oid			funcid;
+	Oid			funcrettype;
+
+	/* transform key-value pairs, if any */
+	if (ctor->exprs)
+	{
+		ListCell   *lc;
+
+		/* append the first two arguments */
+		args = lappend(args, makeBoolConst(ctor->absent_on_null, false));
+		args = lappend(args, makeBoolConst(ctor->unique, false));
+
+		/* 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 = transformJsonValueExprDefault(pstate, kv->value);
+
+			args = lappend(args, key);
+			args = lappend(args, val);
+		}
+	}
+
+	transformJsonOutput(pstate, ctor->output, true, &returning);
+
+	if (returning.format.type == JS_FORMAT_JSONB)
+	{
+		funcid = args ? F_JSONB_BUILD_OBJECT_EXT : F_JSONB_BUILD_OBJECT_NOARGS;
+		funcrettype = JSONBOID;
+	}
+	else
+	{
+		funcid = args ? F_JSON_BUILD_OBJECT_EXT : F_JSON_BUILD_OBJECT_NOARGS;
+		funcrettype = JSONOID;
+	}
+
+	fexpr = makeFuncExpr(funcid, funcrettype, args,
+						 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+	fexpr->location = ctor->location;
+	fexpr->funcformat2 = FUNCFMT_JSON_OBJECT;
+	fexpr->funcformatopts = (Node *) makeJsonCtorOpts(&returning,
+													  ctor->unique,
+													  ctor->absent_on_null);
+
+	return coerceJsonFuncExpr(pstate, (Node *) fexpr, &returning, true);
+}
+
+/*
+ * Transform JSON_ARRAY(query [FORMAT] [RETURNING] [ON NULL]) into
+ *  (SELECT  JSON_ARRAYAGG(a  [FORMAT] [RETURNING] [ON NULL]) FROM (query) q(a))
+ */
+static Node *
+transformJsonArrayQueryCtor(ParseState *pstate, JsonArrayQueryCtor *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->ctor.agg_order = NIL;
+	agg->ctor.output = ctor->output;
+	agg->absent_on_null = ctor->absent_on_null;
+	agg->ctor.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 *
+transformJsonAggCtor(ParseState *pstate, JsonAggCtor *agg_ctor,
+					 JsonReturning *returning, List *args, const char *aggfn,
+					 Oid aggtype, FuncFormat format, JsonCtorOpts *formatopts)
+{
+	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->winformat = format;
+		wfunc->winformatopts = (Node *) formatopts;
+		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->aggformat = format;
+		aggref->aggformatopts = (Node *) formatopts;
+		aggref->location = agg_ctor->location;
+
+		transformAggregateCall(pstate, aggref, args, agg_ctor->agg_order, false);
+
+		node = (Node *) aggref;
+	}
+
+	return coerceJsonFuncExpr(pstate, node, returning, true);
+}
+
+/*
+ * 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;
+
+	transformJsonOutput(pstate, agg->ctor.output, true, &returning);
+
+	key = transformExprRecurse(pstate, (Node *) agg->arg->key);
+	val = transformJsonValueExprDefault(pstate, agg->arg->value);
+
+	args = list_make4(key,
+					  val,
+					  makeBoolConst(agg->absent_on_null, false),
+					  makeBoolConst(agg->unique, false));
+
+	if (returning.format.type == JS_FORMAT_JSONB)
+	{
+		aggfnname = "pg_catalog.jsonb_objectagg"; /* F_JSONB_OBJECTAGG */
+		aggtype = JSONBOID;
+	}
+	else
+	{
+		aggfnname = "pg_catalog.json_objectagg"; /* F_JSON_OBJECTAGG; */
+		aggtype = JSONOID;
+	}
+
+	return transformJsonAggCtor(pstate, &agg->ctor, &returning, args, aggfnname,
+								aggtype, FUNCFMT_JSON_OBJECTAGG,
+								makeJsonCtorOpts(&returning,
+												 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;
+
+	transformJsonOutput(pstate, agg->ctor.output, true, &returning);
+
+	arg = transformJsonValueExprDefault(pstate, agg->arg);
+
+	if (returning.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 transformJsonAggCtor(pstate, &agg->ctor, &returning, list_make1(arg),
+								aggfnname, aggtype, FUNCFMT_JSON_ARRAYAGG,
+								makeJsonCtorOpts(&returning,
+												 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 *
+transformJsonArrayCtor(ParseState *pstate, JsonArrayCtor *ctor)
+{
+	JsonReturning returning;
+	FuncExpr   *fexpr;
+	List	   *args = NIL;
+	Oid			funcid;
+	Oid			funcrettype;
+
+	/* transform element expressions, if any */
+	if (ctor->exprs)
+	{
+		ListCell   *lc;
+
+		/* append the first absent_on_null argument */
+		args = lappend(args, makeBoolConst(ctor->absent_on_null, false));
+
+		/* transform and append element arguments */
+		foreach(lc, ctor->exprs)
+		{
+			JsonValueExpr *jsval = castNode(JsonValueExpr, lfirst(lc));
+			Node	   *val = transformJsonValueExprDefault(pstate, jsval);
+
+			args = lappend(args, val);
+		}
+	}
+
+	transformJsonOutput(pstate, ctor->output, true, &returning);
+
+	if (returning.format.type == JS_FORMAT_JSONB)
+	{
+		funcid = args ? F_JSONB_BUILD_ARRAY_EXT : F_JSONB_BUILD_ARRAY_NOARGS;
+		funcrettype = JSONBOID;
+	}
+	else
+	{
+		funcid = args ? F_JSON_BUILD_ARRAY_EXT : F_JSON_BUILD_ARRAY_NOARGS;
+		funcrettype = JSONOID;
+	}
+
+	fexpr = makeFuncExpr(funcid, funcrettype, args,
+						 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+	fexpr->location = ctor->location;
+	fexpr->funcformat2 = FUNCFMT_JSON_ARRAY;
+	fexpr->funcformatopts = (Node *) makeJsonCtorOpts(&returning, false,
+													  ctor->absent_on_null);
+
+	return coerceJsonFuncExpr(pstate, (Node *) fexpr, &returning, true);
+}
+
+static const char *
+JsonValueTypeStrings[] =
+{
+	"any",
+	"object",
+	"array",
+	"scalar",
+};
+
+static Const *
+makeJsonValueTypeConst(JsonValueType type)
+{
+	return makeConst(TEXTOID, -1, InvalidOid, -1,
+					 PointerGetDatum(cstring_to_text(
+											JsonValueTypeStrings[(int) type])),
+					 false, false);
+}
+
+/*
+ * Transform IS JSON predicate into
+ * json[b]_is_valid(json, value_type [, check_key_uniqueness]) call.
+ */
+static Node *
+transformJsonIsPredicate(ParseState *pstate, JsonIsPredicate *pred)
+{
+	Node	   *expr = transformExprRecurse(pstate, pred->expr);
+	Oid			exprtype = exprType(expr);
+	FuncExpr   *fexpr;
+	JsonIsPredicateOpts *opts;
+
+	/* prepare input document */
+	if (exprtype == BYTEAOID)
+	{
+		expr = makeJsonByteaToTextConversion(expr, &pred->format,
+											 exprLocation(expr));
+		exprtype = TEXTOID;
+	}
+	else
+	{
+		char		typcategory;
+		bool		typispreferred;
+
+		get_type_category_preferred(exprtype, &typcategory, &typispreferred);
+
+		if (exprtype == UNKNOWNOID || typcategory == TYPCATEGORY_STRING)
+		{
+			expr = coerce_to_target_type(pstate, (Node *) expr, exprtype,
+										 TEXTOID, -1,
+										 COERCION_IMPLICIT,
+										 COERCE_IMPLICIT_CAST, -1);
+			exprtype = TEXTOID;
+		}
+
+		if (pred->format.encoding != JS_ENC_DEFAULT)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 parser_errposition(pstate, pred->format.location),
+					 errmsg("cannot use JSON FORMAT ENCODING clause for non-bytea input types")));
+	}
+
+	expr = (Node *) makeJsonValueExpr((Expr *) expr, pred->format);
+
+	/* make resulting expression */
+	if (exprtype == TEXTOID || exprtype == JSONOID)
+	{
+		fexpr = makeFuncExpr(F_JSON_IS_VALID, BOOLOID,
+							 list_make3(expr,
+										makeJsonValueTypeConst(pred->vtype),
+										makeBoolConst(pred->unique_keys, false)),
+							 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+
+		fexpr->location = pred->location;
+	}
+	else if (exprtype == JSONBOID)
+	{
+		/* XXX the following expressions also can be used here:
+		 * jsonb_type(jsonb) = 'type' (for object and array checks)
+		 * CASE jsonb_type(jsonb) WHEN ... END (for scalars checks)
+		 */
+		fexpr = makeFuncExpr(F_JSONB_IS_VALID, BOOLOID,
+							 list_make2(expr,
+										makeJsonValueTypeConst(pred->vtype)),
+							 InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
+
+		fexpr->location = pred->location;
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("cannot use type %s in IS JSON predicate",
+						 format_type_be(exprtype))));
+		return NULL;
+	}
+
+	opts = makeNode(JsonIsPredicateOpts);
+	opts->unique_keys = pred->unique_keys;
+	opts->value_type = pred->vtype;
+
+	fexpr->funcformat2 = FUNCFMT_IS_JSON;
+	fexpr->funcformatopts = (Node *) opts;
+
+	return (Node *) fexpr;
+}
+
+/*
+ * Transform a JSON PASSING clause.
+ */
+static void
+transformJsonPassingArgs(ParseState *pstate, JsonFormatType format, List *args,
+						 JsonPassing *passing)
+{
+	ListCell   *lc;
+
+	passing->values = NIL;
+	passing->names = NIL;
+
+	foreach(lc, args)
+	{
+		JsonArgument *arg = castNode(JsonArgument, lfirst(lc));
+		Node	   *expr = transformJsonValueExprExt(pstate, arg->val,
+													 format, true, NULL);
+
+		assign_expr_collations(pstate, expr);
+
+		passing->values = lappend(passing->values, expr);
+		passing->names = lappend(passing->names, makeString(arg->name));
+	}
+}
+
+/*
+ * Transform a JSON BEHAVIOR clause.
+ */
+static JsonBehavior
+transformJsonBehavior(ParseState *pstate, JsonBehavior *behavior,
+					  JsonBehaviorType default_behavior)
+{
+	JsonBehavior b;
+
+	b.btype = behavior ? behavior->btype : default_behavior;
+	b.default_expr = b.btype != JSON_BEHAVIOR_DEFAULT ? NULL :
+		transformExprRecurse(pstate, behavior->default_expr);
+
+	return b;
+}
+
+/*
+ * Common code for JSON_VALUE, JSON_QUERY, JSON_EXISTS transformation
+ * into a JsonExpr node.
+ */
+static JsonExpr *
+transformJsonExprCommon(ParseState *pstate, JsonFuncExpr *func)
+{
+	JsonExpr   *jsexpr = makeNode(JsonExpr);
+	Node	   *pathspec;
+	JsonFormatType format;
+
+	if (func->common->pathname)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("JSON_TABLE path name is not allowed here"),
+				 parser_errposition(pstate, func->location)));
+
+	jsexpr->location = func->location;
+	jsexpr->op = func->op;
+	jsexpr->formatted_expr = transformJsonValueExprExt(pstate,
+													   func->common->expr,
+													   JS_FORMAT_JSON,
+													   false,
+													   &jsexpr->raw_expr);
+
+	assign_expr_collations(pstate, jsexpr->formatted_expr);
+
+	/* format is determined by context item type */
+	format = exprType(jsexpr->formatted_expr) == JSONBOID ?
+		JS_FORMAT_JSONB : JS_FORMAT_JSON;
+
+	if (jsexpr->formatted_expr == jsexpr->raw_expr)
+		jsexpr->formatted_expr = NULL;
+
+	jsexpr->result_coercion = NULL;
+	jsexpr->omit_quotes = false;
+
+	jsexpr->format = func->common->expr->format;
+
+	pathspec = transformExprRecurse(pstate, func->common->pathspec);
+
+	jsexpr->path_spec =
+		coerce_to_target_type(pstate, pathspec, exprType(pathspec),
+							  JSONPATHOID, -1,
+							  COERCION_EXPLICIT, COERCE_IMPLICIT_CAST,
+							  exprLocation(pathspec));
+	if (!jsexpr->path_spec)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("JSON path expression must be type %s, not type %s",
+						"jsonpath", format_type_be(exprType(pathspec))),
+				 parser_errposition(pstate, exprLocation(pathspec))));
+
+	/* transform and coerce to json[b] passing arguments */
+	transformJsonPassingArgs(pstate, format, func->common->passing,
+							 &jsexpr->passing);
+
+	if (func->op != IS_JSON_EXISTS)
+		jsexpr->on_empty = transformJsonBehavior(pstate, func->on_empty,
+												 JSON_BEHAVIOR_NULL);
+
+	jsexpr->on_error = transformJsonBehavior(pstate, func->on_error,
+											 func->op == IS_JSON_EXISTS ?
+											 JSON_BEHAVIOR_FALSE :
+											 JSON_BEHAVIOR_NULL);
+
+	return jsexpr;
+}
+
+/*
+ * Assign default JSON returning type from the specified format or from
+ * the context item type.
+ */
+static void
+assignDefaultJsonReturningType(Node *context_item, JsonFormat *context_format,
+							   JsonReturning *ret)
+{
+	bool		is_jsonb;
+
+	ret->format = *context_format;
+
+	if (ret->format.type == JS_FORMAT_DEFAULT)
+		is_jsonb = exprType(context_item) == JSONBOID;
+	else
+		is_jsonb = ret->format.type == JS_FORMAT_JSONB;
+
+	ret->typid = is_jsonb ? JSONBOID : JSONOID;
+	ret->typmod = -1;
+}
+
+/*
+ * Try to coerce expression to the output type or
+ * use json_populate_type() for composite, array and domain types or
+ * use coercion via I/O.
+ */
+static JsonCoercion *
+coerceJsonExpr(ParseState *pstate, Node *expr, JsonReturning *returning)
+{
+	char		typtype;
+	JsonCoercion *coercion = makeNode(JsonCoercion);
+
+	coercion->expr = coerceJsonFuncExpr(pstate, expr, returning, false);
+
+	if (coercion->expr)
+	{
+		if (coercion->expr == expr)
+			coercion->expr = NULL;
+
+		return coercion;
+	}
+
+	typtype = get_typtype(returning->typid);
+
+	if (returning->typid == RECORDOID ||
+		typtype == TYPTYPE_COMPOSITE ||
+		typtype == TYPTYPE_DOMAIN ||
+		type_is_array(returning->typid))
+		coercion->via_populate = true;
+	else
+		coercion->via_io = true;
+
+	return coercion;
+}
+
+/*
+ * Transform a JSON output clause of JSON_VALUE, JSON_QUERY, JSON_EXISTS.
+ */
+static void
+transformJsonFuncExprOutput(ParseState *pstate,	JsonFuncExpr *func,
+							JsonExpr *jsexpr)
+{
+	Node	   *expr = jsexpr->formatted_expr ?
+					   jsexpr->formatted_expr : jsexpr->raw_expr;
+
+	transformJsonOutput(pstate, func->output, false, &jsexpr->returning);
+
+	/* JSON_VALUE returns text by default */
+	if (func->op == IS_JSON_VALUE && !OidIsValid(jsexpr->returning.typid))
+	{
+		jsexpr->returning.typid = TEXTOID;
+		jsexpr->returning.typmod = -1;
+	}
+
+	if (OidIsValid(jsexpr->returning.typid))
+	{
+		JsonReturning ret;
+
+		if (func->op == IS_JSON_VALUE &&
+			jsexpr->returning.typid != JSONOID &&
+			jsexpr->returning.typid != JSONBOID)
+		{
+			/* Forced coercion via I/O for JSON_VALUE for non-JSON types */
+			jsexpr->result_coercion = makeNode(JsonCoercion);
+			jsexpr->result_coercion->expr = NULL;
+			jsexpr->result_coercion->via_io = true;
+			return;
+		}
+
+		assignDefaultJsonReturningType(jsexpr->raw_expr, &jsexpr->format, &ret);
+
+		if (ret.typid != jsexpr->returning.typid ||
+			ret.typmod != jsexpr->returning.typmod)
+		{
+			Node	   *placeholder = makeCaseTestExpr(expr);
+
+			Assert(((CaseTestExpr *) placeholder)->typeId == ret.typid);
+			Assert(((CaseTestExpr *) placeholder)->typeMod == ret.typmod);
+
+			jsexpr->result_coercion = coerceJsonExpr(pstate, placeholder,
+													 &jsexpr->returning);
+		}
+	}
+	else
+		assignDefaultJsonReturningType(jsexpr->raw_expr, &jsexpr->format,
+									   &jsexpr->returning);
+}
+
+/*
+ * Coerce a expression in JSON DEFAULT behavior to the target output type.
+ */
+static Node *
+coerceDefaultJsonExpr(ParseState *pstate, JsonExpr *jsexpr, Node *defexpr)
+{
+	int			location;
+	Oid			exprtype;
+
+	if (!defexpr)
+		return NULL;
+
+	exprtype = exprType(defexpr);
+	location = exprLocation(defexpr);
+
+	if (location < 0)
+		location = jsexpr->location;
+
+	defexpr = coerce_to_target_type(pstate,
+									defexpr,
+									exprtype,
+									jsexpr->returning.typid,
+									jsexpr->returning.typmod,
+									COERCION_EXPLICIT,
+									COERCE_INTERNAL_CAST,
+									location);
+
+	if (!defexpr)
+		ereport(ERROR,
+				(errcode(ERRCODE_CANNOT_COERCE),
+				 errmsg("cannot cast DEFAULT expression type %s to %s",
+						format_type_be(exprtype),
+						format_type_be(jsexpr->returning.typid)),
+				 parser_errposition(pstate, location)));
+
+	return defexpr;
+}
+
+/*
+ * Initialize SQL/JSON item coercion from the SQL type "typid" to the target
+ * "returning" type.
+ */
+static JsonCoercion *
+initJsonItemCoercion(ParseState *pstate, Oid typid, JsonReturning *returning)
+{
+	Node	   *expr;
+
+	if (typid == UNKNOWNOID)
+	{
+		expr = (Node *) makeNullConst(UNKNOWNOID, -1, InvalidOid);
+	}
+	else
+	{
+		CaseTestExpr *placeholder = makeNode(CaseTestExpr);
+
+		placeholder->typeId = typid;
+		placeholder->typeMod = -1;
+		placeholder->collation = InvalidOid;
+
+		expr = (Node *) placeholder;
+	}
+
+	return coerceJsonExpr(pstate, expr, returning);
+}
+
+static void
+initJsonItemCoercions(ParseState *pstate, JsonItemCoercions *coercions,
+					  JsonReturning *returning, Oid contextItemTypeId)
+{
+	struct
+	{
+		JsonCoercion **coercion;
+		Oid			typid;
+	}		   *p,
+				coercionTypids[] =
+				{
+					{ &coercions->null, UNKNOWNOID },
+					{ &coercions->string, TEXTOID },
+					{ &coercions->numeric, NUMERICOID },
+					{ &coercions->boolean, BOOLOID },
+					{ &coercions->date, DATEOID },
+					{ &coercions->time, TIMEOID },
+					{ &coercions->timetz, TIMETZOID },
+					{ &coercions->timestamp, TIMESTAMPOID },
+					{ &coercions->timestamptz, TIMESTAMPTZOID },
+					{ &coercions->composite, contextItemTypeId },
+					{ NULL, InvalidOid }
+				};
+
+	for (p = coercionTypids; p->coercion; p++)
+		*p->coercion = initJsonItemCoercion(pstate, p->typid, returning);
+}
+
+/*
+ * Transform JSON_VALUE, JSON_QUERY, JSON_EXISTS functions into a JsonExpr node.
+ */
+static Node *
+transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
+{
+	JsonExpr   *jsexpr = transformJsonExprCommon(pstate, func);
+	Node	   *contextItemExpr =
+		jsexpr->formatted_expr ? jsexpr->formatted_expr : jsexpr->raw_expr;
+	const char *func_name = NULL;
+
+	switch (func->op)
+	{
+		case IS_JSON_VALUE:
+			func_name = "JSON_VALUE";
+
+			transformJsonFuncExprOutput(pstate, func, jsexpr);
+
+			jsexpr->returning.format.type = JS_FORMAT_DEFAULT;
+			jsexpr->returning.format.encoding = JS_ENC_DEFAULT;
+
+			jsexpr->on_empty.default_expr =
+					coerceDefaultJsonExpr(pstate, jsexpr,
+										  jsexpr->on_empty.default_expr);
+
+			jsexpr->on_error.default_expr =
+					coerceDefaultJsonExpr(pstate, jsexpr,
+										  jsexpr->on_error.default_expr);
+
+			jsexpr->coercions = makeNode(JsonItemCoercions);
+			initJsonItemCoercions(pstate, jsexpr->coercions, &jsexpr->returning,
+								  exprType(contextItemExpr));
+
+			break;
+
+		case IS_JSON_QUERY:
+			func_name = "JSON_QUERY";
+
+			transformJsonFuncExprOutput(pstate, func, jsexpr);
+
+			jsexpr->wrapper = func->wrapper;
+			jsexpr->omit_quotes = func->omit_quotes;
+
+			break;
+
+		case IS_JSON_EXISTS:
+			func_name = "JSON_EXISTS";
+
+			jsexpr->returning.format.type = JS_FORMAT_DEFAULT;
+			jsexpr->returning.format.encoding = JS_ENC_DEFAULT;
+			jsexpr->returning.format.location = -1;
+			jsexpr->returning.typid = BOOLOID;
+			jsexpr->returning.typmod = -1;
+
+			break;
+	}
+
+	if (exprType(contextItemExpr) != JSONBOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("%s() is not yet implemented for json type", func_name),
+				 parser_errposition(pstate, func->location)));
+
+	return (Node *) jsexpr;
+}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 4932e58..7a43515 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1918,6 +1918,34 @@ FigureColnameInternal(Node *node, char **name)
 		case T_XmlSerialize:
 			*name = "xmlserialize";
 			return 2;
+		case T_JsonObjectCtor:
+			*name = "json_object";
+			return 2;
+		case T_JsonArrayCtor:
+		case T_JsonArrayQueryCtor:
+			*name = "json_array";
+			return 2;
+		case T_JsonObjectAgg:
+			*name = "json_objectagg";
+			return 2;
+		case T_JsonArrayAgg:
+			*name = "json_arrayagg";
+			return 2;
+		case T_JsonFuncExpr:
+			/* make SQL/JSON functions act like a regular function */
+			switch (((JsonFuncExpr *) node)->op)
+			{
+				case IS_JSON_QUERY:
+					*name = "json_query";
+					return 2;
+				case IS_JSON_VALUE:
+					*name = "json_value";
+					return 2;
+				case IS_JSON_EXISTS:
+					*name = "json_exists";
+					return 2;
+			}
+			break;
 		default:
 			break;
 	}
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index db30483..3be9d6c 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -24,7 +24,6 @@
 #include "parser/gramparse.h"
 #include "parser/parser.h"
 
-
 /*
  * raw_parser
  *		Given a query in string form, do lexical and grammatical analysis.
@@ -117,6 +116,9 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
 		case WITH:
 			cur_token_length = 4;
 			break;
+		case WITHOUT:
+			cur_token_length = 7;
+			break;
 		default:
 			return cur_token;
 	}
@@ -188,8 +190,22 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
 				case ORDINALITY:
 					cur_token = WITH_LA;
 					break;
+				case UNIQUE:
+					cur_token = WITH_LA_UNIQUE;
+					break;
 			}
 			break;
+
+		case WITHOUT:
+			/* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
+			switch (next_token)
+			{
+				case TIME:
+					cur_token = WITHOUT_LA;
+					break;
+			}
+			break;
+
 	}
 
 	return cur_token;
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index b19d7b1..4f9da97 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -13,6 +13,7 @@
  */
 #include "postgres.h"
 
+#include "access/hash.h"
 #include "access/htup_details.h"
 #include "access/transam.h"
 #include "catalog/pg_type.h"
@@ -66,6 +67,23 @@ typedef enum					/* type categories for datum_to_json */
 	JSONTYPE_OTHER				/* all else */
 } JsonTypeCategory;
 
+/* Context for key uniqueness check */
+typedef struct JsonUniqueCheckContext
+{
+	struct JsonKeyInfo
+	{
+		int			offset;				/* key offset:
+										 *   in result if positive,
+										 *   in skipped_keys if negative */
+		int			length;				/* key length */
+	}		   *keys;					/* key info array */
+	int			nkeys;					/* number of processed keys */
+	int			nallocated;				/* number of allocated keys in array */
+	StringInfo	result;					/* resulting json */
+	StringInfoData skipped_keys;		/* skipped keys with NULL values */
+	MemoryContext mcxt;					/* context for saving skipped keys */
+} JsonUniqueCheckContext;
+
 typedef struct JsonAggState
 {
 	StringInfo	str;
@@ -73,8 +91,23 @@ typedef struct JsonAggState
 	Oid			key_output_func;
 	JsonTypeCategory val_category;
 	Oid			val_output_func;
+	JsonUniqueCheckContext unique_check;
 } JsonAggState;
 
+/* Element of object stack for key uniqueness check */
+typedef struct JsonObjectFields
+{
+	struct JsonObjectFields *parent;
+	HTAB	   *fields;
+} JsonObjectFields;
+
+/* State for key uniqueness check */
+typedef struct JsonUniqueState
+{
+	JsonLexContext *lex;
+	JsonObjectFields *stack;
+} JsonUniqueState;
+
 static inline void json_lex(JsonLexContext *lex);
 static inline void json_lex_string(JsonLexContext *lex);
 static inline void json_lex_number(JsonLexContext *lex, char *s,
@@ -1962,8 +1995,8 @@ to_json(PG_FUNCTION_ARGS)
  *
  * 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;
@@ -2003,9 +2036,14 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 	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))
 	{
@@ -2017,7 +2055,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 	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))
 	{
@@ -2035,6 +2073,25 @@ json_agg_transfn(PG_FUNCTION_ARGS)
 	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
  */
@@ -2058,18 +2115,115 @@ json_agg_finalfn(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
 }
 
+static inline void
+json_unique_check_init(JsonUniqueCheckContext *cxt,
+					   StringInfo result, int nkeys)
+{
+	cxt->mcxt = CurrentMemoryContext;
+	cxt->nkeys = 0;
+	cxt->nallocated = nkeys ? nkeys : 16;
+	cxt->keys = palloc(sizeof(*cxt->keys) * cxt->nallocated);
+	cxt->result = result;
+	cxt->skipped_keys.data = NULL;
+}
+
+static inline void
+json_unique_check_free(JsonUniqueCheckContext *cxt)
+{
+	if (cxt->keys)
+		pfree(cxt->keys);
+
+	if (cxt->skipped_keys.data)
+		pfree(cxt->skipped_keys.data);
+}
+
+/* On-demand initialization of skipped_keys StringInfo structure */
+static inline StringInfo
+json_unique_check_get_skipped_keys(JsonUniqueCheckContext *cxt)
+{
+	StringInfo	out = &cxt->skipped_keys;
+
+	if (!out->data)
+	{
+		MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
+		initStringInfo(out);
+		MemoryContextSwitchTo(oldcxt);
+	}
+
+	return out;
+}
+
+/*
+ * Save current key offset (key is not yet appended) to the key list, key
+ * length is saved later in json_unique_check_key() when the key is appended.
+ */
+static inline void
+json_unique_check_save_key_offset(JsonUniqueCheckContext *cxt, StringInfo out)
+{
+	if (cxt->nkeys >= cxt->nallocated)
+	{
+		cxt->nallocated *= 2;
+		cxt->keys = repalloc(cxt->keys, sizeof(*cxt->keys) * cxt->nallocated);
+	}
+
+	cxt->keys[cxt->nkeys++].offset = out->len;
+}
+
+/*
+ * Check uniqueness of key already appended to 'out' StringInfo.
+ */
+static inline void
+json_unique_check_key(JsonUniqueCheckContext *cxt, StringInfo out)
+{
+	struct JsonKeyInfo *keys = cxt->keys;
+	int			curr = cxt->nkeys - 1;
+	int			offset = keys[curr].offset;
+	int			length = out->len - offset;
+	char	   *curr_key = &out->data[offset];
+	int			i;
+
+	keys[curr].length = length; /* save current key length */
+
+	if (out == &cxt->skipped_keys)
+		/* invert offset for skipped keys for their recognition */
+		keys[curr].offset = -keys[curr].offset;
+
+	/* check collisions with previous keys */
+	for (i = 0; i < curr; i++)
+	{
+		char	   *prev_key;
+
+		if (cxt->keys[i].length != length)
+			continue;
+
+		offset = cxt->keys[i].offset;
+
+		prev_key = offset > 0
+				? &cxt->result->data[offset]
+				: &cxt->skipped_keys.data[-offset];
+
+		if (!memcmp(curr_key, prev_key, length))
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
+					 errmsg("duplicate JSON key %s", curr_key)));
+	}
+}
+
 /*
  * 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;
 
 	if (!AggCheckCallContext(fcinfo, &aggcontext))
 	{
@@ -2090,6 +2244,10 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
 		oldcontext = MemoryContextSwitchTo(aggcontext);
 		state = (JsonAggState *) palloc(sizeof(JsonAggState));
 		state->str = makeStringInfo();
+		if (unique_keys)
+			json_unique_check_init(&state->unique_check, state->str, 0);
+		else
+			memset(&state->unique_check, 0, sizeof(state->unique_check));
 		MemoryContextSwitchTo(oldcontext);
 
 		arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
@@ -2117,7 +2275,6 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
 	else
 	{
 		state = (JsonAggState *) PG_GETARG_POINTER(0);
-		appendStringInfoString(state->str, ", ");
 	}
 
 	/*
@@ -2133,11 +2290,41 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("field name must not be null")));
 
+	/* Skip null values if absent_on_null */
+	skip = absent_on_null && PG_ARGISNULL(2);
+
+	if (skip)
+	{
+		/* If key uniqueness check is needed we must save skipped keys */
+		if (!unique_keys)
+			PG_RETURN_POINTER(state);
+
+		out = json_unique_check_get_skipped_keys(&state->unique_check);
+	}
+	else
+	{
+		out = state->str;
+
+		if (out->len > 2)
+			appendStringInfoString(out, ", ");
+	}
+
 	arg = PG_GETARG_DATUM(1);
 
-	datum_to_json(arg, false, state->str, state->key_category,
+	if (unique_keys)
+		json_unique_check_save_key_offset(&state->unique_check, out);
+
+	datum_to_json(arg, false, out, state->key_category,
 				  state->key_output_func, true);
 
+	if (unique_keys)
+	{
+		json_unique_check_key(&state->unique_check, out);
+
+		if (skip)
+			PG_RETURN_POINTER(state);
+	}
+
 	appendStringInfoString(state->str, " : ");
 
 	if (PG_ARGISNULL(2))
@@ -2152,6 +2339,26 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
 }
 
 /*
+ * json_object_agg aggregate function
+ */
+Datum
+json_object_agg_transfn(PG_FUNCTION_ARGS)
+{
+	return json_object_agg_transfn_worker(fcinfo, false, false);
+}
+
+/*
+ * json_objectagg aggregate function
+ */
+Datum
+json_objectagg_transfn(PG_FUNCTION_ARGS)
+{
+	return json_object_agg_transfn_worker(fcinfo,
+										  PG_GETARG_BOOL(3),
+										  PG_GETARG_BOOL(4));
+}
+
+/*
  * json_object_agg final function.
  */
 Datum
@@ -2168,6 +2375,8 @@ json_object_agg_finalfn(PG_FUNCTION_ARGS)
 	if (state == NULL)
 		PG_RETURN_NULL();
 
+	json_unique_check_free(&state->unique_check);
+
 	/* Else return state with appropriate object terminator added */
 	PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
 }
@@ -2192,11 +2401,9 @@ catenate_stringinfo_string(StringInfo buffer, const char *addon)
 	return result;
 }
 
-/*
- * SQL function json_build_object(variadic "any")
- */
-Datum
-json_build_object(PG_FUNCTION_ARGS)
+static Datum
+json_build_object_worker(FunctionCallInfo fcinfo, int first_vararg,
+						 bool absent_on_null, bool unique_keys)
 {
 	int			nargs = PG_NARGS();
 	int			i;
@@ -2205,9 +2412,11 @@ json_build_object(PG_FUNCTION_ARGS)
 	Datum	   *args;
 	bool	   *nulls;
 	Oid		   *types;
+	JsonUniqueCheckContext unique_check;
 
 	/* fetch argument values to build the object */
-	nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
+	nargs = extract_variadic_args(fcinfo, first_vararg, false,
+								  &args, &types, &nulls);
 
 	if (nargs < 0)
 		PG_RETURN_NULL();
@@ -2222,19 +2431,53 @@ json_build_object(PG_FUNCTION_ARGS)
 
 	appendStringInfoChar(result, '{');
 
+	if (unique_keys)
+		json_unique_check_init(&unique_check, result, nargs / 2);
+
 	for (i = 0; i < nargs; i += 2)
 	{
-		appendStringInfoString(result, sep);
-		sep = ", ";
+		StringInfo	out;
+		bool		skip;
+
+		/* 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_check_get_skipped_keys(&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),
+					 errmsg("argument %d cannot be null", first_vararg + i + 1),
 					 errhint("Object keys should be text.")));
 
-		add_json(args[i], false, result, types[i], true);
+		if (unique_keys)
+			/* save key offset before key appending */
+			json_unique_check_save_key_offset(&unique_check, out);
+
+		add_json(args[i], false, out, types[i], true);
+
+		if (unique_keys)
+		{
+			/* check key uniqueness after key appending */
+			json_unique_check_key(&unique_check, out);
+
+			if (skip)
+				continue;
+		}
 
 		appendStringInfoString(result, " : ");
 
@@ -2244,23 +2487,43 @@ json_build_object(PG_FUNCTION_ARGS)
 
 	appendStringInfoChar(result, '}');
 
+	if (unique_keys)
+		json_unique_check_free(&unique_check);
+
 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
 }
 
 /*
- * degenerate case of json_build_object where it gets 0 arguments.
+ * SQL function json_build_object(variadic "any")
  */
 Datum
-json_build_object_noargs(PG_FUNCTION_ARGS)
+json_build_object(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
+	return json_build_object_worker(fcinfo, 0, false, false);
 }
 
 /*
- * SQL function json_build_array(variadic "any")
+ * SQL function json_build_object_ext(absent_on_null bool, unique bool, variadic "any")
  */
 Datum
-json_build_array(PG_FUNCTION_ARGS)
+json_build_object_ext(PG_FUNCTION_ARGS)
+{
+	return json_build_object_worker(fcinfo, 2,
+									PG_GETARG_BOOL(0), PG_GETARG_BOOL(1));
+}
+
+/*
+ * degenerate case of json_build_object where it gets 0 arguments.
+ */
+Datum
+json_build_object_noargs(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
+}
+
+static Datum
+json_build_array_worker(FunctionCallInfo fcinfo, int first_vararg,
+						bool absent_on_null)
 {
 	int			nargs;
 	int			i;
@@ -2271,7 +2534,8 @@ json_build_array(PG_FUNCTION_ARGS)
 	Oid		   *types;
 
 	/* fetch argument values to build the array */
-	nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
+	nargs = extract_variadic_args(fcinfo, first_vararg, false,
+								  &args, &types, &nulls);
 
 	if (nargs < 0)
 		PG_RETURN_NULL();
@@ -2282,6 +2546,9 @@ json_build_array(PG_FUNCTION_ARGS)
 
 	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);
@@ -2293,6 +2560,24 @@ json_build_array(PG_FUNCTION_ARGS)
 }
 
 /*
+ * SQL function json_build_array(variadic "any")
+ */
+Datum
+json_build_array(PG_FUNCTION_ARGS)
+{
+	return json_build_array_worker(fcinfo, 0, false);
+}
+
+/*
+ * SQL function json_build_array_ext(absent_on_null bool, variadic "any")
+ */
+Datum
+json_build_array_ext(PG_FUNCTION_ARGS)
+{
+	return json_build_array_worker(fcinfo, 1, PG_GETARG_BOOL(0));
+}
+
+/*
  * degenerate case of json_build_array where it gets 0 arguments.
  */
 Datum
@@ -2523,6 +2808,178 @@ escape_json(StringInfo buf, const char *str)
 	appendStringInfoCharMacro(buf, '"');
 }
 
+/* Functions implementing hash table for key uniqueness check */
+static int
+json_unique_hash_match(const void *key1, const void *key2, Size keysize)
+{
+	return strcmp(*(const char **) key1, *(const char **) key2);
+}
+
+static void *
+json_unique_hash_keycopy(void *dest, const void *src, Size keysize)
+{
+	*(const char **) dest = pstrdup(*(const char **) src);
+
+	return dest;
+}
+
+static uint32
+json_unique_hash(const void *key, Size keysize)
+{
+	const char *s = *(const char **) key;
+
+	return DatumGetUInt32(hash_any((const unsigned char *) s, (int) strlen(s)));
+}
+
+/* Semantic actions for key uniqueness check */
+static void
+json_unique_object_start(void *_state)
+{
+	JsonUniqueState *state = _state;
+	JsonObjectFields *obj = palloc(sizeof(*obj));
+	HASHCTL		ctl;
+
+	memset(&ctl, 0, sizeof(ctl));
+	ctl.keysize = sizeof(char *);
+	ctl.entrysize = sizeof(char *);
+	ctl.hcxt = CurrentMemoryContext;
+	ctl.hash = json_unique_hash;
+	ctl.keycopy = json_unique_hash_keycopy;
+	ctl.match = json_unique_hash_match;
+	obj->fields = hash_create("json object hashtable",
+							  32,
+							  &ctl,
+							  HASH_ELEM | HASH_CONTEXT |
+							  HASH_FUNCTION | HASH_COMPARE | HASH_KEYCOPY);
+	obj->parent = state->stack;		/* push object to stack */
+
+	state->stack = obj;
+}
+
+static void
+json_unique_object_end(void *_state)
+{
+	JsonUniqueState *state = _state;
+
+	hash_destroy(state->stack->fields);
+
+	state->stack = state->stack->parent;	/* pop object from stack */
+}
+
+static void
+json_unique_object_field_start(void *_state, char *field, bool isnull)
+{
+	JsonUniqueState *state = _state;
+	bool		found;
+
+	/* find key collision in the current object */
+	(void) hash_search(state->stack->fields, &field, HASH_ENTER, &found);
+
+	if (found)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("duplicate JSON key \"%s\"", field),
+				 report_json_context(state->lex)));
+}
+
+/*
+ * json_is_valid -- check json text validity, its value type and key uniqueness
+ */
+Datum
+json_is_valid(PG_FUNCTION_ARGS)
+{
+	text	   *json = PG_GETARG_TEXT_P(0);
+	text	   *type = PG_GETARG_TEXT_P(1);
+	bool		unique = PG_GETARG_BOOL(2);
+	MemoryContext mcxt = CurrentMemoryContext;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+
+	if (!PG_ARGISNULL(1) &&
+		strncmp("any", VARDATA(type), VARSIZE_ANY_EXHDR(type)))
+	{
+		JsonLexContext *lex;
+		JsonTokenType tok;
+
+		lex = makeJsonLexContext(json, false);
+
+		/* Lex exactly one token from the input and check its type. */
+		PG_TRY();
+		{
+			json_lex(lex);
+		}
+		PG_CATCH();
+		{
+			if (ERRCODE_TO_CATEGORY(geterrcode()) == ERRCODE_DATA_EXCEPTION)
+			{
+				FlushErrorState();
+				MemoryContextSwitchTo(mcxt);
+				PG_RETURN_BOOL(false);	/* invalid json */
+			}
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+
+		tok = lex_peek(lex);
+
+		if (!strncmp("object", VARDATA(type), VARSIZE_ANY_EXHDR(type)))
+		{
+			if (tok != JSON_TOKEN_OBJECT_START)
+				PG_RETURN_BOOL(false);	/* json is not a object */
+		}
+		else if (!strncmp("array", VARDATA(type), VARSIZE_ANY_EXHDR(type)))
+		{
+			if (tok != JSON_TOKEN_ARRAY_START)
+				PG_RETURN_BOOL(false);	/* json is not an array */
+		}
+		else
+		{
+			if (tok == JSON_TOKEN_OBJECT_START ||
+				tok == JSON_TOKEN_ARRAY_START)
+				PG_RETURN_BOOL(false);	/* json is not a scalar */
+		}
+	}
+
+	/* do full parsing pass only for uniqueness check or JSON text validation */
+	if (unique ||
+		get_fn_expr_argtype(fcinfo->flinfo, 0) != JSONOID)
+	{
+		JsonLexContext *lex = makeJsonLexContext(json, unique);
+		JsonSemAction uniqueSemAction = {0};
+		JsonUniqueState state;
+
+		if (unique)
+		{
+			state.lex = lex;
+			state.stack = NULL;
+
+			uniqueSemAction.semstate = &state;
+			uniqueSemAction.object_start = json_unique_object_start;
+			uniqueSemAction.object_field_start = json_unique_object_field_start;
+			uniqueSemAction.object_end = json_unique_object_end;
+		}
+
+		PG_TRY();
+		{
+			pg_parse_json(lex, unique ? &uniqueSemAction : &nullSemAction);
+		}
+		PG_CATCH();
+		{
+			if (ERRCODE_TO_CATEGORY(geterrcode()) == ERRCODE_DATA_EXCEPTION)
+			{
+				FlushErrorState();
+				MemoryContextSwitchTo(mcxt);
+				PG_RETURN_BOOL(false);	/* invalid json or key collision found */
+			}
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+	}
+
+	PG_RETURN_BOOL(true);	/* ok */
+}
+
 /*
  * SQL function json_typeof(json) -> text
  *
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 9d0582c..6f20946 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -52,6 +52,16 @@ typedef enum					/* type categories for datum_to_jsonb */
 	JSONBTYPE_OTHER				/* all else */
 } JsonbTypeCategory;
 
+/* Context for key uniqueness check */
+typedef struct JsonbUniqueCheckContext
+{
+	JsonbValue *obj;				/* object containing skipped keys also */
+	int		   *skipped_keys;		/* array of skipped key-value pair indices */
+	int			skipped_keys_allocated;
+	int			skipped_keys_count;
+	MemoryContext mcxt;				/* context for saving skipped keys */
+} JsonbUniqueCheckContext;
+
 typedef struct JsonbAggState
 {
 	JsonbInState *res;
@@ -59,6 +69,7 @@ typedef struct JsonbAggState
 	Oid			key_output_func;
 	JsonbTypeCategory val_category;
 	Oid			val_output_func;
+	JsonbUniqueCheckContext unique_check;
 } JsonbAggState;
 
 static inline Datum jsonb_from_cstring(char *json, int len);
@@ -1121,11 +1132,121 @@ to_jsonb(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
 }
 
+static inline void
+jsonb_unique_check_init(JsonbUniqueCheckContext *cxt, JsonbValue *obj,
+						MemoryContext mcxt)
+{
+	cxt->mcxt = mcxt;
+	cxt->obj = obj;
+	cxt->skipped_keys = NULL;
+	cxt->skipped_keys_count = 0;
+	cxt->skipped_keys_allocated = 0;
+}
+
 /*
- * SQL function jsonb_build_object(variadic "any")
+ * Save the index of the skipped key-value pair that has just been appended
+ * to the object.
  */
-Datum
-jsonb_build_object(PG_FUNCTION_ARGS)
+static inline void
+jsonb_unique_check_add_skipped(JsonbUniqueCheckContext *cxt)
+{
+	/*
+	 * Make a room for the skipped index plus one additional index
+	 * (see jsonb_unique_check_remove_skipped_keys()).
+	 */
+	if (cxt->skipped_keys_count + 1 >= cxt->skipped_keys_allocated)
+	{
+		if (cxt->skipped_keys_allocated)
+		{
+			cxt->skipped_keys_allocated *= 2;
+			cxt->skipped_keys = repalloc(cxt->skipped_keys,
+										 sizeof(*cxt->skipped_keys) *
+										 cxt->skipped_keys_allocated);
+		}
+		else
+		{
+			cxt->skipped_keys_allocated = 16;
+			cxt->skipped_keys = MemoryContextAlloc(cxt->mcxt,
+												   sizeof(*cxt->skipped_keys) *
+												   cxt->skipped_keys_allocated);
+		}
+	}
+
+	cxt->skipped_keys[cxt->skipped_keys_count++] = cxt->obj->val.object.nPairs;
+}
+
+/*
+ * Check uniqueness of the key that has just been appended to the object.
+ */
+static inline void
+jsonb_unique_check_key(JsonbUniqueCheckContext *cxt, bool skip)
+{
+	JsonbPair *pair = cxt->obj->val.object.pairs;
+	/* nPairs is incremented only after the value is appended */
+	JsonbPair *last = &pair[cxt->obj->val.object.nPairs];
+
+	for (; pair < last; pair++)
+		if (pair->key.val.string.len ==
+			last->key.val.string.len &&
+			!memcmp(pair->key.val.string.val,
+					last->key.val.string.val,
+					last->key.val.string.len))
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
+					 errmsg("duplicate JSON key \"%*s\"",
+							last->key.val.string.len,
+							last->key.val.string.val)));
+
+	if (skip)
+	{
+		/* save skipped key index */
+		jsonb_unique_check_add_skipped(cxt);
+
+		/* add dummy null value for the skipped key */
+		last->value.type = jbvNull;
+		cxt->obj->val.object.nPairs++;
+	}
+}
+
+/*
+ * Remove skipped key-value pairs from the resulting object.
+ */
+static void
+jsonb_unique_check_remove_skipped_keys(JsonbUniqueCheckContext *cxt)
+{
+	int		   *skipped_keys = cxt->skipped_keys;
+	int			skipped_keys_count = cxt->skipped_keys_count;
+
+	if (!skipped_keys_count)
+		return;
+
+	if (cxt->obj->val.object.nPairs > skipped_keys_count)
+	{
+		/* remove skipped key-value pairs */
+		JsonbPair  *pairs = cxt->obj->val.object.pairs;
+		int			i;
+
+		/* save total pair count into the last element of skipped_keys */
+		Assert(cxt->skipped_keys_count < cxt->skipped_keys_allocated);
+		cxt->skipped_keys[cxt->skipped_keys_count] = cxt->obj->val.object.nPairs;
+
+		for (i = 0; i < skipped_keys_count; i++)
+		{
+			int			skipped_key = skipped_keys[i];
+			int			nkeys = skipped_keys[i + 1] - skipped_key - 1;
+
+			memmove(&pairs[skipped_key - i],
+					&pairs[skipped_key + 1],
+					sizeof(JsonbPair) * nkeys);
+		}
+	}
+
+	cxt->obj->val.object.nPairs -= skipped_keys_count;
+}
+
+static Datum
+jsonb_build_object_worker(FunctionCallInfo fcinfo, int first_vararg,
+						  bool absent_on_null, bool unique_keys)
 {
 	int			nargs;
 	int			i;
@@ -1133,9 +1254,11 @@ jsonb_build_object(PG_FUNCTION_ARGS)
 	Datum	   *args;
 	bool	   *nulls;
 	Oid		   *types;
+	JsonbUniqueCheckContext unique_check;
 
 	/* build argument values to build the object */
-	nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+	nargs = extract_variadic_args(fcinfo, first_vararg, true,
+								  &args, &types, &nulls);
 
 	if (nargs < 0)
 		PG_RETURN_NULL();
@@ -1150,26 +1273,69 @@ jsonb_build_object(PG_FUNCTION_ARGS)
 
 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
 
+	/* if (unique_keys) */
+	jsonb_unique_check_init(&unique_check, result.res, CurrentMemoryContext);
+
 	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)));
+					 errmsg("argument %d: key must not be null",
+							first_vararg + 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);
 
+		if (unique_keys)
+		{
+			jsonb_unique_check_key(&unique_check, skip);
+
+			if (skip)
+				continue;	/* do not process the value if the key is skipped */
+		}
+
 		/* process value */
 		add_jsonb(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
 	}
 
+	if (unique_keys && absent_on_null)
+		jsonb_unique_check_remove_skipped_keys(&unique_check);
+
 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
 
 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
 }
 
 /*
+ * SQL function jsonb_build_object(variadic "any")
+ */
+Datum
+jsonb_build_object(PG_FUNCTION_ARGS)
+{
+	return jsonb_build_object_worker(fcinfo, 0, false, false);
+}
+
+/*
+ * SQL function jsonb_build_object_ext(absent_on_null bool, unique bool, variadic "any")
+ */
+Datum
+jsonb_build_object_ext(PG_FUNCTION_ARGS)
+{
+	return jsonb_build_object_worker(fcinfo, 2,
+									 PG_GETARG_BOOL(0), PG_GETARG_BOOL(1));
+}
+
+/*
  * degenerate case of jsonb_build_object where it gets 0 arguments.
  */
 Datum
@@ -1185,11 +1351,9 @@ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
 }
 
-/*
- * SQL function jsonb_build_array(variadic "any")
- */
-Datum
-jsonb_build_array(PG_FUNCTION_ARGS)
+static Datum
+jsonb_build_array_worker(FunctionCallInfo fcinfo, int first_vararg,
+						 bool absent_on_null)
 {
 	int			nargs;
 	int			i;
@@ -1199,7 +1363,8 @@ jsonb_build_array(PG_FUNCTION_ARGS)
 	Oid		   *types;
 
 	/* build argument values to build the array */
-	nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+	nargs = extract_variadic_args(fcinfo, first_vararg, true,
+								  &args, &types, &nulls);
 
 	if (nargs < 0)
 		PG_RETURN_NULL();
@@ -1209,7 +1374,12 @@ jsonb_build_array(PG_FUNCTION_ARGS)
 	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);
 
@@ -1217,6 +1387,24 @@ jsonb_build_array(PG_FUNCTION_ARGS)
 }
 
 /*
+ * SQL function jsonb_build_array(variadic "any")
+ */
+Datum
+jsonb_build_array(PG_FUNCTION_ARGS)
+{
+	return jsonb_build_array_worker(fcinfo, 0, false);
+}
+
+/*
+ * SQL function jsonb_build_array_ext(absent_on_null bool, variadic "any")
+ */
+Datum
+jsonb_build_array_ext(PG_FUNCTION_ARGS)
+{
+	return jsonb_build_array_worker(fcinfo, 1, PG_GETARG_BOOL(0));
+}
+
+/*
  * degenerate case of jsonb_build_array where it gets 0 arguments.
  */
 Datum
@@ -1467,12 +1655,8 @@ clone_parse_state(JsonbParseState *state)
 	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;
@@ -1520,6 +1704,9 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
 		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);
@@ -1589,6 +1776,24 @@ jsonb_agg_transfn(PG_FUNCTION_ARGS)
 	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)
 {
@@ -1621,11 +1826,9 @@ 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;
@@ -1639,6 +1842,7 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
 			   *jbval;
 	JsonbValue	v;
 	JsonbIteratorToken type;
+	bool		skip;
 
 	if (!AggCheckCallContext(fcinfo, &aggcontext))
 	{
@@ -1658,6 +1862,11 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
 		state->res = result;
 		result->res = pushJsonbValue(&result->parseState,
 									 WJB_BEGIN_OBJECT, NULL);
+		if (unique_keys)
+			jsonb_unique_check_init(&state->unique_check, result->res,
+									aggcontext);
+		else
+			memset(&state->unique_check, 0, sizeof(state->unique_check));
 		MemoryContextSwitchTo(oldcontext);
 
 		arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
@@ -1693,6 +1902,15 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
 				(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));
@@ -1748,6 +1966,18 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
 				}
 				result->res = pushJsonbValue(&result->parseState,
 											 WJB_KEY, &v);
+
+				if (unique_keys)
+				{
+					jsonb_unique_check_key(&state->unique_check, skip);
+
+					if (skip)
+					{
+						MemoryContextSwitchTo(oldcontext);
+						PG_RETURN_POINTER(state);
+					}
+				}
+
 				break;
 			case WJB_END_ARRAY:
 				break;
@@ -1820,6 +2050,26 @@ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
 	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_objectagg aggregate function
+ */
+Datum
+jsonb_objectagg_transfn(PG_FUNCTION_ARGS)
+{
+	return jsonb_object_agg_transfn_worker(fcinfo,
+										   PG_GETARG_BOOL(3),
+										   PG_GETARG_BOOL(4));
+}
+
 Datum
 jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
 {
@@ -1855,6 +2105,41 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
 
 
 /*
+ * jsonb_is_valid -- check jsonb value type
+ */
+Datum
+jsonb_is_valid(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
+	text	   *type = PG_GETARG_TEXT_P(1);
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+
+	if (!PG_ARGISNULL(1) &&
+		strncmp("any", VARDATA(type), VARSIZE_ANY_EXHDR(type)))
+	{
+		if (!strncmp("object", VARDATA(type), VARSIZE_ANY_EXHDR(type)))
+		{
+			if (!JB_ROOT_IS_OBJECT(jb))
+				PG_RETURN_BOOL(false);
+		}
+		else if (!strncmp("array", VARDATA(type), VARSIZE_ANY_EXHDR(type)))
+		{
+			if (!JB_ROOT_IS_ARRAY(jb) || JB_ROOT_IS_SCALAR(jb))
+				PG_RETURN_BOOL(false);
+		}
+		else
+		{
+			if (!JB_ROOT_IS_ARRAY(jb) || !JB_ROOT_IS_SCALAR(jb))
+				PG_RETURN_BOOL(false);
+		}
+	}
+
+	PG_RETURN_BOOL(true);
+}
+
+/*
  * Extract scalar value from raw-scalar pseudo-array jsonb.
  */
 JsonbValue *
@@ -2051,3 +2336,65 @@ jsonb_float8(PG_FUNCTION_ARGS)
 
 	PG_RETURN_DATUM(retValue);
 }
+
+/*
+ * Construct an empty array jsonb.
+ */
+Jsonb *
+JsonbMakeEmptyArray(void)
+{
+	JsonbValue jbv;
+
+	jbv.type = jbvArray;
+	jbv.val.array.elems = NULL;
+	jbv.val.array.nElems = 0;
+	jbv.val.array.rawScalar = false;
+
+	return JsonbValueToJsonb(&jbv);
+}
+
+/*
+ * Construct an empty object jsonb.
+ */
+Jsonb *
+JsonbMakeEmptyObject(void)
+{
+	JsonbValue jbv;
+
+	jbv.type = jbvObject;
+	jbv.val.object.pairs = NULL;
+	jbv.val.object.nPairs = 0;
+
+	return JsonbValueToJsonb(&jbv);
+}
+
+/*
+ * Convert jsonb to a C-string stripping quotes from scalar strings.
+ */
+char *
+JsonbUnquote(Jsonb *jb)
+{
+	if (JB_ROOT_IS_SCALAR(jb))
+	{
+		JsonbValue	v;
+
+		JsonbExtractScalar(&jb->root, &v);
+
+		if (v.type == jbvString)
+			return pnstrdup(v.val.string.val, v.val.string.len);
+		else if (v.type == jbvBool)
+			return pstrdup(v.val.boolean ? "true" : "false");
+		else if (v.type == jbvNumeric)
+			return DatumGetCString(DirectFunctionCall1(numeric_out,
+									   PointerGetDatum(v.val.numeric)));
+		else if (v.type == jbvNull)
+			return pstrdup("null");
+		else
+		{
+			elog(ERROR, "unrecognized jsonb value type %d", v.type);
+			return NULL;
+		}
+	}
+	else
+		return JsonbToCString(NULL, &jb->root, VARSIZE(jb));
+}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index e358b5a..0c37932 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3051,6 +3051,50 @@ populate_record_field(ColumnIOData *col,
 	}
 }
 
+/* recursively populate specified type from a json/jsonb value */
+Datum
+json_populate_type(Datum json_val, Oid json_type, Oid typid, int32 typmod,
+				   void **cache, MemoryContext mcxt, bool *isnull)
+{
+	JsValue		jsv = { 0 };
+	JsonbValue	jbv;
+
+	jsv.is_json = json_type == JSONOID;
+
+	if (*isnull)
+	{
+		if (jsv.is_json)
+			jsv.val.json.str = NULL;
+		else
+			jsv.val.jsonb = NULL;
+	}
+	else if (jsv.is_json)
+	{
+		text	   *json = DatumGetTextPP(json_val);
+
+		jsv.val.json.str = VARDATA_ANY(json);
+		jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
+		jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in populate_composite() */
+	}
+	else
+	{
+		Jsonb	   *jsonb = DatumGetJsonbP(json_val);
+
+		jsv.val.jsonb = &jbv;
+
+		/* fill binary jsonb value pointing to jb */
+		jbv.type = jbvBinary;
+		jbv.val.binary.data = &jsonb->root;
+		jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+	}
+
+	if (!*cache)
+		*cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
+
+	return populate_record_field(*cache , typid, typmod, NULL, mcxt,
+								 PointerGetDatum(NULL), &jsv, isnull);
+}
+
 static RecordIOData *
 allocate_record_info(MemoryContext mcxt, int ncolumns)
 {
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index f8944ff..a252383 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -2712,3 +2712,104 @@ wrapItemsInArray(const JsonValueList *items)
 
 	return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
 }
+
+/********************Interface to pgsql's executor***************************/
+bool
+JsonbPathExists(Datum jb, JsonPath *jp, List *vars)
+{
+	JsonPathExecResult res = executeJsonPath(jp, vars, DatumGetJsonbP(jb),
+											 NULL);
+
+	throwJsonPathError(res);
+
+	return res == jperOk;
+}
+
+Datum
+JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
+			   bool *empty, List *vars)
+{
+	JsonbValue *first;
+	bool		wrap;
+	JsonValueList found = { 0 };
+	JsonPathExecResult jper = executeJsonPath(jp, vars, DatumGetJsonbP(jb),
+											  &found);
+	int			count;
+
+	throwJsonPathError(jper);
+
+	count = JsonValueListLength(&found);
+
+	first = count ? JsonValueListHead(&found) : NULL;
+
+	if (!first)
+		wrap = false;
+	else if (wrapper == JSW_NONE)
+		wrap = false;
+	else if (wrapper == JSW_UNCONDITIONAL)
+		wrap = true;
+	else if (wrapper == JSW_CONDITIONAL)
+		wrap = count > 1 ||
+			IsAJsonbScalar(first) ||
+			(first->type == jbvBinary &&
+			 JsonContainerIsScalar(first->val.binary.data));
+	else
+	{
+		elog(ERROR, "unrecognized json wrapper %d", wrapper);
+		wrap = false;
+	}
+
+	if (wrap)
+		return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
+
+	if (count > 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_MORE_THAN_ONE_JSON_ITEM),
+				 errmsg("more than one SQL/JSON item")));
+
+	if (first)
+		return JsonbPGetDatum(JsonbValueToJsonb(first));
+
+	*empty = true;
+	return PointerGetDatum(NULL);
+}
+
+JsonbValue *
+JsonbPathValue(Datum jb, JsonPath *jp, bool *empty, List *vars)
+{
+	JsonbValue *res;
+	JsonValueList found = { 0 };
+	JsonPathExecResult jper = executeJsonPath(jp, vars, DatumGetJsonbP(jb),
+											  &found);
+	int			count;
+
+	throwJsonPathError(jper);
+
+	count = JsonValueListLength(&found);
+
+	*empty = !count;
+
+	if (*empty)
+		return NULL;
+
+	if (count > 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_MORE_THAN_ONE_JSON_ITEM),
+				 errmsg("more than one SQL/JSON item")));
+
+	res = JsonValueListHead(&found);
+
+	if (res->type == jbvBinary &&
+		JsonContainerIsScalar(res->val.binary.data))
+		JsonbExtractScalar(res->val.binary.data, res);
+
+	if (!IsAJsonbScalar(res))
+		ereport(ERROR,
+				(errcode(ERRCODE_JSON_SCALAR_REQUIRED),
+				 errmsg("SQL/JSON scalar required")));
+
+	if (res->type == jbvNull)
+		return NULL;
+
+	return res;
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ad4e315..d6776a0 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -467,6 +467,8 @@ static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
+static void get_json_path_spec(Node *path_spec, deparse_context *context,
+				   bool showimplicit);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -7395,6 +7397,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 		case T_Aggref:
 		case T_WindowFunc:
 		case T_FuncExpr:
+		case T_JsonExpr:
 			/* function-like: name(..) or name[..] */
 			return true;
 
@@ -7513,6 +7516,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 				case T_Aggref:	/* own parentheses */
 				case T_WindowFunc:	/* own parentheses */
 				case T_CaseExpr:	/* other separators */
+				case T_JsonExpr: /* own parentheses */
 					return true;
 				default:
 					return false;
@@ -7676,6 +7680,61 @@ get_rule_expr_paren(Node *node, deparse_context *context,
 
 
 /*
+ * get_json_path_spec		- Parse back a JSON path specification
+ */
+static void
+get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
+{
+	if (IsA(path_spec, Const))
+		get_const_expr((Const *) path_spec, context, -1);
+	else
+		get_rule_expr(path_spec, context, showimplicit);
+}
+
+/*
+ * get_json_format			- Parse back a JsonFormat structure
+ */
+static void
+get_json_format(JsonFormat *format, deparse_context *context)
+{
+	if (format->type == JS_FORMAT_DEFAULT)
+		return;
+
+	appendStringInfoString(context->buf,
+						   format->type == JS_FORMAT_JSONB ?
+						   " FORMAT JSONB" : " FORMAT JSON");
+
+	if (format->encoding != JS_ENC_DEFAULT)
+	{
+		const char *encoding =
+			format->encoding == JS_ENC_UTF16 ? "UTF16" :
+			format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
+
+		appendStringInfo(context->buf, " ENCODING %s", encoding);
+	}
+}
+
+/*
+ * get_json_returning		- Parse back a JsonReturning structure
+ */
+static void
+get_json_returning(JsonReturning *returning, deparse_context *context,
+				   bool json_format_by_default)
+{
+	if (!OidIsValid(returning->typid))
+		return;
+
+	appendStringInfo(context->buf, " RETURNING %s",
+					 format_type_with_typemod(returning->typid,
+											  returning->typmod));
+
+	if (!json_format_by_default ||
+		returning->format.type !=
+			(returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
+		get_json_format(&returning->format, context);
+}
+
+/*
  * get_coercion				- Parse back a coercion
  */
 static void
@@ -7694,6 +7753,54 @@ get_coercion(Expr *arg, deparse_context *context, bool showimplicit,
 	}
 }
 
+static void
+get_json_behavior(JsonBehavior *behavior, deparse_context *context,
+				  const char *on)
+{
+	switch (behavior->btype)
+	{
+		case JSON_BEHAVIOR_DEFAULT:
+			appendStringInfoString(context->buf, " DEFAULT ");
+			get_rule_expr(behavior->default_expr, context, false);
+			break;
+
+		case JSON_BEHAVIOR_EMPTY:
+			appendStringInfoString(context->buf, " EMPTY");
+			break;
+
+		case JSON_BEHAVIOR_EMPTY_ARRAY:
+			appendStringInfoString(context->buf, " EMPTY ARRAY");
+			break;
+
+		case JSON_BEHAVIOR_EMPTY_OBJECT:
+			appendStringInfoString(context->buf, " EMPTY OBJECT");
+			break;
+
+		case JSON_BEHAVIOR_ERROR:
+			appendStringInfoString(context->buf, " ERROR");
+			break;
+
+		case JSON_BEHAVIOR_FALSE:
+			appendStringInfoString(context->buf, " FALSE");
+			break;
+
+		case JSON_BEHAVIOR_NULL:
+			appendStringInfoString(context->buf, " NULL");
+			break;
+
+		case JSON_BEHAVIOR_TRUE:
+			appendStringInfoString(context->buf, " TRUE");
+			break;
+
+		case JSON_BEHAVIOR_UNKNOWN:
+			appendStringInfoString(context->buf, " UNKNOWN");
+			break;
+	}
+
+	appendStringInfo(context->buf, " ON %s", on);
+}
+
+
 /* ----------
  * get_rule_expr			- Parse back an expression
  *
@@ -8808,6 +8915,83 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+
+		case T_JsonValueExpr:
+			{
+				JsonValueExpr *jve = (JsonValueExpr *) node;
+
+				get_rule_expr((Node *) jve->expr, context, false);
+				get_json_format(&jve->format, context);
+			}
+			break;
+
+		case T_JsonExpr:
+			{
+				JsonExpr   *jexpr = (JsonExpr *) node;
+
+				switch (jexpr->op)
+				{
+					case IS_JSON_QUERY:
+						appendStringInfoString(buf, "JSON_QUERY(");
+						break;
+					case IS_JSON_VALUE:
+						appendStringInfoString(buf, "JSON_VALUE(");
+						break;
+					case IS_JSON_EXISTS:
+						appendStringInfoString(buf, "JSON_EXISTS(");
+						break;
+				}
+
+				get_rule_expr(jexpr->raw_expr, context, showimplicit);
+
+				get_json_format(&jexpr->format, context);
+
+				appendStringInfoString(buf, ", ");
+
+				get_json_path_spec(jexpr->path_spec, context, showimplicit);
+
+				if (jexpr->passing.values)
+				{
+					ListCell   *lc1, *lc2;
+					bool		needcomma = false;
+
+					appendStringInfoString(buf, " PASSING ");
+
+					forboth(lc1, jexpr->passing.names,
+							lc2, jexpr->passing.values)
+					{
+						if (needcomma)
+							appendStringInfoString(buf, ", ");
+						needcomma = true;
+
+						get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
+						appendStringInfo(buf, " AS %s",
+										 ((Value *) lfirst(lc1))->val.str);
+					}
+				}
+
+				if (jexpr->op != IS_JSON_EXISTS)
+					get_json_returning(&jexpr->returning, context,
+									   jexpr->op != IS_JSON_VALUE);
+
+				if (jexpr->wrapper == JSW_CONDITIONAL)
+					appendStringInfo(buf, " WITH CONDITIONAL WRAPPER");
+
+				if (jexpr->wrapper == JSW_UNCONDITIONAL)
+					appendStringInfo(buf, " WITH UNCONDITIONAL WRAPPER");
+
+				if (jexpr->omit_quotes)
+					appendStringInfo(buf, " OMIT QUOTES");
+
+				if (jexpr->op != IS_JSON_EXISTS)
+					get_json_behavior(&jexpr->on_empty, context, "EMPTY");
+
+				get_json_behavior(&jexpr->on_error, context, "ERROR");
+
+				appendStringInfoString(buf, ")");
+			}
+			break;
+
 		case T_List:
 			{
 				char	   *sep;
@@ -8904,6 +9088,7 @@ looks_like_function(Node *node)
 		case T_MinMaxExpr:
 		case T_SQLValueFunction:
 		case T_XmlExpr:
+		case T_JsonExpr:
 			/* these are all accepted by func_expr_common_subexpr */
 			return true;
 		default:
@@ -8979,6 +9164,66 @@ get_func_opts(FuncFormat aggformat, Node *aggformatopts, deparse_context *contex
 {
 	switch (aggformat)
 	{
+		case FUNCFMT_JSON_OBJECT:
+		case FUNCFMT_JSON_OBJECTAGG:
+		case FUNCFMT_JSON_ARRAY:
+		case FUNCFMT_JSON_ARRAYAGG:
+			{
+				JsonCtorOpts *opts = castNode(JsonCtorOpts, aggformatopts);
+
+				if (!opts)
+					break;
+
+				if (opts->absent_on_null)
+				{
+					if (aggformat == FUNCFMT_JSON_OBJECT ||
+						aggformat == FUNCFMT_JSON_OBJECTAGG)
+						appendStringInfoString(context->buf, " ABSENT ON NULL");
+				}
+				else
+				{
+					if (aggformat == FUNCFMT_JSON_ARRAY ||
+						aggformat == FUNCFMT_JSON_ARRAYAGG)
+						appendStringInfoString(context->buf, " NULL ON NULL");
+				}
+
+				if (opts->unique)
+					appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
+
+				get_json_returning(&opts->returning, context, true);
+			}
+			break;
+
+		case FUNCFMT_IS_JSON:
+			{
+				JsonIsPredicateOpts *opts =
+					castNode(JsonIsPredicateOpts, aggformatopts);
+
+				appendStringInfoString(context->buf, " IS JSON");
+
+				if (!opts)
+					break;
+
+				switch (opts->value_type)
+				{
+					case JS_TYPE_SCALAR:
+						appendStringInfoString(context->buf, " SCALAR");
+						break;
+					case JS_TYPE_ARRAY:
+						appendStringInfoString(context->buf, " ARRAY");
+						break;
+					case JS_TYPE_OBJECT:
+						appendStringInfoString(context->buf, " OBJECT");
+						break;
+					default:
+						break;
+				}
+
+				if (opts->unique_keys)
+					appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
+			}
+			break;
+
 		default:
 			break;
 	}
@@ -8995,6 +9240,8 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 	Oid			funcoid = expr->funcid;
 	Oid			argtypes[FUNC_MAX_ARGS];
 	int			nargs;
+	int			firstarg;
+	int			lastarg = list_length(expr->args);
 	List	   *argnames;
 	bool		use_variadic;
 	ListCell   *l;
@@ -9055,22 +9302,57 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 
 	switch (expr->funcformat2)
 	{
+		case FUNCFMT_JSON_OBJECT:
+			funcname = "JSON_OBJECT";
+			firstarg = 2;
+			use_variadic = false;
+			break;
+
+		case FUNCFMT_JSON_ARRAY:
+			funcname = "JSON_ARRAY";
+			firstarg = 1;
+			use_variadic = false;
+			break;
+
+		case FUNCFMT_IS_JSON:
+			funcname = NULL;
+			firstarg = 0;
+			lastarg = 0;
+			use_variadic = false;
+			break;
+
 		default:
 			funcname = generate_function_name(funcoid, nargs,
 											  argnames, argtypes,
 											  expr->funcvariadic,
 											  &use_variadic,
 											  context->special_exprkind);
+			firstarg = 0;
 			break;
 	}
 
-	appendStringInfo(buf, "%s(", funcname);
+	if (funcname)
+		appendStringInfo(buf, "%s(", funcname);
+	else if (!PRETTY_PAREN(context))
+		appendStringInfoChar(buf, '(');
 
 	nargs = 0;
 	foreach(l, expr->args)
 	{
-		if (nargs++ > 0)
-			appendStringInfoString(buf, ", ");
+		if (nargs > lastarg)
+			break;
+
+		if (nargs++ < firstarg)
+			continue;
+
+		if (nargs > firstarg + 1)
+		{
+			const char *sep = expr->funcformat2 == FUNCFMT_JSON_OBJECT &&
+				!((nargs - firstarg) % 2) ? " : " : ", ";
+
+			appendStringInfoString(buf, sep);
+		}
+
 		if (use_variadic && lnext(l) == NULL)
 			appendStringInfoString(buf, "VARIADIC ");
 		get_rule_expr((Node *) lfirst(l), context, true);
@@ -9078,7 +9360,8 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 
 	get_func_opts(expr->funcformat2, expr->funcformatopts, context);
 
-	appendStringInfoChar(buf, ')');
+	if (funcname || !PRETTY_PAREN(context))
+		appendStringInfoChar(buf, ')');
 }
 
 /*
@@ -9090,8 +9373,9 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
 {
 	StringInfo	buf = context->buf;
 	Oid			argtypes[FUNC_MAX_ARGS];
+	const char *funcname;
 	int			nargs;
-	bool		use_variadic;
+	bool		use_variadic = false;
 
 	/*
 	 * For a combining aggregate, we look up and deparse the corresponding
@@ -9120,13 +9404,24 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
 	/* Extract the argument types as seen by the parser */
 	nargs = get_aggregate_argtypes(aggref, argtypes);
 
+	switch (aggref->aggformat)
+	{
+		case FUNCFMT_JSON_OBJECTAGG:
+			funcname = "JSON_OBJECTAGG";
+			break;
+		case FUNCFMT_JSON_ARRAYAGG:
+			funcname = "JSON_ARRAYAGG";
+			break;
+		default:
+			funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
+											  argtypes, aggref->aggvariadic,
+											  &use_variadic,
+											  context->special_exprkind);
+			break;
+	}
+
 	/* 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))
@@ -9162,7 +9457,17 @@ get_agg_expr(Aggref *aggref, deparse_context *context,
 				if (tle->resjunk)
 					continue;
 				if (i++ > 0)
-					appendStringInfoString(buf, ", ");
+				{
+					if (aggref->aggformat == FUNCFMT_JSON_OBJECTAGG)
+					{
+						if (i > 2)
+							break; /* skip ABSENT ON NULL and WITH UNIQUE args */
+
+						appendStringInfoString(buf, " : ");
+					}
+					else
+						appendStringInfoString(buf, ", ");
+				}
 				if (use_variadic && i == nargs)
 					appendStringInfoString(buf, "VARIADIC ");
 				get_rule_expr(arg, context, true);
@@ -9216,6 +9521,7 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
 	int			nargs;
 	List	   *argnames;
 	ListCell   *l;
+	const char *funcname;
 
 	if (list_length(wfunc->args) > FUNC_MAX_ARGS)
 		ereport(ERROR,
@@ -9233,16 +9539,37 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
 		nargs++;
 	}
 
-	appendStringInfo(buf, "%s(",
-					 generate_function_name(wfunc->winfnoid, nargs,
-											argnames, argtypes,
-											false, NULL,
-											context->special_exprkind));
+	switch (wfunc->winformat)
+	{
+		case FUNCFMT_JSON_OBJECTAGG:
+			funcname = "JSON_OBJECTAGG";
+			break;
+		case FUNCFMT_JSON_ARRAYAGG:
+			funcname = "JSON_ARRAYAGG";
+			break;
+		default:
+			funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
+											  argtypes, false, NULL,
+											  context->special_exprkind);
+			break;
+	}
+
+	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 (wfunc->winformat == FUNCFMT_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);
+	}
 
 	get_func_opts(wfunc->winformat, wfunc->winformatopts, context);
 
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index d2a4298..954ec8a 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -541,14 +541,22 @@
 # 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_objectagg', aggtransfn => 'json_objectagg_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_objectagg', aggtransfn => 'jsonb_objectagg_transfn',
+  aggfinalfn => 'jsonb_object_agg_finalfn', aggtranstype => 'internal' },
 
 # ordered-set and hypothetical-set aggregates
 { aggfnoid => 'percentile_disc(float8,anyelement)', aggkind => 'o',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 326c62a..ed6195e 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -8224,17 +8224,31 @@
   proname => 'json_agg_transfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'internal', proargtypes => 'internal anyelement',
   prosrc => 'json_agg_transfn' },
+{ oid => '3426', 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' },
+#define F_JSON_AGG 3175
 { oid => '3175', descr => 'aggregate input into json',
   proname => 'json_agg', prokind => 'a', proisstrict => 'f', provolatile => 's',
   prorettype => 'json', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
+#define F_JSON_AGG_STRICT 3450
+{ oid => '3424', 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 => '3427', descr => 'json object aggregate transition function',
+  proname => 'json_objectagg_transfn', proisstrict => 'f', provolatile => 's',
+  prorettype => 'internal', proargtypes => 'internal any any bool bool',
+  prosrc => 'json_objectagg_transfn' },
 { oid => '3196', descr => 'json object aggregate final function',
   proname => 'json_object_agg_finalfn', proisstrict => 'f',
   prorettype => 'json', proargtypes => 'internal',
@@ -8243,6 +8257,11 @@
   proname => 'json_object_agg', prokind => 'a', proisstrict => 'f',
   provolatile => 's', prorettype => 'json', proargtypes => 'any any',
   prosrc => 'aggregate_dummy' },
+#define F_JSON_OBJECTAGG 3451
+{ oid => '3425', descr => 'aggregate input into a json object',
+  proname => 'json_objectagg', prokind => 'a', proisstrict => 'f',
+  provolatile => 's', prorettype => 'json', proargtypes => 'any any bool bool',
+  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',
@@ -8252,6 +8271,11 @@
   proname => 'json_build_array', proisstrict => 'f', provolatile => 's',
   prorettype => 'json', proargtypes => '',
   prosrc => 'json_build_array_noargs' },
+{ oid => '3998', descr => 'build a json array from any inputs',
+  proname => 'json_build_array_ext', provariadic => 'any', proisstrict => 'f',
+  provolatile => 's', prorettype => 'json', proargtypes => 'bool any',
+  proallargtypes => '{bool,any}', proargmodes => '{i,v}',
+  prosrc => 'json_build_array_ext' },
 { oid => '3200',
   descr => 'build a json object from pairwise key/value inputs',
   proname => 'json_build_object', provariadic => 'any', proisstrict => 'f',
@@ -8262,6 +8286,11 @@
   proname => 'json_build_object', proisstrict => 'f', provolatile => 's',
   prorettype => 'json', proargtypes => '',
   prosrc => 'json_build_object_noargs' },
+{ oid => '6066', descr => 'build a json object from pairwise key/value inputs',
+  proname => 'json_build_object_ext', provariadic => 'any', proisstrict => 'f',
+  provolatile => 's', prorettype => 'json', proargtypes => 'bool bool any',
+  proallargtypes => '{bool,bool,any}', proargmodes => '{i,i,v}',
+  prosrc => 'json_build_object_ext' },
 { oid => '3202', descr => 'map text array of key value pairs to json object',
   proname => 'json_object', prorettype => 'json', proargtypes => '_text',
   prosrc => 'json_object' },
@@ -8274,6 +8303,13 @@
 { oid => '3261', descr => 'remove object fields with null values from json',
   proname => 'json_strip_nulls', prorettype => 'json', proargtypes => 'json',
   prosrc => 'json_strip_nulls' },
+{ oid => '6060', descr => 'check json value type and key uniqueness',
+  proname => 'json_is_valid', prorettype => 'bool',
+  proargtypes => 'json text bool', prosrc => 'json_is_valid' },
+{ oid => '6061',
+  descr => 'check json text validity, value type and key uniquenes',
+  proname => 'json_is_valid', prorettype => 'bool',
+  proargtypes => 'text text bool', prosrc => 'json_is_valid' },
 
 { oid => '3947',
   proname => 'json_object_field', prorettype => 'json',
@@ -9077,18 +9113,32 @@
   proname => 'jsonb_agg_transfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'internal', proargtypes => 'internal anyelement',
   prosrc => 'jsonb_agg_transfn' },
+{ oid => '6065', 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',
   prosrc => 'jsonb_agg_finalfn' },
+#define F_JSONB_AGG 3267
 { oid => '3267', descr => 'aggregate input into jsonb',
   proname => 'jsonb_agg', prokind => 'a', proisstrict => 'f',
   provolatile => 's', prorettype => 'jsonb', proargtypes => 'anyelement',
   prosrc => 'aggregate_dummy' },
+#define F_JSONB_AGG_STRICT 6063
+{ oid => '6063', 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 => '3423', descr => 'jsonb object aggregate transition function',
+  proname => 'jsonb_objectagg_transfn', proisstrict => 'f', provolatile => 's',
+  prorettype => 'internal', proargtypes => 'internal any any bool bool',
+  prosrc => 'jsonb_objectagg_transfn' },
 { oid => '3269', descr => 'jsonb object aggregate final function',
   proname => 'jsonb_object_agg_finalfn', proisstrict => 'f', provolatile => 's',
   prorettype => 'jsonb', proargtypes => 'internal',
@@ -9097,6 +9147,11 @@
   proname => 'jsonb_object_agg', prokind => 'a', proisstrict => 'f',
   prorettype => 'jsonb', proargtypes => 'any any',
   prosrc => 'aggregate_dummy' },
+#define F_JSONB_OBJECTAGG 6064
+{ oid => '6064', descr => 'aggregate inputs into jsonb object',
+  proname => 'jsonb_objectagg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'jsonb', proargtypes => 'any any bool bool',
+  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',
@@ -9106,6 +9161,11 @@
   proname => 'jsonb_build_array', proisstrict => 'f', provolatile => 's',
   prorettype => 'jsonb', proargtypes => '',
   prosrc => 'jsonb_build_array_noargs' },
+{ oid => '6068', descr => 'build a jsonb array from any inputs',
+  proname => 'jsonb_build_array_ext', provariadic => 'any', proisstrict => 'f',
+  provolatile => 's', prorettype => 'jsonb', proargtypes => 'bool any',
+  proallargtypes => '{bool,any}', proargmodes => '{i,v}',
+  prosrc => 'jsonb_build_array_ext' },
 { oid => '3273',
   descr => 'build a jsonb object from pairwise key/value inputs',
   proname => 'jsonb_build_object', provariadic => 'any', proisstrict => 'f',
@@ -9116,9 +9176,17 @@
   proname => 'jsonb_build_object', proisstrict => 'f', provolatile => 's',
   prorettype => 'jsonb', proargtypes => '',
   prosrc => 'jsonb_build_object_noargs' },
+{ oid => '6067', descr => 'build a jsonb object from pairwise key/value inputs',
+  proname => 'jsonb_build_object_ext', provariadic => 'any', proisstrict => 'f',
+  provolatile => 's', prorettype => 'jsonb', proargtypes => 'bool bool any',
+  proallargtypes => '{bool,bool,any}', proargmodes => '{i,i,v}',
+  prosrc => 'jsonb_build_object_ext' },
 { oid => '3262', descr => 'remove object fields with null values from jsonb',
   proname => 'jsonb_strip_nulls', prorettype => 'jsonb', proargtypes => 'jsonb',
   prosrc => 'jsonb_strip_nulls' },
+{ oid => '6062', descr => 'check jsonb value type',
+  proname => 'jsonb_is_valid', prorettype => 'bool',
+  proargtypes => 'jsonb text', prosrc => 'jsonb_is_valid' },
 
 { oid => '3478',
   proname => 'jsonb_object_field', prorettype => 'jsonb',
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index f7b1f77..3fb1975 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -20,6 +20,7 @@
 /* forward references to avoid circularity */
 struct ExprEvalStep;
 struct ArrayRefState;
+struct JsonbValue;
 
 /* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
 /* expression's interpreter has been initialized */
@@ -219,6 +220,7 @@ typedef enum ExprEvalOp
 	EEOP_WINDOW_FUNC,
 	EEOP_SUBPLAN,
 	EEOP_ALTERNATIVE_SUBPLAN,
+	EEOP_JSONEXPR,
 
 	/* aggregation related nodes */
 	EEOP_AGG_STRICT_DESERIALIZE,
@@ -636,6 +638,55 @@ typedef struct ExprEvalStep
 			int			transno;
 			int			setoff;
 		}			agg_trans;
+
+		/* for EEOP_JSONEXPR */
+		struct
+		{
+			JsonExpr   *jsexpr;			/* original expression node */
+
+			struct
+			{
+				FmgrInfo	func;		/* typinput function for output type */
+				Oid			typioparam;
+			} input;					/* I/O info for output type */
+
+			struct
+			{
+				Datum		value;
+				bool		isnull;
+			}		   *raw_expr,			/* raw context item value */
+					   *res_expr,			/* result item */
+					   *coercion_expr,		/* input for JSON item coercion */
+					   *pathspec;			/* path specification value */
+
+			ExprState  *formatted_expr;		/* formatted context item */
+			ExprState  *result_expr;		/* coerced to output type */
+			ExprState  *default_on_empty;	/* ON EMPTY DEFAULT expression */
+			ExprState  *default_on_error;	/* ON ERROR DEFAULT expression */
+			List	   *args;				/* passing arguments */
+
+			void	   *cache;				/* cache for json_populate_type() */
+
+			struct JsonCoercionsState
+			{
+				struct JsonCoercionState
+				{
+					JsonCoercion *coercion;		/* coercion expression */
+					ExprState  *estate;	/* coercion expression state */
+				} 			null,
+							string,
+							numeric,
+							boolean,
+							date,
+							time,
+							timetz,
+							timestamp,
+							timestamptz,
+							composite;
+			}			coercions;	/* states for coercion from SQL/JSON item
+									 * types directly to the output type */
+		}			jsonexpr;
+
 	}			d;
 } ExprEvalStep;
 
@@ -735,6 +786,13 @@ extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op,
 						   ExprContext *econtext);
 extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
 					ExprContext *econtext);
+extern void ExecEvalJson(ExprState *state, ExprEvalStep *op,
+						 ExprContext *econtext);
+extern Datum ExecPrepareJsonItemCoercion(struct JsonbValue *item,
+							JsonReturning *returning,
+							struct JsonCoercionsState *coercions,
+							struct JsonCoercionState **pjcstate);
+extern bool ExecEvalJsonNeedsSubTransaction(JsonExpr *jsexpr);
 
 extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup);
 extern Datum ExecAggTransReparent(AggState *aggstate, AggStatePerTrans pertrans,
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index f82b516..56c00c4 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,6 +243,8 @@ ExecProcNode(PlanState *node)
  */
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
+extern ExprState *ExecInitExprWithCaseValue(Expr *node, PlanState *parent,
+						  Datum *caseval, bool *casenull);
 extern ExprState *ExecInitQual(List *qual, PlanState *parent);
 extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
 extern List *ExecInitExprList(List *nodes, PlanState *parent);
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 57bd52f..f7aec03 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -88,4 +88,11 @@ extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int loc
 
 extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
 
+extern JsonValueExpr *makeJsonValueExpr(Expr *expr, JsonFormat format);
+extern JsonBehavior *makeJsonBehavior(JsonBehaviorType type, Node *expr);
+extern Node *makeJsonKeyValue(Node *key, Node *value);
+extern Node *makeJsonIsPredicate(Node *expr, JsonFormat format,
+							   JsonValueType vtype, bool unique_keys);
+extern JsonEncoding makeJsonEncoding(char *name);
+
 #endif							/* MAKEFUNC_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 43f1552..4527b5e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -195,6 +195,9 @@ typedef enum NodeTag
 	T_FromExpr,
 	T_OnConflictExpr,
 	T_IntoClause,
+	T_JsonExpr,
+	T_JsonCoercion,
+	T_JsonItemCoercions,
 
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
@@ -474,6 +477,22 @@ typedef enum NodeTag
 	T_PartitionRangeDatum,
 	T_PartitionCmd,
 	T_VacuumRelation,
+	T_JsonValueExpr,
+	T_JsonObjectCtor,
+	T_JsonArrayCtor,
+	T_JsonArrayQueryCtor,
+	T_JsonObjectAgg,
+	T_JsonArrayAgg,
+	T_JsonFuncExpr,
+	T_JsonIsPredicate,
+	T_JsonExistsPredicate,
+	T_JsonCommon,
+	T_JsonArgument,
+	T_JsonKeyValue,
+	T_JsonBehavior,
+	T_JsonOutput,
+	T_JsonCtorOpts,
+	T_JsonIsPredicateOpts,
 
 	/*
 	 * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6390f7e..857f0b5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1428,6 +1428,221 @@ typedef struct TriggerTransition
 	bool		isTable;
 } TriggerTransition;
 
+/* Nodes for SQL/JSON support */
+
+/*
+ * JsonQuotes -
+ *		representation of [KEEP|OMIT] QUOTES clause for JSON_QUERY()
+ */
+typedef enum JsonQuotes
+{
+	JS_QUOTES_UNSPEC,			/* unspecified */
+	JS_QUOTES_KEEP,				/* KEEP QUOTES */
+	JS_QUOTES_OMIT				/* OMIT QUOTES */
+} JsonQuotes;
+
+/*
+ * JsonPathSpec -
+ *		representation of JSON path constant
+ */
+typedef char *JsonPathSpec;
+
+/*
+ * 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;
+
+/*
+ * JsonValueExpr -
+ *		representation of JSON value expression (expr [FORMAT json_format])
+ */
+typedef struct JsonValueExpr
+{
+	NodeTag		type;
+	Expr	   *expr;			/* raw expression */
+	JsonFormat  format;			/* FORMAT clause, if specified */
+} JsonValueExpr;
+
+/*
+ * JsonArgument -
+ *		representation of argument from JSON PASSING clause
+ */
+typedef struct JsonArgument
+{
+	NodeTag		type;
+	JsonValueExpr *val;			/* argument value expression */
+	char	   *name;			/* argument name */
+} JsonArgument;
+
+/*
+ * JsonCommon -
+ *		representation of common syntax of functions using JSON path
+ */
+typedef struct JsonCommon
+{
+	NodeTag		type;
+	JsonValueExpr *expr;		/* context item expression */
+	Node	   *pathspec;		/* JSON path specification expression */
+	char	   *pathname;		/* path name, if any */
+	List	   *passing;		/* list of PASSING clause arguments, if any */
+	int			location;		/* token location, or -1 if unknown */
+} JsonCommon;
+
+/*
+ * JsonFuncExpr -
+ *		untransformed representation of JSON function expressions
+ */
+typedef struct JsonFuncExpr
+{
+	NodeTag		type;
+	JsonExprOp	op;				/* expression type */
+	JsonCommon *common;			/* common syntax */
+	JsonOutput *output;			/* output clause, if specified */
+	JsonBehavior *on_empty;		/* ON EMPTY behavior, if specified */
+	JsonBehavior *on_error;		/* ON ERROR behavior, if specified */
+	JsonWrapper	wrapper;		/* array wrapper behavior (JSON_QUERY only) */
+	bool		omit_quotes;	/* omit or keep quotes? (JSON_QUERY only) */
+	int			location;		/* token location, or -1 if unknown */
+} JsonFuncExpr;
+
+/*
+ * JsonValueType -
+ *		representation of JSON item type in IS JSON predicate
+ */
+typedef enum JsonValueType
+{
+	JS_TYPE_ANY,				/* IS JSON [VALUE] */
+	JS_TYPE_OBJECT,				/* IS JSON OBJECT */
+	JS_TYPE_ARRAY,				/* IS JSON ARRAY*/
+	JS_TYPE_SCALAR				/* IS JSON SCALAR */
+} JsonValueType;
+
+/*
+ * JsonIsPredicate -
+ *		untransformed representation of IS JSON predicate
+ */
+typedef struct JsonIsPredicate
+{
+	NodeTag		type;
+	Node	   *expr;			/* untransformed expression */
+	JsonFormat	format;			/* FORMAT clause, if specified */
+	JsonValueType vtype;		/* JSON item type */
+	bool		unique_keys;	/* check key uniqueness? */
+	int			location;		/* token location, or -1 if unknown */
+} JsonIsPredicate;
+
+typedef struct JsonIsPredicateOpts
+{
+	NodeTag		type;
+	JsonValueType value_type;	/* JSON item type */
+	bool		unique_keys;	/* check key uniqueness? */
+} JsonIsPredicateOpts;
+
+/*
+ * 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;
+
+/*
+ * JsonObjectCtor -
+ *		untransformed representation of JSON_OBJECT() constructor
+ */
+typedef struct JsonObjectCtor
+{
+	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 */
+} JsonObjectCtor;
+
+/*
+ * JsonArrayCtor -
+ *		untransformed representation of JSON_ARRAY(element,...) constructor
+ */
+typedef struct JsonArrayCtor
+{
+	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 */
+} JsonArrayCtor;
+
+/*
+ * JsonArrayQueryCtor -
+ *		untransformed representation of JSON_ARRAY(subquery) constructor
+ */
+typedef struct JsonArrayQueryCtor
+{
+	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 */
+} JsonArrayQueryCtor;
+
+/*
+ * JsonAggCtor -
+ *		common fields of untransformed representation of
+ *		JSON_ARRAYAGG() and JSON_OBJECTAGG()
+ */
+typedef struct JsonAggCtor
+{
+	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 */
+} JsonAggCtor;
+
+/*
+ * JsonObjectAgg -
+ *		untransformed representation of JSON_OBJECTAGG()
+ */
+typedef struct JsonObjectAgg
+{
+	JsonAggCtor	ctor;			/* 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
+{
+	JsonAggCtor	ctor;			/* common fields */
+	JsonValueExpr *arg;			/* array element expression */
+	bool		absent_on_null;	/* skip NULL elements? */
+} JsonArrayAgg;
+
+typedef struct JsonCtorOpts
+{
+	NodeTag		type;
+	JsonReturning returning;	/* RETURNING clause */
+	bool		absent_on_null;	/* skip NULL values? */
+	bool		unique;			/* check key uniqueness? */
+} JsonCtorOpts;
+
 /*****************************************************************************
  *		Raw Grammar Output Statements
  *****************************************************************************/
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 641500e..4bfa016 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -252,6 +252,11 @@ typedef struct Param
 typedef enum FuncFormat
 {
 	FUNCFMT_REGULAR = 0,
+	FUNCFMT_JSON_OBJECT = 1,
+	FUNCFMT_JSON_ARRAY = 2,
+	FUNCFMT_JSON_OBJECTAGG = 3,
+	FUNCFMT_JSON_ARRAYAGG = 4,
+	FUNCFMT_IS_JSON = 5
 } FuncFormat;
 
 /*
@@ -1169,6 +1174,167 @@ typedef struct XmlExpr
 	int			location;		/* token location, or -1 if unknown */
 } XmlExpr;
 
+/*
+ * JsonExprOp -
+ *		enumeration of JSON functions using JSON path
+ */
+typedef enum JsonExprOp
+{
+	IS_JSON_VALUE,				/* JSON_VALUE() */
+	IS_JSON_QUERY,				/* JSON_QUERY() */
+	IS_JSON_EXISTS				/* JSON_EXISTS() */
+} JsonExprOp;
+
+/*
+ * 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;
+
+/*
+ * JsonBehaviorType -
+ *		enumeration of behavior types used in JSON ON ... BEHAVIOR clause
+ */
+typedef enum
+{
+	JSON_BEHAVIOR_NULL,
+	JSON_BEHAVIOR_ERROR,
+	JSON_BEHAVIOR_EMPTY,
+	JSON_BEHAVIOR_TRUE,
+	JSON_BEHAVIOR_FALSE,
+	JSON_BEHAVIOR_UNKNOWN,
+	JSON_BEHAVIOR_EMPTY_ARRAY,
+	JSON_BEHAVIOR_EMPTY_OBJECT,
+	JSON_BEHAVIOR_DEFAULT,
+} JsonBehaviorType;
+
+/*
+ * JsonWrapper -
+ *		representation of WRAPPER clause for JSON_QUERY()
+ */
+typedef enum JsonWrapper
+{
+	JSW_NONE,
+	JSW_CONDITIONAL,
+	JSW_UNCONDITIONAL,
+} JsonWrapper;
+
+/*
+ * JsonFormat -
+ *		representation of JSON FORMAT clause
+ */
+typedef struct JsonFormat
+{
+	JsonFormatType	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
+{
+	JsonFormat	format;			/* output JSON format */
+	Oid			typid;			/* target type Oid */
+	int32		typmod;			/* target type modifier */
+} JsonReturning;
+
+/*
+ * JsonBehavior -
+ *		representation of JSON ON ... BEHAVIOR clause
+ */
+typedef struct JsonBehavior
+{
+	NodeTag		type;
+	JsonBehaviorType btype;		/* behavior type */
+	Node	   *default_expr;	/* default expression, if any */
+} JsonBehavior;
+
+/*
+ * JsonPassing -
+ *		representation of JSON PASSING clause
+ */
+typedef struct JsonPassing
+{
+	List	   *values;			/* list of PASSING argument expressions */
+	List	   *names;			/* parallel list of Value strings */
+} JsonPassing;
+
+/*
+ * JsonCoercion -
+ *		coercion from SQL/JSON item types to SQL types
+ */
+typedef struct JsonCoercion
+{
+	NodeTag		type;
+	Node	   *expr;			/* resulting expression coerced to target type */
+	bool		via_populate;	/* coerce result using json_populate_type()? */
+	bool		via_io;			/* coerce result using type input function? */
+	Oid			collation;		/* collation for coercion via I/O or populate */
+} JsonCoercion;
+
+/*
+ * JsonItemCoercions -
+ *		expressions for coercion from SQL/JSON item types directly to the
+ *		output SQL type
+ */
+typedef struct JsonItemCoercions
+{
+	NodeTag		type;
+	JsonCoercion *null;
+	JsonCoercion *string;
+	JsonCoercion *numeric;
+	JsonCoercion *boolean;
+	JsonCoercion *date;
+	JsonCoercion *time;
+	JsonCoercion *timetz;
+	JsonCoercion *timestamp;
+	JsonCoercion *timestamptz;
+	JsonCoercion *composite;	/* arrays and objects */
+} JsonItemCoercions;
+
+/*
+ * JsonExpr -
+ *		transformed representation of JSON_VALUE(), JSON_QUERY(), JSON_EXISTS()
+ */
+typedef struct JsonExpr
+{
+	Expr		xpr;
+	JsonExprOp	op;				/* json function ID */
+	Node	   *raw_expr;		/* raw context item expression */
+	Node	   *formatted_expr;	/* formatted context item expression */
+	JsonCoercion *result_coercion;	/* resulting coercion to RETURNING type */
+	JsonFormat	format;			/* context item format (JSON/JSONB) */
+	Node	   *path_spec;		/* JSON path specification expression */
+	JsonPassing	passing;		/* PASSING clause arguments */
+	JsonReturning returning;	/* RETURNING clause type/format info */
+	JsonBehavior on_empty;		/* ON EMPTY behavior */
+	JsonBehavior on_error;		/* ON ERROR behavior */
+	JsonItemCoercions *coercions; /* coercions for JSON_VALUE */
+	JsonWrapper	wrapper;		/* WRAPPER for JSON_QUERY */
+	bool		omit_quotes;	/* KEEP/OMIT QUOTES for JSON_QUERY */
+	int			location;		/* token location, or -1 if unknown */
+} JsonExpr;
+
 /* ----------------
  * NullTest
  *
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 23db401..7732c1b 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -27,6 +27,7 @@
 
 /* name, value, category */
 PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("absent", ABSENT, UNRESERVED_KEYWORD)
 PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD)
 PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD)
@@ -89,6 +90,7 @@ PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
 PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
 PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
 PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("conditional", CONDITIONAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD)
 PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD)
@@ -142,11 +144,13 @@ PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
 PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
 PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
+PG_KEYWORD("empty", EMPTY_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
 PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD)
 PG_KEYWORD("end", END_P, RESERVED_KEYWORD)
 PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("error", ERROR_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD)
 PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD)
 PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD)
@@ -169,6 +173,7 @@ PG_KEYWORD("following", FOLLOWING, UNRESERVED_KEYWORD)
 PG_KEYWORD("for", FOR, RESERVED_KEYWORD)
 PG_KEYWORD("force", FORCE, UNRESERVED_KEYWORD)
 PG_KEYWORD("foreign", FOREIGN, RESERVED_KEYWORD)
+PG_KEYWORD("format", FORMAT, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("forward", FORWARD, UNRESERVED_KEYWORD)
 PG_KEYWORD("freeze", FREEZE, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("from", FROM, RESERVED_KEYWORD)
@@ -221,7 +226,17 @@ PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("json", JSON, UNRESERVED_KEYWORD)
+PG_KEYWORD("json_array", JSON_ARRAY, COL_NAME_KEYWORD)
+PG_KEYWORD("json_arrayagg", JSON_ARRAYAGG, COL_NAME_KEYWORD)
+PG_KEYWORD("json_exists", JSON_EXISTS, COL_NAME_KEYWORD)
+PG_KEYWORD("json_object", JSON_OBJECT, COL_NAME_KEYWORD)
+PG_KEYWORD("json_objectagg", JSON_OBJECTAGG, COL_NAME_KEYWORD)
+PG_KEYWORD("json_query", JSON_QUERY, COL_NAME_KEYWORD)
+PG_KEYWORD("json_value", JSON_VALUE, COL_NAME_KEYWORD)
+PG_KEYWORD("keep", KEEP, UNRESERVED_KEYWORD)
 PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD)
+PG_KEYWORD("keys", KEYS, UNRESERVED_KEYWORD)
 PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD)
 PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD)
 PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD)
@@ -277,6 +292,7 @@ PG_KEYWORD("off", OFF, UNRESERVED_KEYWORD)
 PG_KEYWORD("offset", OFFSET, RESERVED_KEYWORD)
 PG_KEYWORD("oids", OIDS, UNRESERVED_KEYWORD)
 PG_KEYWORD("old", OLD, UNRESERVED_KEYWORD)
+PG_KEYWORD("omit", OMIT, UNRESERVED_KEYWORD)
 PG_KEYWORD("on", ON, RESERVED_KEYWORD)
 PG_KEYWORD("only", ONLY, RESERVED_KEYWORD)
 PG_KEYWORD("operator", OPERATOR, UNRESERVED_KEYWORD)
@@ -318,6 +334,7 @@ PG_KEYWORD("procedures", PROCEDURES, UNRESERVED_KEYWORD)
 PG_KEYWORD("program", PROGRAM, UNRESERVED_KEYWORD)
 PG_KEYWORD("publication", PUBLICATION, UNRESERVED_KEYWORD)
 PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD)
+PG_KEYWORD("quotes", QUOTES, UNRESERVED_KEYWORD)
 PG_KEYWORD("range", RANGE, UNRESERVED_KEYWORD)
 PG_KEYWORD("read", READ, UNRESERVED_KEYWORD)
 PG_KEYWORD("real", REAL, COL_NAME_KEYWORD)
@@ -351,6 +368,7 @@ PG_KEYWORD("row", ROW, COL_NAME_KEYWORD)
 PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD)
 PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD)
 PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD)
+PG_KEYWORD("scalar", SCALAR, UNRESERVED_KEYWORD)
 PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD)
 PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD)
 PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD)
@@ -385,6 +403,7 @@ PG_KEYWORD("stdin", STDIN, UNRESERVED_KEYWORD)
 PG_KEYWORD("stdout", STDOUT, UNRESERVED_KEYWORD)
 PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD)
 PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("string", STRING, COL_NAME_KEYWORD)
 PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD)
@@ -417,6 +436,7 @@ PG_KEYWORD("type", TYPE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("types", TYPES_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("unbounded", UNBOUNDED, UNRESERVED_KEYWORD)
 PG_KEYWORD("uncommitted", UNCOMMITTED, UNRESERVED_KEYWORD)
+PG_KEYWORD("unconditional", UNCONDITIONAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("unencrypted", UNENCRYPTED, UNRESERVED_KEYWORD)
 PG_KEYWORD("union", UNION, RESERVED_KEYWORD)
 PG_KEYWORD("unique", UNIQUE, RESERVED_KEYWORD)
diff --git a/src/include/utils/jsonapi.h b/src/include/utils/jsonapi.h
index 6ef601f..f410bd9 100644
--- a/src/include/utils/jsonapi.h
+++ b/src/include/utils/jsonapi.h
@@ -206,6 +206,10 @@ extern text *transform_json_string_values(text *json, void *action_state,
 
 extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid, int *tz);
 
+extern Datum json_populate_type(Datum json_val, Oid json_type,
+								Oid typid, int32 typmod,
+								void **cache, MemoryContext mcxt, bool *isnull);
+
 extern Json *JsonCreate(text *json);
 extern JsonbIteratorToken JsonIteratorNext(JsonIterator **pit, JsonbValue *val,
 				 bool skipNested);
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 2ea1ec1..d312b6c 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -401,6 +401,9 @@ extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
 extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
 					 int estimated_len);
 
+extern Jsonb *JsonbMakeEmptyArray(void);
+extern Jsonb *JsonbMakeEmptyObject(void);
+extern char *JsonbUnquote(Jsonb *jb);
 extern JsonbValue *JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res);
 
 #endif							/* __JSONB_H__ */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index c808fb1..c349e10 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -17,6 +17,7 @@
 #include "fmgr.h"
 #include "utils/jsonb.h"
 #include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
 
 typedef struct
 {
@@ -276,7 +277,15 @@ typedef struct JsonPathVariable	{
 	void					*cb_arg;
 } JsonPathVariable;
 
-
+typedef struct JsonPathVariableEvalContext
+{
+	JsonPathVariable var;
+	struct ExprContext *econtext;
+	struct ExprState  *estate;
+	Datum		value;
+	bool		isnull;
+	bool		evaluated;
+} JsonPathVariableEvalContext;
 
 typedef struct JsonValueList
 {
@@ -289,4 +298,12 @@ JsonPathExecResult	executeJsonPath(JsonPath *path,
 									Jsonb *json,
 									JsonValueList *foundJson);
 
+extern bool  JsonbPathExists(Datum jb, JsonPath *path, List *vars);
+extern Datum JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
+			   bool *empty, List *vars);
+extern JsonbValue *JsonbPathValue(Datum jb, JsonPath *jp, bool *empty,
+			   List *vars);
+
+extern Datum EvalJsonPathVar(void *cxt, bool *isnull);
+
 #endif
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index b20383a..f0b2416 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -45,6 +45,8 @@ my %replace_string = (
 	'NOT_LA'         => 'not',
 	'NULLS_LA'       => 'nulls',
 	'WITH_LA'        => 'with',
+	'WITH_LA_UNIQUE' => 'with',
+	'WITHOUT_LA'     => 'without',
 	'TYPECAST'       => '::',
 	'DOT_DOT'        => '..',
 	'COLON_EQUALS'   => ':=',
diff --git a/src/interfaces/ecpg/preproc/parser.c b/src/interfaces/ecpg/preproc/parser.c
index e5a8f9d..e576202 100644
--- a/src/interfaces/ecpg/preproc/parser.c
+++ b/src/interfaces/ecpg/preproc/parser.c
@@ -84,6 +84,9 @@ filtered_base_yylex(void)
 		case WITH:
 			cur_token_length = 4;
 			break;
+		case WITHOUT:
+			cur_token_length = 7;
+			break;
 		default:
 			return cur_token;
 	}
@@ -155,8 +158,22 @@ filtered_base_yylex(void)
 				case ORDINALITY:
 					cur_token = WITH_LA;
 					break;
+				case UNIQUE:
+					cur_token = WITH_LA_UNIQUE;
+					break;
+			}
+			break;
+
+		case WITHOUT:
+			/* Replace WITHOUT by WITHOUT_LA if it's followed by TIME */
+			switch (next_token)
+			{
+				case TIME:
+					cur_token = WITHOUT_LA;
+					break;
 			}
 			break;
+
 	}
 
 	return cur_token;
diff --git a/src/test/regress/expected/json_sqljson.out b/src/test/regress/expected/json_sqljson.out
new file mode 100644
index 0000000..bb62634
--- /dev/null
+++ b/src/test/regress/expected/json_sqljson.out
@@ -0,0 +1,15 @@
+-- JSON_EXISTS
+SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
+ERROR:  JSON_EXISTS() is not yet implemented for json type
+LINE 1: SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
+               ^
+-- JSON_VALUE
+SELECT JSON_VALUE(NULL FORMAT JSON, '$');
+ERROR:  JSON_VALUE() is not yet implemented for json type
+LINE 1: SELECT JSON_VALUE(NULL FORMAT JSON, '$');
+               ^
+-- JSON_QUERY
+SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+ERROR:  JSON_QUERY() is not yet implemented for json type
+LINE 1: SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+               ^
diff --git a/src/test/regress/expected/jsonb_sqljson.out b/src/test/regress/expected/jsonb_sqljson.out
new file mode 100644
index 0000000..c516870
--- /dev/null
+++ b/src/test/regress/expected/jsonb_sqljson.out
@@ -0,0 +1,992 @@
+-- JSON_EXISTS
+SELECT JSON_EXISTS(NULL::jsonb, '$');
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '[]', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(JSON_OBJECT(RETURNING jsonb), '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '1', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb 'null', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '[]', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '1', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '1', 'strict $.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '1', 'strict $.a' ERROR ON ERROR);
+ERROR:  SQL/JSON member not found
+SELECT JSON_EXISTS(jsonb 'null', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '[]', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '[1, "aaa", {"a": 1}]', 'strict $.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '[1, "aaa", {"a": 1}]', 'lax $.a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{}', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"b": 1, "a": 2}', '$.a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '1', '$.a.b');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"a": {"b": 1}}', '$.a.b');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.a.b');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING 1 AS x);
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING '1' AS x);
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 2 AS y);
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 1 AS y);
+ json_exists 
+-------------
+ f
+(1 row)
+
+-- extension: boolean expressions
+SELECT JSON_EXISTS(jsonb '1', '$ > 2');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
+ json_exists 
+-------------
+ t
+(1 row)
+
+-- JSON_VALUE
+SELECT JSON_VALUE(NULL::jsonb, '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$' RETURNING int);
+ json_value 
+------------
+           
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'true', '$');
+ json_value 
+------------
+ true
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'true', '$' RETURNING bool);
+ json_value 
+------------
+ t
+(1 row)
+
+SELECT JSON_VALUE(jsonb '123', '$');
+ json_value 
+------------
+ 123
+(1 row)
+
+SELECT JSON_VALUE(jsonb '123', '$' RETURNING int) + 234;
+ ?column? 
+----------
+      357
+(1 row)
+
+SELECT JSON_VALUE(jsonb '123', '$' RETURNING text);
+ json_value 
+------------
+ 123
+(1 row)
+
+/* jsonb bytea ??? */
+SELECT JSON_VALUE(jsonb '123', '$' RETURNING bytea);
+ json_value 
+------------
+ \x313233
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1.23', '$');
+ json_value 
+------------
+ 1.23
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
+ json_value 
+------------
+          1
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
+ json_value 
+------------
+       1.23
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING int ERROR ON ERROR);
+ERROR:  invalid input syntax for integer: "1.23"
+SELECT JSON_VALUE(jsonb '"aaa"', '$');
+ json_value 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING text);
+ json_value 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING char(5));
+ json_value 
+------------
+ aaa  
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING char(2));
+ json_value 
+------------
+ aa
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING json);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING jsonb);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING json ERROR ON ERROR);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING jsonb ERROR ON ERROR);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"\"aaa\""', '$' RETURNING json);
+ json_value 
+------------
+ "\"aaa\""
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"\"aaa\""', '$' RETURNING jsonb);
+ json_value 
+------------
+ "\"aaa\""
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int);
+ json_value 
+------------
+           
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int ERROR ON ERROR);
+ERROR:  invalid input syntax for integer: "aaa"
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int DEFAULT 111 ON ERROR);
+ json_value 
+------------
+        111
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"123"', '$' RETURNING int) + 234;
+ ?column? 
+----------
+      357
+(1 row)
+
+SELECT JSON_VALUE(jsonb '"2017-02-20"', '$' RETURNING date) + 9;
+  ?column?  
+------------
+ 03-01-2017
+(1 row)
+
+-- Test NULL checks execution in domain types
+CREATE DOMAIN sqljsonb_int_not_null AS int NOT NULL;
+SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null);
+ERROR:  domain sqljsonb_int_not_null does not allow null values
+SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null NULL ON ERROR);
+ERROR:  domain sqljsonb_int_not_null does not allow null values
+SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null DEFAULT NULL ON ERROR);
+ERROR:  domain sqljsonb_int_not_null does not allow null values
+SELECT JSON_VALUE(jsonb '[]', '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '[]', '$' ERROR ON ERROR);
+ERROR:  SQL/JSON scalar required
+SELECT JSON_VALUE(jsonb '{}', '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '{}', '$' ERROR ON ERROR);
+ERROR:  SQL/JSON scalar required
+SELECT JSON_VALUE(jsonb '1', '$.a');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'strict $.a' ERROR ON ERROR);
+ERROR:  SQL/JSON member not found
+SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 'error' ON ERROR);
+ json_value 
+------------
+ error
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY ERROR ON ERROR);
+ERROR:  no SQL/JSON item
+SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 2 ON ERROR);
+ json_value 
+------------
+ 2
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT 2 ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' NULL ON EMPTY DEFAULT '2' ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON EMPTY DEFAULT '3' ON ERROR);
+ json_value 
+------------
+ 2
+(1 row)
+
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY DEFAULT '3' ON ERROR);
+ json_value 
+------------
+ 3
+(1 row)
+
+SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
+ERROR:  more than one SQL/JSON item
+SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' DEFAULT '0' ON ERROR);
+ json_value 
+------------
+ 0
+(1 row)
+
+SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int ERROR ON ERROR);
+ERROR:  invalid input syntax for integer: " "
+SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+ json_value 
+------------
+          5
+(1 row)
+
+SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+ json_value 
+------------
+          1
+(1 row)
+
+SELECT
+	x,
+	JSON_VALUE(
+		jsonb '{"a": 1, "b": 2}',
+		'$.* ? (@ > $x)' PASSING x AS x
+		RETURNING int
+		DEFAULT -1 ON EMPTY
+		DEFAULT -2 ON ERROR
+	) y
+FROM
+	generate_series(0, 2) x;
+ x | y  
+---+----
+ 0 | -2
+ 1 |  2
+ 2 | -1
+(3 rows)
+
+SELECT JSON_VALUE(jsonb 'null', '$a' PASSING point ' (1, 2 )' AS a);
+ json_value 
+------------
+ (1,2)
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$a' PASSING point ' (1, 2 )' AS a RETURNING point);
+ json_value 
+------------
+ (1,2)
+(1 row)
+
+-- Test timestamptz passing and output
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+          json_value          
+------------------------------
+ Tue Feb 20 18:34:56 2018 PST
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamptz);
+          json_value          
+------------------------------
+ Tue Feb 20 18:34:56 2018 PST
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamp);
+        json_value        
+--------------------------
+ Tue Feb 20 18:34:56 2018
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+         json_value          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+         json_value          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+-- JSON_QUERY
+SELECT
+	JSON_QUERY(js, '$'),
+	JSON_QUERY(js, '$' WITHOUT WRAPPER),
+	JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
+	JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
+	JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+FROM
+	(VALUES
+		(jsonb 'null'),
+		('12.3'),
+		('true'),
+		('"aaa"'),
+		('[1, null, "2"]'),
+		('{"a": 1, "b": [2]}')
+	) foo(js);
+     json_query     |     json_query     |     json_query     |      json_query      |      json_query      
+--------------------+--------------------+--------------------+----------------------+----------------------
+ null               | null               | [null]             | [null]               | [null]
+ 12.3               | 12.3               | [12.3]             | [12.3]               | [12.3]
+ true               | true               | [true]             | [true]               | [true]
+ "aaa"              | "aaa"              | ["aaa"]            | ["aaa"]              | ["aaa"]
+ [1, null, "2"]     | [1, null, "2"]     | [1, null, "2"]     | [[1, null, "2"]]     | [[1, null, "2"]]
+ {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
+(6 rows)
+
+SELECT
+	JSON_QUERY(js, 'strict $[*]') AS "unspec",
+	JSON_QUERY(js, 'strict $[*]' WITHOUT WRAPPER) AS "without",
+	JSON_QUERY(js, 'strict $[*]' WITH CONDITIONAL WRAPPER) AS "with cond",
+	JSON_QUERY(js, 'strict $[*]' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+	JSON_QUERY(js, 'strict $[*]' WITH ARRAY WRAPPER) AS "with"
+FROM
+	(VALUES
+		(jsonb '1'),
+		('[]'),
+		('[null]'),
+		('[12.3]'),
+		('[true]'),
+		('["aaa"]'),
+		('[[1, 2, 3]]'),
+		('[{"a": 1, "b": [2]}]'),
+		('[1, "2", null, [3]]')
+	) foo(js);
+       unspec       |      without       |      with cond      |     with uncond      |         with         
+--------------------+--------------------+---------------------+----------------------+----------------------
+                    |                    |                     |                      | 
+                    |                    |                     |                      | 
+ null               | null               | [null]              | [null]               | [null]
+ 12.3               | 12.3               | [12.3]              | [12.3]               | [12.3]
+ true               | true               | [true]              | [true]               | [true]
+ "aaa"              | "aaa"              | ["aaa"]             | ["aaa"]              | ["aaa"]
+ [1, 2, 3]          | [1, 2, 3]          | [1, 2, 3]           | [[1, 2, 3]]          | [[1, 2, 3]]
+ {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]}  | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
+                    |                    | [1, "2", null, [3]] | [1, "2", null, [3]]  | [1, "2", null, [3]]
+(9 rows)
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text);
+ json_query 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text KEEP QUOTES);
+ json_query 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text KEEP QUOTES ON SCALAR STRING);
+ json_query 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text OMIT QUOTES);
+ json_query 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text OMIT QUOTES ON SCALAR STRING);
+ json_query 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' OMIT QUOTES ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  Token "aaa" is invalid.
+CONTEXT:  JSON data, line 1: aaa
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING json OMIT QUOTES ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  Token "aaa" is invalid.
+CONTEXT:  JSON data, line 1: aaa
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING bytea FORMAT JSON OMIT QUOTES ERROR ON ERROR);
+ json_query 
+------------
+ \x616161
+(1 row)
+
+-- QUOTES behavior should not be specified when WITH WRAPPER used:
+-- Should fail
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER OMIT QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER OMIT QUOTES)...
+                                                        ^
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER KEEP QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER KEEP QUOTES)...
+                                                        ^
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: ...N_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTE...
+                                                             ^
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: ...N_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTE...
+                                                             ^
+-- Should succeed
+SELECT JSON_QUERY(jsonb '[1]', '$' WITHOUT WRAPPER OMIT QUOTES);
+ json_query 
+------------
+ [1]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1]', '$' WITHOUT WRAPPER KEEP QUOTES);
+ json_query 
+------------
+ [1]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]');
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' NULL ON EMPTY);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY ARRAY ON EMPTY);
+ json_query 
+------------
+ []
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY OBJECT ON EMPTY);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY NULL ON ERROR);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY ARRAY ON ERROR);
+ json_query 
+------------
+ []
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY ERROR ON ERROR);
+ERROR:  no SQL/JSON item
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON ERROR);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
+ERROR:  more than one SQL/JSON item
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING json);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING json FORMAT JSON);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING jsonb);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING jsonb FORMAT JSON);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING text);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING char(10));
+ json_query 
+------------
+ [1, 2]    
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING char(3));
+ json_query 
+------------
+ [1,
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING text FORMAT JSON);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea);
+   json_query   
+----------------
+ \x5b312c20325d
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea FORMAT JSON);
+   json_query   
+----------------
+ \x5b312c20325d
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea FORMAT JSON EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING json EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING jsonb EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT
+	x, y,
+	JSON_QUERY(
+		jsonb '[1,2,3,4,5,null]',
+		'$[*] ? (@ >= $x && @ <= $y)'
+		PASSING x AS x, y AS y
+		WITH CONDITIONAL WRAPPER
+		EMPTY ARRAY ON EMPTY
+	) list
+FROM
+	generate_series(0, 4) x,
+	generate_series(0, 4) y;
+ x | y |     list     
+---+---+--------------
+ 0 | 0 | []
+ 0 | 1 | [1]
+ 0 | 2 | [1, 2]
+ 0 | 3 | [1, 2, 3]
+ 0 | 4 | [1, 2, 3, 4]
+ 1 | 0 | []
+ 1 | 1 | [1]
+ 1 | 2 | [1, 2]
+ 1 | 3 | [1, 2, 3]
+ 1 | 4 | [1, 2, 3, 4]
+ 2 | 0 | []
+ 2 | 1 | []
+ 2 | 2 | [2]
+ 2 | 3 | [2, 3]
+ 2 | 4 | [2, 3, 4]
+ 3 | 0 | []
+ 3 | 1 | []
+ 3 | 2 | []
+ 3 | 3 | [3]
+ 3 | 4 | [3, 4]
+ 4 | 0 | []
+ 4 | 1 | []
+ 4 | 2 | []
+ 4 | 3 | []
+ 4 | 4 | [4]
+(25 rows)
+
+-- Extension: record types returning
+CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
+CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
+SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}},  {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
+                     json_query                      
+-----------------------------------------------------
+ (1,aaa,"[1, ""2"", {}]","{""x"": [1, ""2"", {}]}",)
+(1 row)
+
+SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa":  [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
+         unnest         
+------------------------
+ {"a": 1, "b": ["foo"]}
+ {"a": 2, "c": {}}
+ 123
+(3 rows)
+
+SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
+ a |      t      | js |     jb     | jsa 
+---+-------------+----+------------+-----
+ 1 | ["foo", []] |    |            | 
+ 2 |             |    | [{}, true] | 
+(2 rows)
+
+-- Extension: array types returning
+SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
+  json_query  
+--------------
+ {1,2,NULL,3}
+(1 row)
+
+SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
+ a |      t      | js |     jb     | jsa 
+---+-------------+----+------------+-----
+ 1 | ["foo", []] |    |            | 
+ 2 |             |    | [{}, true] | 
+(2 rows)
+
+-- Extension: domain types returning
+SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
+ json_query 
+------------
+          1
+(1 row)
+
+SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
+ERROR:  domain sqljsonb_int_not_null does not allow null values
+-- Test timestamptz passing and output
+SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+         json_query          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+         json_query          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+         json_query          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+-- Test constraints
+CREATE TABLE test_jsonb_constraints (
+	js text,
+	i int,
+	x jsonb DEFAULT JSON_QUERY(jsonb '[1,2]', '$[*]' WITH WRAPPER)
+	CONSTRAINT test_jsonb_constraint1
+		CHECK (js IS JSON)
+	CONSTRAINT test_jsonb_constraint2
+		CHECK (JSON_EXISTS(js::jsonb, '$.a' PASSING i + 5 AS int, i::text AS txt, array[1,2,3] as arr))
+	CONSTRAINT test_jsonb_constraint3
+		CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT ('12' || i)::int ON EMPTY ERROR ON ERROR) > i)
+	CONSTRAINT test_jsonb_constraint4
+		CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
+	CONSTRAINT test_jsonb_constraint5
+		CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) >  'a')
+);
+\d test_jsonb_constraints
+                                                        Table "public.test_jsonb_constraints"
+ Column |  Type   | Collation | Nullable |                                                  Default                                                   
+--------+---------+-----------+----------+------------------------------------------------------------------------------------------------------------
+ js     | text    |           |          | 
+ i      | integer |           |          | 
+ x      | jsonb   |           |          | JSON_QUERY('[1, 2]'::jsonb, '$[*]' RETURNING jsonb WITH UNCONDITIONAL WRAPPER NULL ON EMPTY NULL ON ERROR)
+Check constraints:
+    "test_jsonb_constraint1" CHECK (js IS JSON)
+    "test_jsonb_constraint2" CHECK (JSON_EXISTS(js::jsonb, '$."a"' PASSING i + 5 AS int, i::text AS txt, ARRAY[1, 2, 3] AS arr FALSE ON ERROR))
+    "test_jsonb_constraint3" CHECK (JSON_VALUE(js::jsonb, '$."a"' RETURNING integer DEFAULT ('12'::text || i)::integer ON EMPTY ERROR ON ERROR) > i)
+    "test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER NULL ON EMPTY EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
+    "test_jsonb_constraint5" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY NULL ON ERROR) > 'a'::bpchar)
+
+SELECT check_clause
+FROM information_schema.check_constraints
+WHERE constraint_name LIKE 'test_jsonb_constraint%';
+                                                           check_clause                                                            
+-----------------------------------------------------------------------------------------------------------------------------------
+ ((js IS JSON))
+ (JSON_EXISTS((js)::jsonb, '$."a"' PASSING (i + 5) AS int, (i)::text AS txt, ARRAY[1, 2, 3] AS arr FALSE ON ERROR))
+ ((JSON_VALUE((js)::jsonb, '$."a"' RETURNING integer DEFAULT (('12'::text || i))::integer ON EMPTY ERROR ON ERROR) > i))
+ ((JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER NULL ON EMPTY EMPTY OBJECT ON ERROR) < '[10]'::jsonb))
+ ((JSON_QUERY((js)::jsonb, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY NULL ON ERROR) > 'a'::bpchar))
+(5 rows)
+
+SELECT adsrc FROM pg_attrdef WHERE adrelid = 'test_jsonb_constraints'::regclass;
+                                                   adsrc                                                    
+------------------------------------------------------------------------------------------------------------
+ JSON_QUERY('[1, 2]'::jsonb, '$[*]' RETURNING jsonb WITH UNCONDITIONAL WRAPPER NULL ON EMPTY NULL ON ERROR)
+(1 row)
+
+INSERT INTO test_jsonb_constraints VALUES ('', 1);
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint1"
+DETAIL:  Failing row contains (, 1, [1, 2]).
+INSERT INTO test_jsonb_constraints VALUES ('1', 1);
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint2"
+DETAIL:  Failing row contains (1, 1, [1, 2]).
+INSERT INTO test_jsonb_constraints VALUES ('[]');
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint2"
+DETAIL:  Failing row contains ([], null, [1, 2]).
+INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1);
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint2"
+DETAIL:  Failing row contains ({"b": 1}, 1, [1, 2]).
+INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint3"
+DETAIL:  Failing row contains ({"a": 1}, 1, [1, 2]).
+INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"
+DETAIL:  Failing row contains ({"a": 7}, 1, [1, 2]).
+INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
+ERROR:  new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
+DETAIL:  Failing row contains ({"a": 10}, 1, [1, 2]).
+DROP TABLE test_jsonb_constraints;
+-- Extension: non-constant JSON path
+SELECT JSON_EXISTS(jsonb '{"a": 123}', '$' || '.' || 'a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a');
+ json_value 
+------------
+ 123
+(1 row)
+
+SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
+ json_value 
+------------
+ foo
+(1 row)
+
+SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
+ json_query 
+------------
+ 123
+(1 row)
+
+SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
+ json_query 
+------------
+ [123]
+(1 row)
+
+-- Should fail (invalid path)
+SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
+ERROR:  bad jsonpath representation
+DETAIL:  syntax error, unexpected IDENT_P at or near " "
+-- Test parallel JSON_VALUE()
+CREATE TABLE test_parallel_jsonb_value AS
+SELECT i::text::jsonb AS js
+FROM generate_series(1, 1000000) i;
+-- Should be non-parallel due to subtransactions
+EXPLAIN (COSTS OFF)
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
+                 QUERY PLAN                  
+---------------------------------------------
+ Aggregate
+   ->  Seq Scan on test_parallel_jsonb_value
+(2 rows)
+
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
+     sum      
+--------------
+ 500000500000
+(1 row)
+
+-- Should be parallel
+EXPLAIN (COSTS OFF)
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Finalize Aggregate
+   ->  Gather
+         Workers Planned: 2
+         ->  Partial Aggregate
+               ->  Parallel Seq Scan on test_parallel_jsonb_value
+(5 rows)
+
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
+     sum      
+--------------
+ 500000500000
+(1 row)
+
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index dae99a0..b8a75ee 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -206,11 +206,12 @@ WHERE p1.oid != p2.oid AND
 ORDER BY 1, 2;
  proargtypes | proargtypes 
 -------------+-------------
+          25 |         114
           25 |        1042
           25 |        1043
         1114 |        1184
         1560 |        1562
-(4 rows)
+(5 rows)
 
 SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
 FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out
new file mode 100644
index 0000000..be2add5
--- /dev/null
+++ b/src/test/regress/expected/sqljson.out
@@ -0,0 +1,940 @@
+-- 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
+  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
+  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:  argument 3 cannot be null
+HINT:  Object keys should be text.
+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", "1.23" : 123, "c" : [ 1,true,{ } ], "d" : {"x": 123.45}}
+(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 key "1"
+SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE RETURNING jsonb);
+ERROR:  duplicate JSON key "1"
+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 key "1"
+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)
+
+SELECT
+	JSON_ARRAYAGG(bar),
+	JSON_ARRAYAGG(bar RETURNING jsonb),
+	JSON_ARRAYAGG(bar ABSENT ON NULL),
+	JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb),
+	JSON_ARRAYAGG(bar NULL ON NULL),
+	JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb),
+	JSON_ARRAYAGG(foo),
+	JSON_ARRAYAGG(foo RETURNING jsonb),
+	JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2),
+	JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2)
+FROM
+	(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
+  json_arrayagg  |  json_arrayagg  |  json_arrayagg  |  json_arrayagg  |              json_arrayagg              |              json_arrayagg              |  json_arrayagg  |                                                      json_arrayagg                                                       | json_arrayagg |            json_arrayagg             
+-----------------+-----------------+-----------------+-----------------+-----------------------------------------+-----------------------------------------+-----------------+--------------------------------------------------------------------------------------------------------------------------+---------------+--------------------------------------
+ [3, 1, 5, 2, 4] | [3, 1, 5, 2, 4] | [3, 1, 5, 2, 4] | [3, 1, 5, 2, 4] | [null, 3, 1, null, null, 5, 2, 4, null] | [null, 3, 1, null, null, 5, 2, 4, null] | [{"bar":null}, +| [{"bar": null}, {"bar": 3}, {"bar": 1}, {"bar": null}, {"bar": null}, {"bar": 5}, {"bar": 2}, {"bar": 4}, {"bar": null}] | [{"bar":3},  +| [{"bar": 3}, {"bar": 4}, {"bar": 5}]
+                 |                 |                 |                 |                                         |                                         |  {"bar":3},    +|                                                                                                                          |  {"bar":4},  +| 
+                 |                 |                 |                 |                                         |                                         |  {"bar":1},    +|                                                                                                                          |  {"bar":5}]   | 
+                 |                 |                 |                 |                                         |                                         |  {"bar":null}, +|                                                                                                                          |               | 
+                 |                 |                 |                 |                                         |                                         |  {"bar":null}, +|                                                                                                                          |               | 
+                 |                 |                 |                 |                                         |                                         |  {"bar":5},    +|                                                                                                                          |               | 
+                 |                 |                 |                 |                                         |                                         |  {"bar":2},    +|                                                                                                                          |               | 
+                 |                 |                 |                 |                                         |                                         |  {"bar":4},    +|                                                                                                                          |               | 
+                 |                 |                 |                 |                                         |                                         |  {"bar":null}]  |                                                                                                                          |               | 
+(1 row)
+
+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:  field name must not be null
+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 key "1"
+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 key "1"
+-- 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 FORMAT 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 FORMAT 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.i : (('111'::text || i.i)::bytea) FORMAT JSON WITH UNIQUE KEYS RETURNING text) FILTER (WHERE i.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.i)::bytea) FORMAT JSON NULL ON NULL RETURNING text) FILTER (WHERE i.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;
+-- IS JSON predicate
+SELECT NULL IS JSON;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT NULL IS NOT JSON;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT NULL::json IS JSON;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT NULL::jsonb IS JSON;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT NULL::text IS JSON;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT NULL::bytea IS JSON;
+ ?column? 
+----------
+ 
+(1 row)
+
+SELECT NULL::int IS JSON;
+ERROR:  cannot use type integer in IS JSON predicate
+SELECT '' IS JSON;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT bytea '\x00' IS JSON;
+ERROR:  invalid byte sequence for encoding "UTF8": 0x00
+CREATE TABLE test_is_json (js text);
+INSERT INTO test_is_json VALUES
+ (NULL),
+ (''),
+ ('123'),
+ ('"aaa "'),
+ ('true'),
+ ('null'),
+ ('[]'),
+ ('[1, "2", {}]'),
+ ('{}'),
+ ('{ "a": 1, "b": null }'),
+ ('{ "a": 1, "a": null }'),
+ ('{ "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }'),
+ ('{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }'),
+ ('aaa'),
+ ('{a:1}'),
+ ('["a",]');
+SELECT
+	js,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	test_is_json;
+                      js                       | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE 
+-----------------------------------------------+---------+-------------+----------+-----------+----------+-----------+----------------+-------------
+                                               |         |             |          |           |          |           |                | 
+                                               | f       | t           | f        | f         | f        | f         | f              | f
+ 123                                           | t       | f           | t        | f         | f        | t         | t              | t
+ "aaa "                                        | t       | f           | t        | f         | f        | t         | t              | t
+ true                                          | t       | f           | t        | f         | f        | t         | t              | t
+ null                                          | t       | f           | t        | f         | f        | t         | t              | t
+ []                                            | t       | f           | t        | f         | t        | f         | t              | t
+ [1, "2", {}]                                  | t       | f           | t        | f         | t        | f         | t              | t
+ {}                                            | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "b": null }                         | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "a": null }                         | t       | f           | t        | t         | f        | f         | t              | f
+ { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }     | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t       | f           | t        | t         | f        | f         | t              | f
+ aaa                                           | f       | t           | f        | f         | f        | f         | f              | f
+ {a:1}                                         | f       | t           | f        | f         | f        | f         | f              | f
+ ["a",]                                        | f       | t           | f        | f         | f        | f         | f              | f
+(16 rows)
+
+SELECT
+	js,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	(SELECT js::json FROM test_is_json WHERE js IS JSON) foo(js);
+                      js                       | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE 
+-----------------------------------------------+---------+-------------+----------+-----------+----------+-----------+----------------+-------------
+ 123                                           | t       | f           | t        | f         | f        | t         | t              | t
+ "aaa "                                        | t       | f           | t        | f         | f        | t         | t              | t
+ true                                          | t       | f           | t        | f         | f        | t         | t              | t
+ null                                          | t       | f           | t        | f         | f        | t         | t              | t
+ []                                            | t       | f           | t        | f         | t        | f         | t              | t
+ [1, "2", {}]                                  | t       | f           | t        | f         | t        | f         | t              | t
+ {}                                            | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "b": null }                         | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "a": null }                         | t       | f           | t        | t         | f        | f         | t              | f
+ { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }     | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t       | f           | t        | t         | f        | f         | t              | f
+(11 rows)
+
+SELECT
+	js0,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	(SELECT js, js::bytea FROM test_is_json WHERE js IS JSON) foo(js0, js);
+                      js0                      | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE 
+-----------------------------------------------+---------+-------------+----------+-----------+----------+-----------+----------------+-------------
+ 123                                           | t       | f           | t        | f         | f        | t         | t              | t
+ "aaa "                                        | t       | f           | t        | f         | f        | t         | t              | t
+ true                                          | t       | f           | t        | f         | f        | t         | t              | t
+ null                                          | t       | f           | t        | f         | f        | t         | t              | t
+ []                                            | t       | f           | t        | f         | t        | f         | t              | t
+ [1, "2", {}]                                  | t       | f           | t        | f         | t        | f         | t              | t
+ {}                                            | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "b": null }                         | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "a": null }                         | t       | f           | t        | t         | f        | f         | t              | f
+ { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }     | t       | f           | t        | t         | f        | f         | t              | t
+ { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t       | f           | t        | t         | f        | f         | t              | f
+(11 rows)
+
+SELECT
+	js,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	(SELECT js::jsonb FROM test_is_json WHERE js IS JSON) foo(js);
+                 js                  | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE 
+-------------------------------------+---------+-------------+----------+-----------+----------+-----------+----------------+-------------
+ 123                                 | t       | f           | t        | f         | f        | t         | t              | t
+ "aaa "                              | t       | f           | t        | f         | f        | t         | t              | t
+ true                                | t       | f           | t        | f         | f        | t         | t              | t
+ null                                | t       | f           | t        | f         | f        | t         | t              | t
+ []                                  | t       | f           | t        | f         | t        | f         | t              | t
+ [1, "2", {}]                        | t       | f           | t        | f         | t        | f         | t              | t
+ {}                                  | t       | f           | t        | t         | f        | f         | t              | t
+ {"a": 1, "b": null}                 | t       | f           | t        | t         | f        | f         | t              | t
+ {"a": null}                         | t       | f           | t        | t         | f        | f         | t              | t
+ {"a": 1, "b": [{"a": 1}, {"a": 2}]} | t       | f           | t        | t         | f        | f         | t              | t
+ {"a": 1, "b": [{"a": 2, "b": 0}]}   | t       | f           | t        | t         | f        | f         | t              | t
+(11 rows)
+
+-- Test IS JSON deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT JSON ARRAY AS "array", '{}' IS JSON OBJECT WITH UNIQUE AS "object" FROM generate_series(1, 3) i;
+                                                                        QUERY PLAN                                                                        
+----------------------------------------------------------------------------------------------------------------------------------------------------------
+ Function Scan on pg_catalog.generate_series i
+   Output: ('1'::text IS JSON), (('1'::text || (i)::text) IS JSON SCALAR), (NOT ('[]'::text IS JSON ARRAY)), ('{}'::text IS JSON OBJECT WITH UNIQUE KEYS)
+   Function Call: generate_series(1, 3)
+(3 rows)
+
+CREATE VIEW is_json_view AS
+SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT JSON ARRAY AS "array", '{}' IS JSON OBJECT WITH UNIQUE AS "object" FROM generate_series(1, 3) i;
+\sv is_json_view
+CREATE OR REPLACE VIEW public.is_json_view AS
+ SELECT '1'::text IS JSON AS "any",
+    '1'::text || i.i IS JSON SCALAR AS scalar,
+    NOT '[]'::text IS JSON ARRAY AS "array",
+    '{}'::text IS JSON OBJECT WITH UNIQUE KEYS AS object
+   FROM generate_series(1, 3) i(i)
+DROP VIEW is_json_view;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2e0cd2d..498542e 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
 # ----------
 # Another group of parallel tests (JSON related)
 # ----------
-test: json jsonb json_encoding jsonpath json_jsonpath jsonb_jsonpath
+test: json jsonb json_encoding jsonpath json_jsonpath jsonb_jsonpath jsonb_sqljson sqljson
 
 # ----------
 # Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 494cceb..452c214 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -163,6 +163,9 @@ test: json_encoding
 test: jsonpath
 test: json_jsonpath
 test: jsonb_jsonpath
+test: sqljson
+test: json_sqljson
+test: jsonb_sqljson
 test: indirect_toast
 test: equivclass
 test: plancache
diff --git a/src/test/regress/sql/json_sqljson.sql b/src/test/regress/sql/json_sqljson.sql
new file mode 100644
index 0000000..4f30fa4
--- /dev/null
+++ b/src/test/regress/sql/json_sqljson.sql
@@ -0,0 +1,11 @@
+-- JSON_EXISTS
+
+SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
+
+-- JSON_VALUE
+
+SELECT JSON_VALUE(NULL FORMAT JSON, '$');
+
+-- JSON_QUERY
+
+SELECT JSON_QUERY(NULL FORMAT JSON, '$');
diff --git a/src/test/regress/sql/jsonb_sqljson.sql b/src/test/regress/sql/jsonb_sqljson.sql
new file mode 100644
index 0000000..857bef4
--- /dev/null
+++ b/src/test/regress/sql/jsonb_sqljson.sql
@@ -0,0 +1,303 @@
+-- JSON_EXISTS
+
+SELECT JSON_EXISTS(NULL::jsonb, '$');
+
+SELECT JSON_EXISTS(jsonb '[]', '$');
+SELECT JSON_EXISTS(JSON_OBJECT(RETURNING jsonb), '$');
+
+SELECT JSON_EXISTS(jsonb '1', '$');
+SELECT JSON_EXISTS(jsonb 'null', '$');
+SELECT JSON_EXISTS(jsonb '[]', '$');
+
+SELECT JSON_EXISTS(jsonb '1', '$.a');
+SELECT JSON_EXISTS(jsonb '1', 'strict $.a');
+SELECT JSON_EXISTS(jsonb '1', 'strict $.a' ERROR ON ERROR);
+SELECT JSON_EXISTS(jsonb 'null', '$.a');
+SELECT JSON_EXISTS(jsonb '[]', '$.a');
+SELECT JSON_EXISTS(jsonb '[1, "aaa", {"a": 1}]', 'strict $.a');
+SELECT JSON_EXISTS(jsonb '[1, "aaa", {"a": 1}]', 'lax $.a');
+SELECT JSON_EXISTS(jsonb '{}', '$.a');
+SELECT JSON_EXISTS(jsonb '{"b": 1, "a": 2}', '$.a');
+
+SELECT JSON_EXISTS(jsonb '1', '$.a.b');
+SELECT JSON_EXISTS(jsonb '{"a": {"b": 1}}', '$.a.b');
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.a.b');
+
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING 1 AS x);
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING '1' AS x);
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 2 AS y);
+SELECT JSON_EXISTS(jsonb '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 1 AS y);
+
+-- extension: boolean expressions
+SELECT JSON_EXISTS(jsonb '1', '$ > 2');
+SELECT JSON_EXISTS(jsonb '1', '$.a > 2' ERROR ON ERROR);
+
+-- JSON_VALUE
+
+SELECT JSON_VALUE(NULL::jsonb, '$');
+
+SELECT JSON_VALUE(jsonb 'null', '$');
+SELECT JSON_VALUE(jsonb 'null', '$' RETURNING int);
+
+SELECT JSON_VALUE(jsonb 'true', '$');
+SELECT JSON_VALUE(jsonb 'true', '$' RETURNING bool);
+
+SELECT JSON_VALUE(jsonb '123', '$');
+SELECT JSON_VALUE(jsonb '123', '$' RETURNING int) + 234;
+SELECT JSON_VALUE(jsonb '123', '$' RETURNING text);
+/* jsonb bytea ??? */
+SELECT JSON_VALUE(jsonb '123', '$' RETURNING bytea);
+
+SELECT JSON_VALUE(jsonb '1.23', '$');
+SELECT JSON_VALUE(jsonb '1.23', '$' RETURNING int);
+SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING numeric);
+SELECT JSON_VALUE(jsonb '"1.23"', '$' RETURNING int ERROR ON ERROR);
+
+SELECT JSON_VALUE(jsonb '"aaa"', '$');
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING text);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING char(5));
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING char(2));
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING json);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING jsonb);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING json ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING jsonb ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '"\"aaa\""', '$' RETURNING json);
+SELECT JSON_VALUE(jsonb '"\"aaa\""', '$' RETURNING jsonb);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '"aaa"', '$' RETURNING int DEFAULT 111 ON ERROR);
+SELECT JSON_VALUE(jsonb '"123"', '$' RETURNING int) + 234;
+
+SELECT JSON_VALUE(jsonb '"2017-02-20"', '$' RETURNING date) + 9;
+
+-- Test NULL checks execution in domain types
+CREATE DOMAIN sqljsonb_int_not_null AS int NOT NULL;
+SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null);
+SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null NULL ON ERROR);
+SELECT JSON_VALUE(jsonb '1', '$.a' RETURNING sqljsonb_int_not_null DEFAULT NULL ON ERROR);
+
+SELECT JSON_VALUE(jsonb '[]', '$');
+SELECT JSON_VALUE(jsonb '[]', '$' ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '{}', '$');
+SELECT JSON_VALUE(jsonb '{}', '$' ERROR ON ERROR);
+
+SELECT JSON_VALUE(jsonb '1', '$.a');
+SELECT JSON_VALUE(jsonb '1', 'strict $.a' ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 'error' ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'strict $.a' DEFAULT 2 ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT 2 ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' NULL ON EMPTY DEFAULT '2' ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' DEFAULT '2' ON EMPTY DEFAULT '3' ON ERROR);
+SELECT JSON_VALUE(jsonb '1', 'lax $.a' ERROR ON EMPTY DEFAULT '3' ON ERROR);
+
+SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '[1,2]', '$[*]' DEFAULT '0' ON ERROR);
+SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int ERROR ON ERROR);
+SELECT JSON_VALUE(jsonb '[" "]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+SELECT JSON_VALUE(jsonb '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+
+SELECT
+	x,
+	JSON_VALUE(
+		jsonb '{"a": 1, "b": 2}',
+		'$.* ? (@ > $x)' PASSING x AS x
+		RETURNING int
+		DEFAULT -1 ON EMPTY
+		DEFAULT -2 ON ERROR
+	) y
+FROM
+	generate_series(0, 2) x;
+
+SELECT JSON_VALUE(jsonb 'null', '$a' PASSING point ' (1, 2 )' AS a);
+SELECT JSON_VALUE(jsonb 'null', '$a' PASSING point ' (1, 2 )' AS a RETURNING point);
+
+-- Test timestamptz passing and output
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamptz);
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamp);
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+SELECT JSON_VALUE(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+
+-- JSON_QUERY
+
+SELECT
+	JSON_QUERY(js, '$'),
+	JSON_QUERY(js, '$' WITHOUT WRAPPER),
+	JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
+	JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
+	JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+FROM
+	(VALUES
+		(jsonb 'null'),
+		('12.3'),
+		('true'),
+		('"aaa"'),
+		('[1, null, "2"]'),
+		('{"a": 1, "b": [2]}')
+	) foo(js);
+
+SELECT
+	JSON_QUERY(js, 'strict $[*]') AS "unspec",
+	JSON_QUERY(js, 'strict $[*]' WITHOUT WRAPPER) AS "without",
+	JSON_QUERY(js, 'strict $[*]' WITH CONDITIONAL WRAPPER) AS "with cond",
+	JSON_QUERY(js, 'strict $[*]' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+	JSON_QUERY(js, 'strict $[*]' WITH ARRAY WRAPPER) AS "with"
+FROM
+	(VALUES
+		(jsonb '1'),
+		('[]'),
+		('[null]'),
+		('[12.3]'),
+		('[true]'),
+		('["aaa"]'),
+		('[[1, 2, 3]]'),
+		('[{"a": 1, "b": [2]}]'),
+		('[1, "2", null, [3]]')
+	) foo(js);
+
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text KEEP QUOTES);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text KEEP QUOTES ON SCALAR STRING);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text OMIT QUOTES);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING text OMIT QUOTES ON SCALAR STRING);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' OMIT QUOTES ERROR ON ERROR);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING json OMIT QUOTES ERROR ON ERROR);
+SELECT JSON_QUERY(jsonb '"aaa"', '$' RETURNING bytea FORMAT JSON OMIT QUOTES ERROR ON ERROR);
+
+-- QUOTES behavior should not be specified when WITH WRAPPER used:
+-- Should fail
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER OMIT QUOTES);
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH WRAPPER KEEP QUOTES);
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTES);
+SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTES);
+-- Should succeed
+SELECT JSON_QUERY(jsonb '[1]', '$' WITHOUT WRAPPER OMIT QUOTES);
+SELECT JSON_QUERY(jsonb '[1]', '$' WITHOUT WRAPPER KEEP QUOTES);
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]');
+SELECT JSON_QUERY(jsonb '[]', '$[*]' NULL ON EMPTY);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY ARRAY ON EMPTY);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' EMPTY OBJECT ON EMPTY);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY);
+
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY NULL ON ERROR);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY ARRAY ON ERROR);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON EMPTY ERROR ON ERROR);
+SELECT JSON_QUERY(jsonb '[]', '$[*]' ERROR ON ERROR);
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' ERROR ON ERROR);
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING json);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING json FORMAT JSON);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING jsonb);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING jsonb FORMAT JSON);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING text);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING char(10));
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING char(3));
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING text FORMAT JSON);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea);
+SELECT JSON_QUERY(jsonb '[1,2]', '$' RETURNING bytea FORMAT JSON);
+
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING bytea FORMAT JSON EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING json EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(jsonb '[1,2]', '$[*]' RETURNING jsonb EMPTY OBJECT ON ERROR);
+
+SELECT
+	x, y,
+	JSON_QUERY(
+		jsonb '[1,2,3,4,5,null]',
+		'$[*] ? (@ >= $x && @ <= $y)'
+		PASSING x AS x, y AS y
+		WITH CONDITIONAL WRAPPER
+		EMPTY ARRAY ON EMPTY
+	) list
+FROM
+	generate_series(0, 4) x,
+	generate_series(0, 4) y;
+
+-- Extension: record types returning
+CREATE TYPE sqljsonb_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
+CREATE TYPE sqljsonb_reca AS (reca sqljsonb_rec[]);
+
+SELECT JSON_QUERY(jsonb '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}},  {"a": 2}]', '$[0]' RETURNING sqljsonb_rec);
+SELECT * FROM unnest((JSON_QUERY(jsonb '{"jsa":  [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljsonb_rec)).jsa);
+SELECT * FROM unnest((JSON_QUERY(jsonb '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljsonb_reca)).reca);
+
+-- Extension: array types returning
+SELECT JSON_QUERY(jsonb '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
+SELECT * FROM unnest(JSON_QUERY(jsonb '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljsonb_rec[]));
+
+-- Extension: domain types returning
+SELECT JSON_QUERY(jsonb '{"a": 1}', '$.a' RETURNING sqljsonb_int_not_null);
+SELECT JSON_QUERY(jsonb '{"a": 1}', '$.b' RETURNING sqljsonb_int_not_null);
+
+-- Test timestamptz passing and output
+SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+SELECT JSON_QUERY(jsonb 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+
+-- Test constraints
+
+CREATE TABLE test_jsonb_constraints (
+	js text,
+	i int,
+	x jsonb DEFAULT JSON_QUERY(jsonb '[1,2]', '$[*]' WITH WRAPPER)
+	CONSTRAINT test_jsonb_constraint1
+		CHECK (js IS JSON)
+	CONSTRAINT test_jsonb_constraint2
+		CHECK (JSON_EXISTS(js::jsonb, '$.a' PASSING i + 5 AS int, i::text AS txt, array[1,2,3] as arr))
+	CONSTRAINT test_jsonb_constraint3
+		CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT ('12' || i)::int ON EMPTY ERROR ON ERROR) > i)
+	CONSTRAINT test_jsonb_constraint4
+		CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
+	CONSTRAINT test_jsonb_constraint5
+		CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) >  'a')
+);
+
+\d test_jsonb_constraints
+
+SELECT check_clause
+FROM information_schema.check_constraints
+WHERE constraint_name LIKE 'test_jsonb_constraint%';
+
+SELECT adsrc FROM pg_attrdef WHERE adrelid = 'test_jsonb_constraints'::regclass;
+
+INSERT INTO test_jsonb_constraints VALUES ('', 1);
+INSERT INTO test_jsonb_constraints VALUES ('1', 1);
+INSERT INTO test_jsonb_constraints VALUES ('[]');
+INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1);
+INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
+INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
+INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
+
+DROP TABLE test_jsonb_constraints;
+
+-- Extension: non-constant JSON path
+SELECT JSON_EXISTS(jsonb '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
+SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
+-- Should fail (invalid path)
+SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error');
+
+-- Test parallel JSON_VALUE()
+CREATE TABLE test_parallel_jsonb_value AS
+SELECT i::text::jsonb AS js
+FROM generate_series(1, 1000000) i;
+
+-- Should be non-parallel due to subtransactions
+EXPLAIN (COSTS OFF)
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric)) FROM test_parallel_jsonb_value;
+
+-- Should be parallel
+EXPLAIN (COSTS OFF)
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
+SELECT sum(JSON_VALUE(js, '$' RETURNING numeric ERROR ON ERROR)) FROM test_parallel_jsonb_value;
+
diff --git a/src/test/regress/sql/sqljson.sql b/src/test/regress/sql/sqljson.sql
new file mode 100644
index 0000000..4f3c06d
--- /dev/null
+++ b/src/test/regress/sql/sqljson.sql
@@ -0,0 +1,378 @@
+-- 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);
+
+SELECT
+	JSON_ARRAYAGG(bar),
+	JSON_ARRAYAGG(bar RETURNING jsonb),
+	JSON_ARRAYAGG(bar ABSENT ON NULL),
+	JSON_ARRAYAGG(bar ABSENT ON NULL RETURNING jsonb),
+	JSON_ARRAYAGG(bar NULL ON NULL),
+	JSON_ARRAYAGG(bar NULL ON NULL RETURNING jsonb),
+	JSON_ARRAYAGG(foo),
+	JSON_ARRAYAGG(foo RETURNING jsonb),
+	JSON_ARRAYAGG(foo ORDER BY bar) FILTER (WHERE bar > 2),
+	JSON_ARRAYAGG(foo ORDER BY bar RETURNING jsonb) FILTER (WHERE bar > 2)
+FROM
+	(VALUES (NULL), (3), (1), (NULL), (NULL), (5), (2), (4), (NULL)) foo(bar);
+
+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;
+
+-- IS JSON predicate
+SELECT NULL IS JSON;
+SELECT NULL IS NOT JSON;
+SELECT NULL::json IS JSON;
+SELECT NULL::jsonb IS JSON;
+SELECT NULL::text IS JSON;
+SELECT NULL::bytea IS JSON;
+SELECT NULL::int IS JSON;
+
+SELECT '' IS JSON;
+
+SELECT bytea '\x00' IS JSON;
+
+CREATE TABLE test_is_json (js text);
+
+INSERT INTO test_is_json VALUES
+ (NULL),
+ (''),
+ ('123'),
+ ('"aaa "'),
+ ('true'),
+ ('null'),
+ ('[]'),
+ ('[1, "2", {}]'),
+ ('{}'),
+ ('{ "a": 1, "b": null }'),
+ ('{ "a": 1, "a": null }'),
+ ('{ "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }'),
+ ('{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }'),
+ ('aaa'),
+ ('{a:1}'),
+ ('["a",]');
+
+SELECT
+	js,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	test_is_json;
+
+SELECT
+	js,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	(SELECT js::json FROM test_is_json WHERE js IS JSON) foo(js);
+
+SELECT
+	js0,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	(SELECT js, js::bytea FROM test_is_json WHERE js IS JSON) foo(js0, js);
+
+SELECT
+	js,
+	js IS JSON "IS JSON",
+	js IS NOT JSON "IS NOT JSON",
+	js IS JSON VALUE "IS VALUE",
+	js IS JSON OBJECT "IS OBJECT",
+	js IS JSON ARRAY "IS ARRAY",
+	js IS JSON SCALAR "IS SCALAR",
+	js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE",
+	js IS JSON WITH UNIQUE KEYS "WITH UNIQUE"
+FROM
+	(SELECT js::jsonb FROM test_is_json WHERE js IS JSON) foo(js);
+
+-- Test IS JSON deparsing
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT JSON ARRAY AS "array", '{}' IS JSON OBJECT WITH UNIQUE AS "object" FROM generate_series(1, 3) i;
+
+CREATE VIEW is_json_view AS
+SELECT '1' IS JSON AS "any", ('1' || i) IS JSON SCALAR AS "scalar", '[]' IS NOT JSON ARRAY AS "array", '{}' IS JSON OBJECT WITH UNIQUE AS "object" FROM generate_series(1, 3) i;
+
+\sv is_json_view
+
+DROP VIEW is_json_view;

  [text/x-patch] 0013-sqljson-json-v15.patch (55.8K, ../e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru/5-0013-sqljson-json-v15.patch)
  download | inline diff:
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c619a56..b3b3b08 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4146,17 +4146,21 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
  */
 static Datum
 ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
-					 ExprState *default_estate, bool *is_null)
+					 ExprState *default_estate, bool is_jsonb, bool *is_null)
 {
 	*is_null = false;
 
 	switch (behavior->btype)
 	{
 		case JSON_BEHAVIOR_EMPTY_ARRAY:
-			return JsonbPGetDatum(JsonbMakeEmptyArray());
+			return is_jsonb
+				? JsonbPGetDatum(JsonbMakeEmptyArray())
+				: PointerGetDatum(cstring_to_text("[]"));
 
 		case JSON_BEHAVIOR_EMPTY_OBJECT:
-			return JsonbPGetDatum(JsonbMakeEmptyObject());
+			return is_jsonb
+				? JsonbPGetDatum(JsonbMakeEmptyObject())
+				: PointerGetDatum(cstring_to_text("{}"));
 
 		case JSON_BEHAVIOR_TRUE:
 			return BoolGetDatum(true);
@@ -4183,17 +4187,20 @@ ExecEvalJsonBehavior(ExprContext *econtext, JsonBehavior *behavior,
  */
 static Datum
 ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
-						 Datum res, bool *isNull)
+						 Datum res, bool *isNull, bool isJsonb)
 {
 	JsonExpr   *jexpr = op->d.jsonexpr.jsexpr;
 	JsonCoercion *coercion = jexpr->result_coercion;
-	Jsonb	   *jb = *isNull ? NULL : DatumGetJsonbP(res);
+	Jsonb	   *jb = *isNull || !isJsonb ? NULL : DatumGetJsonbP(res);
+	Json	   *js = *isNull || isJsonb ? NULL : DatumGetJsonP(res);
 
 	if ((coercion && coercion->via_io) ||
-		(jexpr->omit_quotes && !*isNull && JB_ROOT_IS_SCALAR(jb)))
+		(jexpr->omit_quotes && !*isNull &&
+		 (isJsonb ? JB_ROOT_IS_SCALAR(jb) : JsonContainerIsScalar(&js->root))))
 	{
 		/* strip quotes and call typinput function */
-		char *str = *isNull ? NULL : JsonbUnquote(jb);
+		char *str = *isNull ? NULL :
+			(isJsonb ? JsonbUnquote(jb) : JsonUnquote(js));
 
 		res = InputFunctionCall(&op->d.jsonexpr.input.func, str,
 								op->d.jsonexpr.input.typioparam,
@@ -4207,7 +4214,7 @@ ExecEvalJsonExprCoercion(ExprEvalStep *op, ExprContext *econtext,
 		res = ExecEvalExpr(op->d.jsonexpr.result_expr, econtext, isNull);
 	}
 	else if (coercion && coercion->via_populate)
-		res = json_populate_type(res, JSONBOID,
+		res = json_populate_type(res, isJsonb ? JSONBOID : JSONOID,
 								 jexpr->returning.typid,
 								 jexpr->returning.typmod,
 								 &op->d.jsonexpr.cache,
@@ -4241,7 +4248,7 @@ EvalJsonPathVar(void *cxt, bool *isnull)
  * corresponding SQL type and a pointer to the coercion state.
  */
 Datum
-ExecPrepareJsonItemCoercion(JsonbValue *item,
+ExecPrepareJsonItemCoercion(JsonbValue *item, bool is_jsonb,
 							JsonReturning *returning,
 							struct JsonCoercionsState *coercions,
 							struct JsonCoercionState **pcoercion)
@@ -4250,8 +4257,14 @@ ExecPrepareJsonItemCoercion(JsonbValue *item,
 	Datum		res;
 	JsonbValue	jbvbuf;
 
-	if (item->type == jbvBinary && JsonContainerIsScalar(item->val.binary.data))
-		item = JsonbExtractScalar(item->val.binary.data, &jbvbuf);
+	if (item->type == jbvBinary)
+	{
+		if (JsonContainerIsScalar(item->val.binary.data))
+			item = is_jsonb
+				? JsonbExtractScalar(item->val.binary.data, &jbvbuf)
+				: JsonExtractScalar((JsonContainer *) item->val.binary.data,
+									&jbvbuf);
+	}
 
 	/* get coercion state reference and datum of the corresponding SQL type */
 	switch (item->type)
@@ -4308,7 +4321,18 @@ ExecPrepareJsonItemCoercion(JsonbValue *item,
 		case jbvObject:
 		case jbvBinary:
 			coercion = &coercions->composite;
-			res = JsonbPGetDatum(JsonbValueToJsonb(item));
+			if (is_jsonb)
+			{
+				Jsonb	   *jb = JsonbValueToJsonb(item);
+
+				res = JsonbPGetDatum(jb);
+			}
+			else
+			{
+				Json	   *js = JsonbValueToJson(item);
+
+				res = JsonPGetDatum(js);
+			}
 			break;
 
 		default:
@@ -4323,7 +4347,8 @@ ExecPrepareJsonItemCoercion(JsonbValue *item,
 
 static Datum
 ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
-				 JsonExpr *jexpr, JsonPath *path, Datum item, bool *resnull)
+				 JsonExpr *jexpr, JsonPath *path, Datum item, bool isjsonb,
+				 bool *resnull)
 {
 	bool		empty = false;
 	Datum		res = (Datum) 0;
@@ -4339,7 +4364,8 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 		if (isnull)
 		{
 			/* execute domain checks for NULLs */
-			(void) ExecEvalJsonExprCoercion(op, econtext, res, resnull);
+			(void) ExecEvalJsonExprCoercion(op, econtext, res, resnull,
+											isjsonb);
 			*resnull = true;
 			return (Datum) 0;
 		}
@@ -4348,15 +4374,15 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 	switch (jexpr->op)
 	{
 		case IS_JSON_QUERY:
-			res = JsonbPathQuery(item, path, jexpr->wrapper, &empty,
-								 op->d.jsonexpr.args);
+			res = (isjsonb ? JsonbPathQuery : JsonPathQuery)
+				(item, path, jexpr->wrapper, &empty, op->d.jsonexpr.args);
 			*resnull = !DatumGetPointer(res);
 			break;
 
 		case IS_JSON_VALUE:
 			{
-				JsonbValue *jbv = JsonbPathValue(item, path, &empty,
-												 op->d.jsonexpr.args);
+				JsonbValue *jbv = (isjsonb ? JsonbPathValue : JsonPathValue)
+					(item, path, &empty, op->d.jsonexpr.args);
 				struct JsonCoercionState *jcstate;
 
 				if (!jbv)
@@ -4364,7 +4390,7 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 
 				*resnull = false;
 
-				res = ExecPrepareJsonItemCoercion(jbv,
+				res = ExecPrepareJsonItemCoercion(jbv, isjsonb,
 										&op->d.jsonexpr.jsexpr->returning,
 										&op->d.jsonexpr.coercions,
 										&jcstate);
@@ -4377,8 +4403,11 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 					jexpr->returning.typid == JSONBOID)
 				{
 					/* use coercion via I/O from json[b] to the output type */
-					res = JsonbPGetDatum(JsonbValueToJsonb(jbv));
-					res = ExecEvalJsonExprCoercion(op, econtext, res, resnull);
+					res = isjsonb
+						? JsonbPGetDatum(JsonbValueToJsonb(jbv))
+						: JsonPGetDatum(JsonbValueToJson(jbv));
+					res = ExecEvalJsonExprCoercion(op, econtext, res,
+												   resnull, isjsonb);
 				}
 				else if (jcstate->estate)
 				{
@@ -4392,7 +4421,8 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 			break;
 
 		case IS_JSON_EXISTS:
-			res = BoolGetDatum(JsonbPathExists(item, path, op->d.jsonexpr.args));
+			res = BoolGetDatum((isjsonb ? JsonbPathExists : JsonPathExists)
+				(item, path, op->d.jsonexpr.args));
 			*resnull = false;
 			break;
 
@@ -4411,14 +4441,15 @@ ExecEvalJsonExpr(ExprState *state, ExprEvalStep *op, ExprContext *econtext,
 
 		/* execute ON EMPTY behavior */
 		res = ExecEvalJsonBehavior(econtext, &jexpr->on_empty,
-								   op->d.jsonexpr.default_on_empty, resnull);
+								   op->d.jsonexpr.default_on_empty,
+								   isjsonb, resnull);
 	}
 
 	if (jexpr->op != IS_JSON_EXISTS &&
 		(!empty ? jexpr->op != IS_JSON_VALUE :
 		 /* result is already coerced in DEFAULT behavior case */
 		 jexpr->on_empty.btype != JSON_BEHAVIOR_DEFAULT))
-		res = ExecEvalJsonExprCoercion(op, econtext, res, resnull);
+		res = ExecEvalJsonExprCoercion(op, econtext, res, resnull, isjsonb);
 
 	return res;
 }
@@ -4441,6 +4472,10 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 	Datum		res = (Datum) 0;
 	JsonPath   *path;
 	ListCell   *lc;
+	Oid			formattedType = exprType(jexpr->formatted_expr ?
+										 jexpr->formatted_expr :
+										 jexpr->raw_expr);
+	bool		isjsonb = formattedType == JSONBOID;
 
 	*op->resnull = true;		/* until we get a result */
 	*op->resvalue = (Datum) 0;
@@ -4448,7 +4483,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 	if (op->d.jsonexpr.raw_expr->isnull || op->d.jsonexpr.pathspec->isnull)
 	{
 		/* execute domain checks for NULLs */
-		(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
+		(void) ExecEvalJsonExprCoercion(op, econtext, res, op->resnull, isjsonb);
 
 		Assert(*op->resnull);
 		*op->resnull = true;
@@ -4471,7 +4506,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 	if (!ExecEvalJsonNeedsSubTransaction(jexpr))
 	{
 		/* No need to use PG_TRY/PG_CATCH with subtransactions. */
-		res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
+		res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item, isjsonb,
 							   op->resnull);
 	}
 	else
@@ -4490,7 +4525,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 		PG_TRY();
 		{
 			res = ExecEvalJsonExpr(state, op, econtext, jexpr, path, item,
-								   op->resnull);
+								   isjsonb, op->resnull);
 
 			/* Commit the inner transaction, return to outer xact context */
 			ReleaseCurrentSubTransaction();
@@ -4517,12 +4552,13 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 			/* Execute ON ERROR behavior. */
 			res = ExecEvalJsonBehavior(econtext, &jexpr->on_error,
 									   op->d.jsonexpr.default_on_error,
-									   op->resnull);
+									   isjsonb, op->resnull);
 
 			if (jexpr->op != IS_JSON_EXISTS &&
 				/* result is already coerced in DEFAULT behavior case */
 				jexpr->on_error.btype != JSON_BEHAVIOR_DEFAULT)
-				res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull);
+				res = ExecEvalJsonExprCoercion(op, econtext, res, op->resnull,
+											   isjsonb);
 		}
 		PG_END_TRY();
 	}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 9c66636..ab24b35 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -4685,13 +4685,10 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 	JsonExpr   *jsexpr = transformJsonExprCommon(pstate, func);
 	Node	   *contextItemExpr =
 		jsexpr->formatted_expr ? jsexpr->formatted_expr : jsexpr->raw_expr;
-	const char *func_name = NULL;
 
 	switch (func->op)
 	{
 		case IS_JSON_VALUE:
-			func_name = "JSON_VALUE";
-
 			transformJsonFuncExprOutput(pstate, func, jsexpr);
 
 			jsexpr->returning.format.type = JS_FORMAT_DEFAULT;
@@ -4712,8 +4709,6 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			break;
 
 		case IS_JSON_QUERY:
-			func_name = "JSON_QUERY";
-
 			transformJsonFuncExprOutput(pstate, func, jsexpr);
 
 			jsexpr->wrapper = func->wrapper;
@@ -4722,8 +4717,6 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			break;
 
 		case IS_JSON_EXISTS:
-			func_name = "JSON_EXISTS";
-
 			jsexpr->returning.format.type = JS_FORMAT_DEFAULT;
 			jsexpr->returning.format.encoding = JS_ENC_DEFAULT;
 			jsexpr->returning.format.location = -1;
@@ -4733,11 +4726,5 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 			break;
 	}
 
-	if (exprType(contextItemExpr) != JSONBOID)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("%s() is not yet implemented for json type", func_name),
-				 parser_errposition(pstate, func->location)));
-
 	return (Node *) jsexpr;
 }
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 3fb1975..bd3fbee 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -788,7 +788,7 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
 					ExprContext *econtext);
 extern void ExecEvalJson(ExprState *state, ExprEvalStep *op,
 						 ExprContext *econtext);
-extern Datum ExecPrepareJsonItemCoercion(struct JsonbValue *item,
+extern Datum ExecPrepareJsonItemCoercion(struct JsonbValue *item, bool is_jsonb,
 							JsonReturning *returning,
 							struct JsonCoercionsState *coercions,
 							struct JsonCoercionState **pjcstate);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index c349e10..4071093 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -304,6 +304,12 @@ extern Datum JsonbPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper,
 extern JsonbValue *JsonbPathValue(Datum jb, JsonPath *jp, bool *empty,
 			   List *vars);
 
+extern bool JsonPathExists(Datum json, JsonPath *path, List *vars);
+extern JsonbValue *JsonPathValue(Datum json, JsonPath *jp, bool *empty,
+			  List *vars);
+extern Datum JsonPathQuery(Datum json, JsonPath *jp, JsonWrapper wrapper,
+			  bool *empty, List *vars);
+
 extern Datum EvalJsonPathVar(void *cxt, bool *isnull);
 
 #endif
diff --git a/src/test/regress/expected/json_sqljson.out b/src/test/regress/expected/json_sqljson.out
index bb62634..f7d1568 100644
--- a/src/test/regress/expected/json_sqljson.out
+++ b/src/test/regress/expected/json_sqljson.out
@@ -1,15 +1,1078 @@
 -- JSON_EXISTS
 SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
-ERROR:  JSON_EXISTS() is not yet implemented for json type
-LINE 1: SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
-               ^
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS(NULL::text FORMAT JSON, '$');
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS(NULL::bytea FORMAT JSON, '$');
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS(NULL::json FORMAT JSON, '$');
+WARNING:  FORMAT JSON has no effect for json and jsonb types
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS(NULL::jsonb FORMAT JSON, '$');
+WARNING:  FORMAT JSON has no effect for json and jsonb types
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS(NULL::json, '$');
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS('' FORMAT JSON, '$');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS('' FORMAT JSON, '$' TRUE ON ERROR);
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS('' FORMAT JSON, '$' FALSE ON ERROR);
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS('' FORMAT JSON, '$' UNKNOWN ON ERROR);
+ json_exists 
+-------------
+ 
+(1 row)
+
+SELECT JSON_EXISTS('' FORMAT JSON, '$' ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT JSON_EXISTS(bytea '' FORMAT JSON, '$' ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT JSON_EXISTS(json '[]', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS('[]' FORMAT JSON, '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(JSON_OBJECT(RETURNING bytea FORMAT JSON) FORMAT JSON, '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '1', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json 'null', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '[]', '$');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '1', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '1', 'strict $.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '1', 'strict $.a' ERROR ON ERROR);
+ERROR:  SQL/JSON member not found
+SELECT JSON_EXISTS(json 'null', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '[]', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '[1, "aaa", {"a": 1}]', 'strict $.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '[1, "aaa", {"a": 1}]', 'lax $.a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '{}', '$.a');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '{"b": 1, "a": 2}', '$.a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '1', '$.a.b');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '{"a": {"b": 1}}', '$.a.b');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.a.b');
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING 1 AS x);
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING '1' AS x);
+ json_exists 
+-------------
+ f
+(1 row)
+
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 2 AS y);
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 1 AS y);
+ json_exists 
+-------------
+ f
+(1 row)
+
+-- extension: boolean expressions
+SELECT JSON_EXISTS(json '1', '$ > 2');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_EXISTS(json '1', '$.a > 2' ERROR ON ERROR);
+ json_exists 
+-------------
+ t
+(1 row)
+
 -- JSON_VALUE
+SELECT JSON_VALUE(NULL, '$');
+ json_value 
+------------
+ 
+(1 row)
+
 SELECT JSON_VALUE(NULL FORMAT JSON, '$');
-ERROR:  JSON_VALUE() is not yet implemented for json type
-LINE 1: SELECT JSON_VALUE(NULL FORMAT JSON, '$');
-               ^
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(NULL::text, '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(NULL::bytea, '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(NULL::json, '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(NULL::jsonb FORMAT JSON, '$');
+WARNING:  FORMAT JSON has no effect for json and jsonb types
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE('' FORMAT JSON, '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE('' FORMAT JSON, '$' NULL ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE('' FORMAT JSON, '$' DEFAULT '"default value"' ON ERROR);
+   json_value    
+-----------------
+ "default value"
+(1 row)
+
+SELECT JSON_VALUE('' FORMAT JSON, '$' ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  The input string ended unexpectedly.
+CONTEXT:  JSON data, line 1: 
+SELECT JSON_VALUE(json 'null', '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json 'null', '$' RETURNING int);
+ json_value 
+------------
+           
+(1 row)
+
+SELECT JSON_VALUE(json 'true', '$');
+ json_value 
+------------
+ true
+(1 row)
+
+SELECT JSON_VALUE(json 'true', '$' RETURNING bool);
+ json_value 
+------------
+ t
+(1 row)
+
+SELECT JSON_VALUE(json '123', '$');
+ json_value 
+------------
+ 123
+(1 row)
+
+SELECT JSON_VALUE(json '123', '$' RETURNING int) + 234;
+ ?column? 
+----------
+      357
+(1 row)
+
+SELECT JSON_VALUE(json '123', '$' RETURNING text);
+ json_value 
+------------
+ 123
+(1 row)
+
+/* jsonb bytea ??? */
+SELECT JSON_VALUE(json '123', '$' RETURNING bytea);
+ json_value 
+------------
+ \x313233
+(1 row)
+
+SELECT JSON_VALUE(json '1.23', '$');
+ json_value 
+------------
+ 1.23
+(1 row)
+
+SELECT JSON_VALUE(json '1.23', '$' RETURNING int);
+ json_value 
+------------
+          1
+(1 row)
+
+SELECT JSON_VALUE(json '"1.23"', '$' RETURNING numeric);
+ json_value 
+------------
+       1.23
+(1 row)
+
+SELECT JSON_VALUE(json '"1.23"', '$' RETURNING int ERROR ON ERROR);
+ERROR:  invalid input syntax for integer: "1.23"
+SELECT JSON_VALUE(json '"aaa"', '$');
+ json_value 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING text);
+ json_value 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING char(5));
+ json_value 
+------------
+ aaa  
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING char(2));
+ json_value 
+------------
+ aa
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING json);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING jsonb);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING json ERROR ON ERROR);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING jsonb ERROR ON ERROR);
+ json_value 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_VALUE(json '"\"aaa\""', '$' RETURNING json);
+ json_value 
+------------
+ "\"aaa\""
+(1 row)
+
+SELECT JSON_VALUE(json '"\"aaa\""', '$' RETURNING jsonb);
+ json_value 
+------------
+ "\"aaa\""
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING int);
+ json_value 
+------------
+           
+(1 row)
+
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING int ERROR ON ERROR);
+ERROR:  invalid input syntax for integer: "aaa"
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING int DEFAULT 111 ON ERROR);
+ json_value 
+------------
+        111
+(1 row)
+
+SELECT JSON_VALUE(json '"123"', '$' RETURNING int) + 234;
+ ?column? 
+----------
+      357
+(1 row)
+
+SELECT JSON_VALUE(json '"2017-02-20"', '$' RETURNING date) + 9;
+  ?column?  
+------------
+ 03-01-2017
+(1 row)
+
+-- Test NULL checks execution in domain types
+CREATE DOMAIN sqljson_int_not_null AS int NOT NULL;
+SELECT JSON_VALUE(json '1', '$.a' RETURNING sqljson_int_not_null);
+ERROR:  domain sqljson_int_not_null does not allow null values
+SELECT JSON_VALUE(json '1', '$.a' RETURNING sqljson_int_not_null NULL ON ERROR);
+ERROR:  domain sqljson_int_not_null does not allow null values
+SELECT JSON_VALUE(json '1', '$.a' RETURNING sqljson_int_not_null DEFAULT NULL ON ERROR);
+ERROR:  domain sqljson_int_not_null does not allow null values
+SELECT JSON_VALUE(json '[]', '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '[]', '$' ERROR ON ERROR);
+ERROR:  SQL/JSON scalar required
+SELECT JSON_VALUE(json '{}', '$');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '{}', '$' ERROR ON ERROR);
+ERROR:  SQL/JSON scalar required
+SELECT JSON_VALUE(json '1', '$.a');
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'strict $.a' ERROR ON ERROR);
+ERROR:  SQL/JSON member not found
+SELECT JSON_VALUE(json '1', 'strict $.a' DEFAULT 'error' ON ERROR);
+ json_value 
+------------
+ error
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' ERROR ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' ERROR ON EMPTY ERROR ON ERROR);
+ERROR:  no SQL/JSON item
+SELECT JSON_VALUE(json '1', 'strict $.a' DEFAULT 2 ON ERROR);
+ json_value 
+------------
+ 2
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' DEFAULT 2 ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' DEFAULT '2' ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' NULL ON EMPTY DEFAULT '2' ON ERROR);
+ json_value 
+------------
+ 
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' DEFAULT '2' ON EMPTY DEFAULT '3' ON ERROR);
+ json_value 
+------------
+ 2
+(1 row)
+
+SELECT JSON_VALUE(json '1', 'lax $.a' ERROR ON EMPTY DEFAULT '3' ON ERROR);
+ json_value 
+------------
+ 3
+(1 row)
+
+SELECT JSON_VALUE(json '[1,2]', '$[*]' ERROR ON ERROR);
+ERROR:  more than one SQL/JSON item
+SELECT JSON_VALUE(json '[1,2]', '$[*]' DEFAULT '0' ON ERROR);
+ json_value 
+------------
+ 0
+(1 row)
+
+SELECT JSON_VALUE(json '[" "]', '$[*]' RETURNING int ERROR ON ERROR);
+ERROR:  invalid input syntax for integer: " "
+SELECT JSON_VALUE(json '[" "]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+ json_value 
+------------
+          5
+(1 row)
+
+SELECT JSON_VALUE(json '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+ json_value 
+------------
+          1
+(1 row)
+
+SELECT
+	x,
+	JSON_VALUE(
+		json '{"a": 1, "b": 2}',
+		'$.* ? (@ > $x)' PASSING x AS x
+		RETURNING int
+		DEFAULT -1 ON EMPTY
+		DEFAULT -2 ON ERROR
+	) y
+FROM
+	generate_series(0, 2) x;
+ x | y  
+---+----
+ 0 | -2
+ 1 |  2
+ 2 | -1
+(3 rows)
+
+SELECT JSON_VALUE(json 'null', '$a' PASSING point ' (1, 2 )' AS a);
+ json_value 
+------------
+ (1,2)
+(1 row)
+
+SELECT JSON_VALUE(json 'null', '$a' PASSING point ' (1, 2 )' AS a RETURNING point);
+ json_value 
+------------
+ (1,2)
+(1 row)
+
+-- Test timestamptz passing and output
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+          json_value          
+------------------------------
+ Tue Feb 20 18:34:56 2018 PST
+(1 row)
+
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamptz);
+          json_value          
+------------------------------
+ Tue Feb 20 18:34:56 2018 PST
+(1 row)
+
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamp);
+        json_value        
+--------------------------
+ Tue Feb 20 18:34:56 2018
+(1 row)
+
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+         json_value          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+         json_value          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
 -- JSON_QUERY
-SELECT JSON_QUERY(NULL FORMAT JSON, '$');
-ERROR:  JSON_QUERY() is not yet implemented for json type
-LINE 1: SELECT JSON_QUERY(NULL FORMAT JSON, '$');
-               ^
+SELECT
+	JSON_QUERY(js FORMAT JSON, '$'),
+	JSON_QUERY(js FORMAT JSON, '$' WITHOUT WRAPPER),
+	JSON_QUERY(js FORMAT JSON, '$' WITH CONDITIONAL WRAPPER),
+	JSON_QUERY(js FORMAT JSON, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
+	JSON_QUERY(js FORMAT JSON, '$' WITH ARRAY WRAPPER)
+FROM
+	(VALUES
+		('null'),
+		('12.3'),
+		('true'),
+		('"aaa"'),
+		('[1, null, "2"]'),
+		('{"a": 1, "b": [2]}')
+	) foo(js);
+     json_query     |     json_query     |     json_query     |      json_query      |      json_query      
+--------------------+--------------------+--------------------+----------------------+----------------------
+ null               | null               | [null]             | [null]               | [null]
+ 12.3               | 12.3               | [12.3]             | [12.3]               | [12.3]
+ true               | true               | [true]             | [true]               | [true]
+ "aaa"              | "aaa"              | ["aaa"]            | ["aaa"]              | ["aaa"]
+ [1, null, "2"]     | [1, null, "2"]     | [1, null, "2"]     | [[1, null, "2"]]     | [[1, null, "2"]]
+ {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
+(6 rows)
+
+SELECT
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]') AS "unspec",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITHOUT WRAPPER) AS "without",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITH CONDITIONAL WRAPPER) AS "with cond",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITH ARRAY WRAPPER) AS "with"
+FROM
+	(VALUES
+		('1'),
+		('[]'),
+		('[null]'),
+		('[12.3]'),
+		('[true]'),
+		('["aaa"]'),
+		('[[1, 2, 3]]'),
+		('[{"a": 1, "b": [2]}]'),
+		('[1, "2", null, [3]]')
+	) foo(js);
+       unspec       |      without       |      with cond      |     with uncond      |         with         
+--------------------+--------------------+---------------------+----------------------+----------------------
+                    |                    |                     |                      | 
+                    |                    |                     |                      | 
+ null               | null               | [null]              | [null]               | [null]
+ 12.3               | 12.3               | [12.3]              | [12.3]               | [12.3]
+ true               | true               | [true]              | [true]               | [true]
+ "aaa"              | "aaa"              | ["aaa"]             | ["aaa"]              | ["aaa"]
+ [1, 2, 3]          | [1, 2, 3]          | [1, 2, 3]           | [[1, 2, 3]]          | [[1, 2, 3]]
+ {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]}  | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
+                    |                    | [1, "2", null, [3]] | [1, "2", null, [3]]  | [1, "2", null, [3]]
+(9 rows)
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text);
+ json_query 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text KEEP QUOTES);
+ json_query 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text KEEP QUOTES ON SCALAR STRING);
+ json_query 
+------------
+ "aaa"
+(1 row)
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text OMIT QUOTES);
+ json_query 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text OMIT QUOTES ON SCALAR STRING);
+ json_query 
+------------
+ aaa
+(1 row)
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' OMIT QUOTES ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  Token "aaa" is invalid.
+CONTEXT:  JSON data, line 1: aaa
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING json OMIT QUOTES ERROR ON ERROR);
+ERROR:  invalid input syntax for type json
+DETAIL:  Token "aaa" is invalid.
+CONTEXT:  JSON data, line 1: aaa
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING bytea FORMAT JSON OMIT QUOTES ERROR ON ERROR);
+ json_query 
+------------
+ \x616161
+(1 row)
+
+-- QUOTES behavior should not be specified when WITH WRAPPER used:
+-- Should fail
+SELECT JSON_QUERY(json '[1]', '$' WITH WRAPPER OMIT QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: SELECT JSON_QUERY(json '[1]', '$' WITH WRAPPER OMIT QUOTES);
+                                                       ^
+SELECT JSON_QUERY(json '[1]', '$' WITH WRAPPER KEEP QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: SELECT JSON_QUERY(json '[1]', '$' WITH WRAPPER KEEP QUOTES);
+                                                       ^
+SELECT JSON_QUERY(json '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: ...ON_QUERY(json '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTE...
+                                                             ^
+SELECT JSON_QUERY(json '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTES);
+ERROR:  SQL/JSON QUOTES behavior shall not be specified when WITH WRAPPER is used
+LINE 1: ...ON_QUERY(json '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTE...
+                                                             ^
+-- Should succeed
+SELECT JSON_QUERY(json '[1]', '$' WITHOUT WRAPPER OMIT QUOTES);
+ json_query 
+------------
+ [1]
+(1 row)
+
+SELECT JSON_QUERY(json '[1]', '$' WITHOUT WRAPPER KEEP QUOTES);
+ json_query 
+------------
+ [1]
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]');
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' NULL ON EMPTY);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' EMPTY ARRAY ON EMPTY);
+ json_query 
+------------
+ []
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' EMPTY OBJECT ON EMPTY);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY NULL ON ERROR);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY EMPTY ARRAY ON ERROR);
+ json_query 
+------------
+ []
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY ERROR ON ERROR);
+ERROR:  no SQL/JSON item
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON ERROR);
+ json_query 
+------------
+ 
+(1 row)
+
+SELECT JSON_QUERY('[1,2]' FORMAT JSON, '$[*]' ERROR ON ERROR);
+ERROR:  more than one SQL/JSON item
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING json);
+ json_query 
+------------
+ [1,2]
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING json FORMAT JSON);
+ json_query 
+------------
+ [1,2]
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING jsonb);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING jsonb FORMAT JSON);
+ json_query 
+------------
+ [1, 2]
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING text);
+ json_query 
+------------
+ [1,2]
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING char(10));
+ json_query 
+------------
+ [1,2]     
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING char(3));
+ json_query 
+------------
+ [1,
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING text FORMAT JSON);
+ json_query 
+------------
+ [1,2]
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING bytea);
+  json_query  
+--------------
+ \x5b312c325d
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING bytea FORMAT JSON);
+  json_query  
+--------------
+ \x5b312c325d
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING bytea EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING bytea FORMAT JSON EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ \x7b7d
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING json EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING jsonb EMPTY OBJECT ON ERROR);
+ json_query 
+------------
+ {}
+(1 row)
+
+SELECT
+	x, y,
+	JSON_QUERY(
+		json '[1,2,3,4,5,null]',
+		'$[*] ? (@ >= $x && @ <= $y)'
+		PASSING x AS x, y AS y
+		WITH CONDITIONAL WRAPPER
+		EMPTY ARRAY ON EMPTY
+	) list
+FROM
+	generate_series(0, 4) x,
+	generate_series(0, 4) y;
+ x | y |     list     
+---+---+--------------
+ 0 | 0 | []
+ 0 | 1 | [1]
+ 0 | 2 | [1, 2]
+ 0 | 3 | [1, 2, 3]
+ 0 | 4 | [1, 2, 3, 4]
+ 1 | 0 | []
+ 1 | 1 | [1]
+ 1 | 2 | [1, 2]
+ 1 | 3 | [1, 2, 3]
+ 1 | 4 | [1, 2, 3, 4]
+ 2 | 0 | []
+ 2 | 1 | []
+ 2 | 2 | [2]
+ 2 | 3 | [2, 3]
+ 2 | 4 | [2, 3, 4]
+ 3 | 0 | []
+ 3 | 1 | []
+ 3 | 2 | []
+ 3 | 3 | [3]
+ 3 | 4 | [3, 4]
+ 4 | 0 | []
+ 4 | 1 | []
+ 4 | 2 | []
+ 4 | 3 | []
+ 4 | 4 | [4]
+(25 rows)
+
+-- Extension: record types returning
+CREATE TYPE sqljson_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
+CREATE TYPE sqljson_reca AS (reca sqljson_rec[]);
+SELECT JSON_QUERY(json '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}},  {"a": 2}]', '$[0]' RETURNING sqljson_rec);
+                     json_query                      
+-----------------------------------------------------
+ (1,aaa,"[1, ""2"", {}]","{""x"": [1, ""2"", {}]}",)
+(1 row)
+
+SELECT * FROM unnest((JSON_QUERY(json '{"jsa":  [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljson_rec)).jsa);
+         unnest         
+------------------------
+ {"a": 1, "b": ["foo"]}
+ {"a": 2, "c": {}}
+ 123
+(3 rows)
+
+SELECT * FROM unnest((JSON_QUERY(json '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljson_reca)).reca);
+ a |      t      | js |     jb     | jsa 
+---+-------------+----+------------+-----
+ 1 | ["foo", []] |    |            | 
+ 2 |             |    | [{}, true] | 
+(2 rows)
+
+-- Extension: array types returning
+SELECT JSON_QUERY(json '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
+  json_query  
+--------------
+ {1,2,NULL,3}
+(1 row)
+
+SELECT * FROM unnest(JSON_QUERY(json '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljson_rec[]));
+ a |      t      | js |     jb     | jsa 
+---+-------------+----+------------+-----
+ 1 | ["foo", []] |    |            | 
+ 2 |             |    | [{}, true] | 
+(2 rows)
+
+-- Extension: domain types returning
+SELECT JSON_QUERY(json '{"a": 1}', '$.a' RETURNING sqljson_int_not_null);
+ json_query 
+------------
+          1
+(1 row)
+
+SELECT JSON_QUERY(json '{"a": 1}', '$.b' RETURNING sqljson_int_not_null);
+ERROR:  domain sqljson_int_not_null does not allow null values
+-- Test timestamptz passing and output
+SELECT JSON_QUERY(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+         json_query          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+SELECT JSON_QUERY(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+         json_query          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+SELECT JSON_QUERY(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+         json_query          
+-----------------------------
+ "2018-02-21T02:34:56+00:00"
+(1 row)
+
+-- Test constraints
+CREATE TABLE test_json_constraints (
+	js text,
+	i int,
+	x jsonb DEFAULT JSON_QUERY(json '[1,2]', '$[*]' WITH WRAPPER)
+	CONSTRAINT test_json_constraint1
+		CHECK (js IS JSON)
+	CONSTRAINT test_json_constraint2
+		CHECK (JSON_EXISTS(js FORMAT JSON, '$.a' PASSING i + 5 AS int, i::text AS txt, array[1,2,3] as arr))
+	CONSTRAINT test_json_constraint3
+		CHECK (JSON_VALUE(js::json, '$.a' RETURNING int DEFAULT ('12' || i)::int ON EMPTY ERROR ON ERROR) > i)
+	CONSTRAINT test_json_constraint4
+		CHECK (JSON_QUERY(js FORMAT JSON, '$.a' RETURNING jsonb WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
+	CONSTRAINT test_json_constraint5
+		CHECK (JSON_QUERY(js FORMAT JSON, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) >  'a')
+);
+\d test_json_constraints
+                                                       Table "public.test_json_constraints"
+ Column |  Type   | Collation | Nullable |                                                 Default                                                 
+--------+---------+-----------+----------+---------------------------------------------------------------------------------------------------------
+ js     | text    |           |          | 
+ i      | integer |           |          | 
+ x      | jsonb   |           |          | JSON_QUERY('[1,2]'::json, '$[*]' RETURNING json WITH UNCONDITIONAL WRAPPER NULL ON EMPTY NULL ON ERROR)
+Check constraints:
+    "test_json_constraint1" CHECK (js IS JSON)
+    "test_json_constraint2" CHECK (JSON_EXISTS(js FORMAT JSON, '$."a"' PASSING i + 5 AS int, i::text AS txt, ARRAY[1, 2, 3] AS arr FALSE ON ERROR))
+    "test_json_constraint3" CHECK (JSON_VALUE(js::json, '$."a"' RETURNING integer DEFAULT ('12'::text || i)::integer ON EMPTY ERROR ON ERROR) > i)
+    "test_json_constraint4" CHECK (JSON_QUERY(js FORMAT JSON, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER NULL ON EMPTY EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
+    "test_json_constraint5" CHECK (JSON_QUERY(js FORMAT JSON, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY NULL ON ERROR) > 'a'::bpchar)
+
+SELECT check_clause
+FROM information_schema.check_constraints
+WHERE constraint_name LIKE 'test_json_constraint%';
+                                                             check_clause                                                             
+--------------------------------------------------------------------------------------------------------------------------------------
+ ((js IS JSON))
+ (JSON_EXISTS(js FORMAT JSON, '$."a"' PASSING (i + 5) AS int, (i)::text AS txt, ARRAY[1, 2, 3] AS arr FALSE ON ERROR))
+ ((JSON_VALUE((js)::json, '$."a"' RETURNING integer DEFAULT (('12'::text || i))::integer ON EMPTY ERROR ON ERROR) > i))
+ ((JSON_QUERY(js FORMAT JSON, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER NULL ON EMPTY EMPTY OBJECT ON ERROR) < '[10]'::jsonb))
+ ((JSON_QUERY(js FORMAT JSON, '$."a"' RETURNING character(5) OMIT QUOTES EMPTY ARRAY ON EMPTY NULL ON ERROR) > 'a'::bpchar))
+(5 rows)
+
+SELECT adsrc FROM pg_attrdef WHERE adrelid = 'test_json_constraints'::regclass;
+                                                  adsrc                                                  
+---------------------------------------------------------------------------------------------------------
+ JSON_QUERY('[1,2]'::json, '$[*]' RETURNING json WITH UNCONDITIONAL WRAPPER NULL ON EMPTY NULL ON ERROR)
+(1 row)
+
+INSERT INTO test_json_constraints VALUES ('', 1);
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint1"
+DETAIL:  Failing row contains (, 1, [1, 2]).
+INSERT INTO test_json_constraints VALUES ('1', 1);
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint2"
+DETAIL:  Failing row contains (1, 1, [1, 2]).
+INSERT INTO test_json_constraints VALUES ('[]');
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint2"
+DETAIL:  Failing row contains ([], null, [1, 2]).
+INSERT INTO test_json_constraints VALUES ('{"b": 1}', 1);
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint2"
+DETAIL:  Failing row contains ({"b": 1}, 1, [1, 2]).
+INSERT INTO test_json_constraints VALUES ('{"a": 1}', 1);
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint3"
+DETAIL:  Failing row contains ({"a": 1}, 1, [1, 2]).
+INSERT INTO test_json_constraints VALUES ('{"a": 7}', 1);
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint5"
+DETAIL:  Failing row contains ({"a": 7}, 1, [1, 2]).
+INSERT INTO test_json_constraints VALUES ('{"a": 10}', 1);
+ERROR:  new row for relation "test_json_constraints" violates check constraint "test_json_constraint4"
+DETAIL:  Failing row contains ({"a": 10}, 1, [1, 2]).
+DROP TABLE test_json_constraints;
+-- Extension: non-constant JSON path
+SELECT JSON_EXISTS(json '{"a": 123}', '$' || '.' || 'a');
+ json_exists 
+-------------
+ t
+(1 row)
+
+SELECT JSON_VALUE(json '{"a": 123}', '$' || '.' || 'a');
+ json_value 
+------------
+ 123
+(1 row)
+
+SELECT JSON_VALUE(json '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
+ json_value 
+------------
+ foo
+(1 row)
+
+SELECT JSON_QUERY(json '{"a": 123}', '$' || '.' || 'a');
+ json_query 
+------------
+ 123
+(1 row)
+
+SELECT JSON_QUERY(json '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
+ json_query 
+------------
+ [123]
+(1 row)
+
+-- Should fail (invalid path)
+SELECT JSON_QUERY(json '{"a": 123}', 'error' || ' ' || 'error');
+ERROR:  bad jsonpath representation
+DETAIL:  syntax error, unexpected IDENT_P at or near " "
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 498542e..f3f69ac 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
 # ----------
 # Another group of parallel tests (JSON related)
 # ----------
-test: json jsonb json_encoding jsonpath json_jsonpath jsonb_jsonpath jsonb_sqljson sqljson
+test: json jsonb json_encoding jsonpath json_jsonpath jsonb_jsonpath json_sqljson jsonb_sqljson sqljson
 
 # ----------
 # Another group of parallel tests
diff --git a/src/test/regress/sql/json_sqljson.sql b/src/test/regress/sql/json_sqljson.sql
index 4f30fa4..6146c45 100644
--- a/src/test/regress/sql/json_sqljson.sql
+++ b/src/test/regress/sql/json_sqljson.sql
@@ -1,11 +1,312 @@
 -- JSON_EXISTS
 
 SELECT JSON_EXISTS(NULL FORMAT JSON, '$');
+SELECT JSON_EXISTS(NULL::text FORMAT JSON, '$');
+SELECT JSON_EXISTS(NULL::bytea FORMAT JSON, '$');
+SELECT JSON_EXISTS(NULL::json FORMAT JSON, '$');
+SELECT JSON_EXISTS(NULL::jsonb FORMAT JSON, '$');
+SELECT JSON_EXISTS(NULL::json, '$');
+
+SELECT JSON_EXISTS('' FORMAT JSON, '$');
+SELECT JSON_EXISTS('' FORMAT JSON, '$' TRUE ON ERROR);
+SELECT JSON_EXISTS('' FORMAT JSON, '$' FALSE ON ERROR);
+SELECT JSON_EXISTS('' FORMAT JSON, '$' UNKNOWN ON ERROR);
+SELECT JSON_EXISTS('' FORMAT JSON, '$' ERROR ON ERROR);
+
+
+SELECT JSON_EXISTS(bytea '' FORMAT JSON, '$' ERROR ON ERROR);
+
+SELECT JSON_EXISTS(json '[]', '$');
+SELECT JSON_EXISTS('[]' FORMAT JSON, '$');
+SELECT JSON_EXISTS(JSON_OBJECT(RETURNING bytea FORMAT JSON) FORMAT JSON, '$');
+
+SELECT JSON_EXISTS(json '1', '$');
+SELECT JSON_EXISTS(json 'null', '$');
+SELECT JSON_EXISTS(json '[]', '$');
+
+SELECT JSON_EXISTS(json '1', '$.a');
+SELECT JSON_EXISTS(json '1', 'strict $.a');
+SELECT JSON_EXISTS(json '1', 'strict $.a' ERROR ON ERROR);
+SELECT JSON_EXISTS(json 'null', '$.a');
+SELECT JSON_EXISTS(json '[]', '$.a');
+SELECT JSON_EXISTS(json '[1, "aaa", {"a": 1}]', 'strict $.a');
+SELECT JSON_EXISTS(json '[1, "aaa", {"a": 1}]', 'lax $.a');
+SELECT JSON_EXISTS(json '{}', '$.a');
+SELECT JSON_EXISTS(json '{"b": 1, "a": 2}', '$.a');
+
+SELECT JSON_EXISTS(json '1', '$.a.b');
+SELECT JSON_EXISTS(json '{"a": {"b": 1}}', '$.a.b');
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.a.b');
+
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING 1 AS x);
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x)' PASSING '1' AS x);
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 2 AS y);
+SELECT JSON_EXISTS(json '{"a": 1, "b": 2}', '$.* ? (@ > $x && @ < $y)' PASSING 0 AS x, 1 AS y);
+
+-- extension: boolean expressions
+SELECT JSON_EXISTS(json '1', '$ > 2');
+SELECT JSON_EXISTS(json '1', '$.a > 2' ERROR ON ERROR);
 
 -- JSON_VALUE
 
+SELECT JSON_VALUE(NULL, '$');
 SELECT JSON_VALUE(NULL FORMAT JSON, '$');
+SELECT JSON_VALUE(NULL::text, '$');
+SELECT JSON_VALUE(NULL::bytea, '$');
+SELECT JSON_VALUE(NULL::json, '$');
+SELECT JSON_VALUE(NULL::jsonb FORMAT JSON, '$');
+
+SELECT JSON_VALUE('' FORMAT JSON, '$');
+SELECT JSON_VALUE('' FORMAT JSON, '$' NULL ON ERROR);
+SELECT JSON_VALUE('' FORMAT JSON, '$' DEFAULT '"default value"' ON ERROR);
+SELECT JSON_VALUE('' FORMAT JSON, '$' ERROR ON ERROR);
+
+SELECT JSON_VALUE(json 'null', '$');
+SELECT JSON_VALUE(json 'null', '$' RETURNING int);
+
+SELECT JSON_VALUE(json 'true', '$');
+SELECT JSON_VALUE(json 'true', '$' RETURNING bool);
+
+SELECT JSON_VALUE(json '123', '$');
+SELECT JSON_VALUE(json '123', '$' RETURNING int) + 234;
+SELECT JSON_VALUE(json '123', '$' RETURNING text);
+/* jsonb bytea ??? */
+SELECT JSON_VALUE(json '123', '$' RETURNING bytea);
+
+SELECT JSON_VALUE(json '1.23', '$');
+SELECT JSON_VALUE(json '1.23', '$' RETURNING int);
+SELECT JSON_VALUE(json '"1.23"', '$' RETURNING numeric);
+SELECT JSON_VALUE(json '"1.23"', '$' RETURNING int ERROR ON ERROR);
+
+SELECT JSON_VALUE(json '"aaa"', '$');
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING text);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING char(5));
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING char(2));
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING json);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING jsonb);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING json ERROR ON ERROR);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING jsonb ERROR ON ERROR);
+SELECT JSON_VALUE(json '"\"aaa\""', '$' RETURNING json);
+SELECT JSON_VALUE(json '"\"aaa\""', '$' RETURNING jsonb);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING int);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING int ERROR ON ERROR);
+SELECT JSON_VALUE(json '"aaa"', '$' RETURNING int DEFAULT 111 ON ERROR);
+SELECT JSON_VALUE(json '"123"', '$' RETURNING int) + 234;
+
+SELECT JSON_VALUE(json '"2017-02-20"', '$' RETURNING date) + 9;
+
+-- Test NULL checks execution in domain types
+CREATE DOMAIN sqljson_int_not_null AS int NOT NULL;
+SELECT JSON_VALUE(json '1', '$.a' RETURNING sqljson_int_not_null);
+SELECT JSON_VALUE(json '1', '$.a' RETURNING sqljson_int_not_null NULL ON ERROR);
+SELECT JSON_VALUE(json '1', '$.a' RETURNING sqljson_int_not_null DEFAULT NULL ON ERROR);
+
+SELECT JSON_VALUE(json '[]', '$');
+SELECT JSON_VALUE(json '[]', '$' ERROR ON ERROR);
+SELECT JSON_VALUE(json '{}', '$');
+SELECT JSON_VALUE(json '{}', '$' ERROR ON ERROR);
+
+SELECT JSON_VALUE(json '1', '$.a');
+SELECT JSON_VALUE(json '1', 'strict $.a' ERROR ON ERROR);
+SELECT JSON_VALUE(json '1', 'strict $.a' DEFAULT 'error' ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' ERROR ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' ERROR ON EMPTY ERROR ON ERROR);
+SELECT JSON_VALUE(json '1', 'strict $.a' DEFAULT 2 ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' DEFAULT 2 ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' DEFAULT '2' ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' NULL ON EMPTY DEFAULT '2' ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' DEFAULT '2' ON EMPTY DEFAULT '3' ON ERROR);
+SELECT JSON_VALUE(json '1', 'lax $.a' ERROR ON EMPTY DEFAULT '3' ON ERROR);
+
+SELECT JSON_VALUE(json '[1,2]', '$[*]' ERROR ON ERROR);
+SELECT JSON_VALUE(json '[1,2]', '$[*]' DEFAULT '0' ON ERROR);
+SELECT JSON_VALUE(json '[" "]', '$[*]' RETURNING int ERROR ON ERROR);
+SELECT JSON_VALUE(json '[" "]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+SELECT JSON_VALUE(json '["1"]', '$[*]' RETURNING int DEFAULT 2 + 3 ON ERROR);
+
+SELECT
+	x,
+	JSON_VALUE(
+		json '{"a": 1, "b": 2}',
+		'$.* ? (@ > $x)' PASSING x AS x
+		RETURNING int
+		DEFAULT -1 ON EMPTY
+		DEFAULT -2 ON ERROR
+	) y
+FROM
+	generate_series(0, 2) x;
+
+SELECT JSON_VALUE(json 'null', '$a' PASSING point ' (1, 2 )' AS a);
+SELECT JSON_VALUE(json 'null', '$a' PASSING point ' (1, 2 )' AS a RETURNING point);
+
+-- Test timestamptz passing and output
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamptz);
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING timestamp);
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+SELECT JSON_VALUE(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
 
 -- JSON_QUERY
 
-SELECT JSON_QUERY(NULL FORMAT JSON, '$');
+SELECT
+	JSON_QUERY(js FORMAT JSON, '$'),
+	JSON_QUERY(js FORMAT JSON, '$' WITHOUT WRAPPER),
+	JSON_QUERY(js FORMAT JSON, '$' WITH CONDITIONAL WRAPPER),
+	JSON_QUERY(js FORMAT JSON, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
+	JSON_QUERY(js FORMAT JSON, '$' WITH ARRAY WRAPPER)
+FROM
+	(VALUES
+		('null'),
+		('12.3'),
+		('true'),
+		('"aaa"'),
+		('[1, null, "2"]'),
+		('{"a": 1, "b": [2]}')
+	) foo(js);
+
+SELECT
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]') AS "unspec",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITHOUT WRAPPER) AS "without",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITH CONDITIONAL WRAPPER) AS "with cond",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+	JSON_QUERY(js FORMAT JSON, 'strict $[*]' WITH ARRAY WRAPPER) AS "with"
+FROM
+	(VALUES
+		('1'),
+		('[]'),
+		('[null]'),
+		('[12.3]'),
+		('[true]'),
+		('["aaa"]'),
+		('[[1, 2, 3]]'),
+		('[{"a": 1, "b": [2]}]'),
+		('[1, "2", null, [3]]')
+	) foo(js);
+
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text KEEP QUOTES);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text KEEP QUOTES ON SCALAR STRING);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text OMIT QUOTES);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING text OMIT QUOTES ON SCALAR STRING);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' OMIT QUOTES ERROR ON ERROR);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING json OMIT QUOTES ERROR ON ERROR);
+SELECT JSON_QUERY('"aaa"' FORMAT JSON, '$' RETURNING bytea FORMAT JSON OMIT QUOTES ERROR ON ERROR);
+
+-- QUOTES behavior should not be specified when WITH WRAPPER used:
+-- Should fail
+SELECT JSON_QUERY(json '[1]', '$' WITH WRAPPER OMIT QUOTES);
+SELECT JSON_QUERY(json '[1]', '$' WITH WRAPPER KEEP QUOTES);
+SELECT JSON_QUERY(json '[1]', '$' WITH CONDITIONAL WRAPPER KEEP QUOTES);
+SELECT JSON_QUERY(json '[1]', '$' WITH CONDITIONAL WRAPPER OMIT QUOTES);
+-- Should succeed
+SELECT JSON_QUERY(json '[1]', '$' WITHOUT WRAPPER OMIT QUOTES);
+SELECT JSON_QUERY(json '[1]', '$' WITHOUT WRAPPER KEEP QUOTES);
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]');
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' NULL ON EMPTY);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' EMPTY ARRAY ON EMPTY);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' EMPTY OBJECT ON EMPTY);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY);
+
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY NULL ON ERROR);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY EMPTY ARRAY ON ERROR);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON EMPTY ERROR ON ERROR);
+SELECT JSON_QUERY('[]' FORMAT JSON, '$[*]' ERROR ON ERROR);
+
+SELECT JSON_QUERY('[1,2]' FORMAT JSON, '$[*]' ERROR ON ERROR);
+
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING json);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING json FORMAT JSON);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING jsonb);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING jsonb FORMAT JSON);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING text);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING char(10));
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING char(3));
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING text FORMAT JSON);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING bytea);
+SELECT JSON_QUERY(json '[1,2]', '$' RETURNING bytea FORMAT JSON);
+
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING bytea EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING bytea FORMAT JSON EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING json EMPTY OBJECT ON ERROR);
+SELECT JSON_QUERY(json '[1,2]', '$[*]' RETURNING jsonb EMPTY OBJECT ON ERROR);
+
+SELECT
+	x, y,
+	JSON_QUERY(
+		json '[1,2,3,4,5,null]',
+		'$[*] ? (@ >= $x && @ <= $y)'
+		PASSING x AS x, y AS y
+		WITH CONDITIONAL WRAPPER
+		EMPTY ARRAY ON EMPTY
+	) list
+FROM
+	generate_series(0, 4) x,
+	generate_series(0, 4) y;
+
+-- Extension: record types returning
+CREATE TYPE sqljson_rec AS (a int, t text, js json, jb jsonb, jsa json[]);
+CREATE TYPE sqljson_reca AS (reca sqljson_rec[]);
+
+SELECT JSON_QUERY(json '[{"a": 1, "b": "foo", "t": "aaa", "js": [1, "2", {}], "jb": {"x": [1, "2", {}]}},  {"a": 2}]', '$[0]' RETURNING sqljson_rec);
+SELECT * FROM unnest((JSON_QUERY(json '{"jsa":  [{"a": 1, "b": ["foo"]}, {"a": 2, "c": {}}, 123]}', '$' RETURNING sqljson_rec)).jsa);
+SELECT * FROM unnest((JSON_QUERY(json '{"reca": [{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]}', '$' RETURNING sqljson_reca)).reca);
+
+-- Extension: array types returning
+SELECT JSON_QUERY(json '[1,2,null,"3"]', '$[*]' RETURNING int[] WITH WRAPPER);
+SELECT * FROM unnest(JSON_QUERY(json '[{"a": 1, "t": ["foo", []]}, {"a": 2, "jb": [{}, true]}]', '$' RETURNING sqljson_rec[]));
+
+-- Extension: domain types returning
+SELECT JSON_QUERY(json '{"a": 1}', '$.a' RETURNING sqljson_int_not_null);
+SELECT JSON_QUERY(json '{"a": 1}', '$.b' RETURNING sqljson_int_not_null);
+
+-- Test timestamptz passing and output
+SELECT JSON_QUERY(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts);
+SELECT JSON_QUERY(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING json);
+SELECT JSON_QUERY(json 'null', '$ts' PASSING timestamptz '2018-02-21 12:34:56 +10' AS ts RETURNING jsonb);
+
+-- Test constraints
+
+CREATE TABLE test_json_constraints (
+	js text,
+	i int,
+	x jsonb DEFAULT JSON_QUERY(json '[1,2]', '$[*]' WITH WRAPPER)
+	CONSTRAINT test_json_constraint1
+		CHECK (js IS JSON)
+	CONSTRAINT test_json_constraint2
+		CHECK (JSON_EXISTS(js FORMAT JSON, '$.a' PASSING i + 5 AS int, i::text AS txt, array[1,2,3] as arr))
+	CONSTRAINT test_json_constraint3
+		CHECK (JSON_VALUE(js::json, '$.a' RETURNING int DEFAULT ('12' || i)::int ON EMPTY ERROR ON ERROR) > i)
+	CONSTRAINT test_json_constraint4
+		CHECK (JSON_QUERY(js FORMAT JSON, '$.a' RETURNING jsonb WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
+	CONSTRAINT test_json_constraint5
+		CHECK (JSON_QUERY(js FORMAT JSON, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) >  'a')
+);
+
+\d test_json_constraints
+
+SELECT check_clause
+FROM information_schema.check_constraints
+WHERE constraint_name LIKE 'test_json_constraint%';
+
+SELECT adsrc FROM pg_attrdef WHERE adrelid = 'test_json_constraints'::regclass;
+
+INSERT INTO test_json_constraints VALUES ('', 1);
+INSERT INTO test_json_constraints VALUES ('1', 1);
+INSERT INTO test_json_constraints VALUES ('[]');
+INSERT INTO test_json_constraints VALUES ('{"b": 1}', 1);
+INSERT INTO test_json_constraints VALUES ('{"a": 1}', 1);
+INSERT INTO test_json_constraints VALUES ('{"a": 7}', 1);
+INSERT INTO test_json_constraints VALUES ('{"a": 10}', 1);
+
+DROP TABLE test_json_constraints;
+
+-- Extension: non-constant JSON path
+SELECT JSON_EXISTS(json '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_VALUE(json '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_VALUE(json '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY);
+SELECT JSON_QUERY(json '{"a": 123}', '$' || '.' || 'a');
+SELECT JSON_QUERY(json '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER);
+-- Should fail (invalid path)
+SELECT JSON_QUERY(json '{"a": 123}', 'error' || ' ' || 'error');

  [text/x-patch] 0014-optimize-sqljson-subtransactions-v15.patch (7.9K, ../e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru/6-0014-optimize-sqljson-subtransactions-v15.patch)
  download | inline diff:
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 8e6aef3..eb51783 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -188,6 +188,8 @@ typedef struct TransactionStateData
 	bool		startedInRecovery;	/* did we start in recovery? */
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
+	bool		isCachedSubXact;
+	struct TransactionStateData *cachedSubXact;
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -320,7 +322,7 @@ static void CommitSubTransaction(void);
 static void AbortSubTransaction(void);
 static void CleanupSubTransaction(void);
 static void PushTransaction(void);
-static void PopTransaction(void);
+static void PopTransaction(bool free);
 
 static void AtSubAbort_Memory(void);
 static void AtSubCleanup_Memory(void);
@@ -334,6 +336,8 @@ static void ShowTransactionStateRec(const char *str, TransactionState state);
 static const char *BlockStateAsString(TBlockState blockState);
 static const char *TransStateAsString(TransState state);
 
+static void ReleaseCurrentCachedSubTransaction(void);
+static void RollbackAndReleaseCurrentCachedSubTransaction(void);
 
 /* ----------------------------------------------------------------
  *	transaction state accessors
@@ -2745,6 +2749,8 @@ CommitTransactionCommand(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	ReleaseCurrentCachedSubTransaction();
+
 	switch (s->blockState)
 	{
 			/*
@@ -2985,6 +2991,8 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	RollbackAndReleaseCurrentCachedSubTransaction();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -4132,6 +4140,47 @@ RollbackToSavepoint(const char *name)
 			 BlockStateAsString(xact->blockState));
 }
 
+static void
+RestoreSubTransactionState(TransactionState subxact)
+{
+	CurrentTransactionState = subxact;
+
+	CurTransactionContext = subxact->curTransactionContext;
+	MemoryContextSwitchTo(CurTransactionContext);
+	CurTransactionResourceOwner = subxact->curTransactionOwner;
+	CurrentResourceOwner = subxact->curTransactionOwner;
+}
+
+static void
+ReleaseCurrentCachedSubTransaction(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	if (s->cachedSubXact)
+	{
+		RestoreSubTransactionState(s->cachedSubXact);
+		ReleaseCurrentCachedSubTransaction();
+		MemoryContextSwitchTo(CurTransactionContext);
+		CommitSubTransaction();
+		Assert(s == CurrentTransactionState);
+		s->cachedSubXact = NULL;
+	}
+}
+
+static void
+RollbackAndReleaseCurrentCachedSubTransaction(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	if (s->cachedSubXact)
+	{
+		RestoreSubTransactionState(s->cachedSubXact);
+		RollbackAndReleaseCurrentSubTransaction();
+		Assert(s == CurrentTransactionState);
+		s->cachedSubXact = NULL;
+	}
+}
+
 /*
  * BeginInternalSubTransaction
  *		This is the same as DefineSavepoint except it allows TBLOCK_STARTED,
@@ -4142,8 +4191,8 @@ RollbackToSavepoint(const char *name)
  *		CommitTransactionCommand/StartTransactionCommand instead of expecting
  *		the caller to do it.
  */
-void
-BeginInternalSubTransaction(const char *name)
+static void
+BeginInternalSubTransactionInternal(const char *name, bool cached)
 {
 	TransactionState s = CurrentTransactionState;
 
@@ -4170,10 +4219,31 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_END:
 		case TBLOCK_PREPARE:
 		case TBLOCK_SUBINPROGRESS:
+			if (s->cachedSubXact)
+			{
+				TransactionState subxact = s->cachedSubXact;
+
+				s->cachedSubXact = NULL;
+
+				Assert(subxact->subTransactionId == currentSubTransactionId);
+				if (subxact->subTransactionId == currentSubTransactionId)
+				{
+					/* reuse cached subtransaction */
+					RestoreSubTransactionState(subxact);
+					return;
+				}
+				else
+				{
+					ReleaseCurrentCachedSubTransaction();
+				}
+			}
+
 			/* Normal subtransaction start */
 			PushTransaction();
 			s = CurrentTransactionState;	/* changed by push */
 
+			s->isCachedSubXact = cached;
+
 			/*
 			 * Savepoint names, like the TransactionState block itself, live
 			 * in TopTransactionContext.
@@ -4206,6 +4276,18 @@ BeginInternalSubTransaction(const char *name)
 	StartTransactionCommand();
 }
 
+void
+BeginInternalSubTransaction(const char *name)
+{
+	BeginInternalSubTransactionInternal(name, false);
+}
+
+void
+BeginInternalCachedSubTransaction(const char *name)
+{
+	BeginInternalSubTransactionInternal(name, true);
+}
+
 /*
  * ReleaseCurrentSubTransaction
  *
@@ -4234,8 +4316,40 @@ ReleaseCurrentSubTransaction(void)
 		elog(ERROR, "ReleaseCurrentSubTransaction: unexpected state %s",
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
-	MemoryContextSwitchTo(CurTransactionContext);
-	CommitSubTransaction();
+
+	ReleaseCurrentCachedSubTransaction();
+
+	if (s->isCachedSubXact &&
+		!TransactionIdIsValid(s->transactionId) &&
+		!s->cachedSubXact /*&
+		(!s->curTransactionOwner ||
+		 (!s->curTransactionOwner->firstchild &&
+		   s->curTransactionOwner->nlocks <= 0))*/)
+	{
+		if (s->curTransactionOwner)
+		{
+			ResourceOwnerRelease(s->curTransactionOwner,
+								 RESOURCE_RELEASE_BEFORE_LOCKS,
+								 true, false);
+			ResourceOwnerRelease(s->curTransactionOwner,
+								 RESOURCE_RELEASE_LOCKS,
+								 true, false);
+			ResourceOwnerRelease(s->curTransactionOwner,
+								 RESOURCE_RELEASE_AFTER_LOCKS,
+								 true, false);
+		}
+
+		Assert(!s->parent->cachedSubXact);
+		s->parent->cachedSubXact = s;
+
+		PopTransaction(false);
+	}
+	else
+	{
+		MemoryContextSwitchTo(CurTransactionContext);
+		CommitSubTransaction();
+	}
+
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
 }
@@ -4291,6 +4405,8 @@ RollbackAndReleaseCurrentSubTransaction(void)
 			break;
 	}
 
+	RollbackAndReleaseCurrentCachedSubTransaction();
+
 	/*
 	 * Abort the current subtransaction, if needed.
 	 */
@@ -4654,7 +4770,7 @@ CommitSubTransaction(void)
 
 	s->state = TRANS_DEFAULT;
 
-	PopTransaction();
+	PopTransaction(true);
 }
 
 /*
@@ -4831,7 +4947,7 @@ CleanupSubTransaction(void)
 
 	s->state = TRANS_DEFAULT;
 
-	PopTransaction();
+	PopTransaction(true);
 }
 
 /*
@@ -4901,11 +5017,11 @@ PushTransaction(void)
  *	if it has a local pointer to it after calling this function.
  */
 static void
-PopTransaction(void)
+PopTransaction(bool free)
 {
 	TransactionState s = CurrentTransactionState;
 
-	if (s->state != TRANS_DEFAULT)
+	if (free && s->state != TRANS_DEFAULT)
 		elog(WARNING, "PopTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -4922,6 +5038,9 @@ PopTransaction(void)
 	CurTransactionResourceOwner = s->parent->curTransactionOwner;
 	CurrentResourceOwner = s->parent->curTransactionOwner;
 
+	if (!free)
+		return;
+
 	/* Free the old child structure */
 	if (s->name)
 		pfree(s->name);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1ca6d7b..c13143e 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -4531,7 +4531,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
 		MemoryContext oldcontext = CurrentMemoryContext;
 		ResourceOwner oldowner = CurrentResourceOwner;
 
-		BeginInternalSubTransaction(NULL);
+		BeginInternalCachedSubTransaction(NULL);
 		/* Want to execute expressions inside function's memory context */
 		MemoryContextSwitchTo(oldcontext);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 083e879..8f7be9a 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -380,6 +380,7 @@ extern void ReleaseSavepoint(const char *name);
 extern void DefineSavepoint(const char *name);
 extern void RollbackToSavepoint(const char *name);
 extern void BeginInternalSubTransaction(const char *name);
+extern void BeginInternalCachedSubTransaction(const char *name);
 extern void ReleaseCurrentSubTransaction(void);
 extern void RollbackAndReleaseCurrentSubTransaction(void);
 extern bool IsSubTransaction(void);

view thread (156+ messages)  latest in thread

Message-ID: <e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru>
Permalink:  ../e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru/
Also on:    postgresql.org/message-id/e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: n.gluhov@postgrespro.ru, obartunov@postgrespro.ru, a.korotkov@postgrespro.ru, andres@anarazel.de, michael@paquier.xyz, simon@2ndquadrant.com, andrew.dunstan@2ndquadrant.com, pavel.stehule@gmail.com
  Subject: Re: SQL/JSON: functions
  In-Reply-To: <e467c355-6fc7-e1b0-44d5-9707eca8f1dd@postgrespro.ru>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox