From f321afa928672e8f4c1af18971ccee3fc69b8d3c Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Thu, 16 Sep 2021 10:18:05 -0400
Subject: [PATCH 6/6] GUC sql_json

---
 doc/src/sgml/config.sgml                      |  19 +++
 src/backend/parser/gram.y                     |  11 +-
 src/backend/parser/parse_expr.c               |  48 ++++--
 src/backend/utils/adt/format_type.c           |   7 +-
 src/backend/utils/adt/jsonb.c                 |   2 +
 src/backend/utils/adt/ruleutils.c             |   6 +-
 src/backend/utils/misc/guc.c                  |  19 +++
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/nodes/parsenodes.h                |   2 +
 src/include/utils/jsonb.h                     |  14 ++
 src/test/regress/expected/jsonb.out           | 130 ++++++++++++++++
 src/test/regress/expected/sqljson.out         | 141 ++++++++++++++++++
 src/test/regress/sql/jsonb.sql                |  38 +++++
 src/test/regress/sql/sqljson.sql              |  32 ++++
 14 files changed, 454 insertions(+), 16 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ef0e2a7746..1b9a552ec8 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9500,6 +9500,25 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-sql-json" xreflabel="sql_json">
+      <term><varname>sql_json</varname> (<type>enum</type>)
+      <indexterm><primary>json</primary></indexterm>
+      <indexterm><primary>jsonb</primary></indexterm>
+      <indexterm>
+       <primary><varname>sql_json</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+         Valid values are <literal>json</literal> and <literal>jsonb</literal>.
+         Specifies what <productname>PostgreSQL</productname> type is used
+         as an implementation of SQL type <type>JSON</type>.
+         When <varname>sql_json</varname> is set to <literal>jsonb</literal>,
+         <productname>PostgreSQL</productname> type <type>json</type> can be
+         accessed using explicit qualification <type>pg_catalog.json</type>.
+       </para>
+      </listitem>
+     </varlistentry>
      </variablelist>
     </sect2>
    </sect1>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1084f8e918..b70949b122 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -62,6 +62,7 @@
 #include "storage/lmgr.h"
 #include "utils/date.h"
 #include "utils/datetime.h"
+#include "utils/jsonb.h"
 #include "utils/numeric.h"
 #include "utils/xml.h"
 
@@ -13321,10 +13322,11 @@ interval_second:
 				}
 		;
 
+/* Mapping of PG jsonb types to SQL/JSON JSON type */
 JsonType:
 			JSON
 				{
-					$$ = SystemTypeName("json");
+					$$ = SystemTypeName(SQLJSON_TYPE_NAME());
 					$$->location = @1;
 				}
 		;
@@ -15199,21 +15201,24 @@ json_func_expr:
 		;
 
 json_parse_expr:
-			JSON '(' json_value_expr json_key_uniqueness_constraint_opt ')'
+			JSON '(' json_value_expr json_key_uniqueness_constraint_opt
+					 json_returning_clause_opt ')'
 				{
 					JsonParseExpr *n = makeNode(JsonParseExpr);
 					n->expr = (JsonValueExpr *) $3;
 					n->unique_keys = $4;
+					n->output = (JsonOutput *) $5;
 					n->location = @1;
 					$$ = (Node *) n;
 				}
 		;
 
 json_scalar_expr:
-			JSON_SCALAR '(' a_expr ')'
+			JSON_SCALAR '(' a_expr json_returning_clause_opt ')'
 				{
 					JsonScalarExpr *n = makeNode(JsonScalarExpr);
 					n->expr = (Expr *) $3;
+					n->output = (JsonOutput *) $4;
 					n->location = @1;
 					$$ = (Node *) n;
 				}
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 6e70e1264e..678840593b 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -37,6 +37,7 @@
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/fmgroids.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/timestamp.h"
 #include "utils/xml.h"
@@ -4448,19 +4449,49 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func)
 	return (Node *) jsexpr;
 }
 
+static JsonReturning *
+transformJsonConstructorRet(ParseState *pstate, JsonOutput *output, const char *fname)
+{
+	JsonReturning *returning;
+
+	if (output)
+	{
+		returning = transformJsonOutput(pstate, output, false);
+
+		Assert(OidIsValid(returning->typid));
+
+		if (returning->typid != JSONOID && returning->typid != JSONBOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("cannot use RETURNING type %s in %s",
+							format_type_be(returning->typid), fname),
+					 parser_errposition(pstate, output->typeName->location)));
+	}
+	else
+	{
+		Oid			targettype = SQLJSON_TYPE_OID();
+		JsonFormatType format =
+			SQLJSON_TYPE_IS_JSONB() ? JS_FORMAT_JSONB : JS_FORMAT_JSON;
+
+		returning = makeNode(JsonReturning);
+		returning->format = makeJsonFormat(format, JS_ENC_DEFAULT, -1);
+		returning->typid = targettype;
+		returning->typmod = -1;
+	}
+
+	return returning;
+}
+
 /*
  * Transform a JSON() expression.
  */
 static Node *
 transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
 {
-	JsonReturning *returning = makeNode(JsonReturning);
+	JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
+													"JSON()");
 	Node	   *arg;
 
-	returning->format = makeJsonFormat(JS_FORMAT_JSON, JS_ENC_DEFAULT, -1);
-	returning->typid = JSONOID;
-	returning->typmod = -1;
-
 	if (jsexpr->unique_keys)
 	{
 		/*
@@ -4500,12 +4531,9 @@ transformJsonParseExpr(ParseState *pstate, JsonParseExpr *jsexpr)
 static Node *
 transformJsonScalarExpr(ParseState *pstate, JsonScalarExpr *jsexpr)
 {
-	JsonReturning *returning = makeNode(JsonReturning);
 	Node	   *arg = transformExprRecurse(pstate, (Node *) jsexpr->expr);
-
-	returning->format = makeJsonFormat(JS_FORMAT_JSON, JS_ENC_DEFAULT, -1);
-	returning->typid = JSONOID;
-	returning->typmod = -1;
+	JsonReturning *returning = transformJsonConstructorRet(pstate, jsexpr->output,
+													"JSON_SCALAR()");
 
 	if (exprType(arg) == UNKNOWNOID)
 		arg = coerce_to_specific_type(pstate, arg, TEXTOID, "JSON_SCALAR");
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index 43fa5a0cde..0f7646996a 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -23,6 +23,7 @@
 #include "mb/pg_wchar.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/numeric.h"
 #include "utils/syscache.h"
@@ -296,7 +297,11 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
 			break;
 
 		case JSONOID:
-			buf = pstrdup("json");
+			buf = pstrdup(SQLJSON_TYPE_IS_JSONB() ? "pg_catalog.json" : "json");
+			break;
+
+		case JSONBOID:
+			buf = pstrdup(SQLJSON_TYPE_IS_JSONB() ? "json" : "jsonb");
 			break;
 	}
 
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index bbf1def4da..d11683c962 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -69,6 +69,8 @@ static JsonbParseState *clone_parse_state(JsonbParseState *state);
 static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
 static void add_indent(StringInfo out, bool indent, int level);
 
+int			sql_json_type;		/* GUC for mapping jsonb to SQL/JSON JSON */
+
 /*
  * jsonb type input function
  */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index be214d875e..439f48feb4 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -63,6 +63,7 @@
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
 #include "utils/hsearch.h"
+#include "utils/jsonb.h"
 #include "utils/lsyscache.h"
 #include "utils/partcache.h"
 #include "utils/rel.h"
@@ -9943,8 +9944,9 @@ get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
 	if (ctor->unique)
 		appendStringInfoString(buf, " WITH UNIQUE KEYS");
 
-	if (ctor->type != JSCTOR_JSON_PARSE &&
-		ctor->type != JSCTOR_JSON_SCALAR)
+	if (!((ctor->type == JSCTOR_JSON_PARSE ||
+		   ctor->type == JSCTOR_JSON_SCALAR) &&
+		  ctor->returning->typid == SQLJSON_TYPE_OID()))
 		get_json_returning(ctor->returning, buf, true);
 }
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a6e4fcc24e..f1a0327148 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -95,6 +95,7 @@
 #include "utils/bytea.h"
 #include "utils/float.h"
 #include "utils/guc_tables.h"
+#include "utils/jsonb.h"
 #include "utils/memutils.h"
 #include "utils/pg_locale.h"
 #include "utils/pg_lsn.h"
@@ -556,6 +557,12 @@ static const struct config_enum_entry wal_compression_options[] = {
 	{NULL, 0, false}
 };
 
+const struct config_enum_entry sql_json_type_info[] = {
+	{"json", SQLJSON_TYPE_JSON, false},
+	{"jsonb", SQLJSON_TYPE_JSONB, false},
+	{NULL, 0, false}
+};
+
 /*
  * Options for enum values stored in other modules
  */
@@ -4982,6 +4989,18 @@ static struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"sql_json", PGC_USERSET, COMPAT_OPTIONS_CLIENT,
+			gettext_noop("Sets what PostgreSQL type to use as an implementaion of SQL JSON type."),
+			gettext_noop("When turned on, jsonb type is mapped to SQL JSON type, "
+						 "json type is mapped to JSON TEXT type.")
+		},
+		&sql_json_type,
+		SQLJSON_TYPE_JSON,
+		sql_json_type_info,
+		NULL, NULL, NULL
+	},
+
 	/* End-of-list marker */
 	{
 		{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 3fe9a53cb3..3292846f89 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -762,6 +762,7 @@
 # - Other Platforms and Clients -
 
 #transform_null_equals = off
+#sql_json = json # jsonb
 
 
 #------------------------------------------------------------------------------
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5d6504eeb9..f8390ee9c0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1642,6 +1642,7 @@ typedef struct JsonParseExpr
 {
 	NodeTag		type;
 	JsonValueExpr *expr;		/* string expression */
+	JsonOutput *output;			/* RETURNING clause, if specified */
 	bool		unique_keys;	/* WITH UNIQUE KEYS? */
 	int			location;		/* token location, or -1 if unknown */
 } JsonParseExpr;
@@ -1654,6 +1655,7 @@ typedef struct JsonScalarExpr
 {
 	NodeTag		type;
 	Expr	   *expr;			/* scalar expression */
+	JsonOutput *output;			/* RETURNING clause, if specified */
 	int			location;		/* token location, or -1 if unknown */
 } JsonScalarExpr;
 
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 3bac254aab..be5cc0e397 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -393,6 +393,20 @@ typedef enum					/* type categories for datum_to_jsonb */
 	JSONBTYPE_OTHER				/* all else */
 } JsonbTypeCategory;
 
+/* values for the sql+json_type GUC. */
+typedef enum SqlJsonType
+{
+	SQLJSON_TYPE_JSON = 0,
+	SQLJSON_TYPE_JSONB = 1
+} SqlJsonType;
+
+#define SQLJSON_TYPE_IS_JSONB() (sql_json_type == SQLJSON_TYPE_JSONB)
+#define SQLJSON_TYPE_OID() (SQLJSON_TYPE_IS_JSONB() ? JSONBOID : JSONOID)
+#define SQLJSON_TYPE_NAME() (SQLJSON_TYPE_IS_JSONB() ? "jsonb" : "json")
+
+/* GUC */
+extern int sql_json_type;
+
 /* Support functions */
 extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
 extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index a9cd145aec..905b50e850 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -5509,3 +5509,133 @@ select '12345.0000000000000000000000000000000000000000000005'::jsonb::int8;
  12345
 (1 row)
 
+-- test mapping of jsonb to SQL/JSON JSON type
+select json(' { "aa": 1, "b" : 2 }');
+         json          
+-----------------------
+  { "aa": 1, "b" : 2 }
+(1 row)
+
+select json ' { "aa": 1, "b" : 2 }';
+         json          
+-----------------------
+  { "aa": 1, "b" : 2 }
+(1 row)
+
+select pg_catalog.json ' { "aa": 1, "b" : 2 }';
+         json          
+-----------------------
+  { "aa": 1, "b" : 2 }
+(1 row)
+
+create table test_json_as_json (js json, jb jsonb);
+\d test_json_as_json
+        Table "public.test_json_as_json"
+ Column | Type  | Collation | Nullable | Default 
+--------+-------+-----------+----------+---------
+ js     | json  |           |          | 
+ jb     | jsonb |           |          | 
+
+set sql_json = jsonb;
+select json(' { "aa": 1, "b" : 2 }');
+       json        
+-------------------
+ {"b": 2, "aa": 1}
+(1 row)
+
+select json ' { "aa": 1, "b" : 2 }';
+       jsonb       
+-------------------
+ {"b": 2, "aa": 1}
+(1 row)
+
+select pg_catalog.json ' { "aa": 1, "b" : 2 }';
+         json          
+-----------------------
+  { "aa": 1, "b" : 2 }
+(1 row)
+
+\d test_json_as_json
+             Table "public.test_json_as_json"
+ Column |      Type       | Collation | Nullable | Default 
+--------+-----------------+-----------+----------+---------
+ js     | pg_catalog.json |           |          | 
+ jb     | json            |           |          | 
+
+create table test_json_as_jsonb (js json, jb jsonb, jt pg_catalog.json);
+\d test_json_as_jsonb
+             Table "public.test_json_as_jsonb"
+ Column |      Type       | Collation | Nullable | Default 
+--------+-----------------+-----------+----------+---------
+ js     | json            |           |          | 
+ jb     | json            |           |          | 
+ jt     | pg_catalog.json |           |          | 
+
+insert into test_json_as_jsonb values ('{ "a" : 1 }', '{ "a" : 1 }', '{ "a" : 1 }');
+select * from test_json_as_jsonb;
+    js    |    jb    |     jt      
+----------+----------+-------------
+ {"a": 1} | {"a": 1} | { "a" : 1 }
+(1 row)
+
+select jsonb_object_field(js, 'a') from test_json_as_jsonb;
+ jsonb_object_field 
+--------------------
+ 1
+(1 row)
+
+select jsonb_object_field(jb, 'a') from test_json_as_jsonb;
+ jsonb_object_field 
+--------------------
+ 1
+(1 row)
+
+select jsonb_object_field(jt, 'a') from test_json_as_jsonb;
+ERROR:  function jsonb_object_field(pg_catalog.json, unknown) does not exist
+LINE 1: select jsonb_object_field(jt, 'a') from test_json_as_jsonb;
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select json_object_field(jt, 'a') from test_json_as_jsonb;
+ json_object_field 
+-------------------
+ 1
+(1 row)
+
+set sql_json = json;
+\d test_json_as_jsonb
+        Table "public.test_json_as_jsonb"
+ Column | Type  | Collation | Nullable | Default 
+--------+-------+-----------+----------+---------
+ js     | jsonb |           |          | 
+ jb     | jsonb |           |          | 
+ jt     | json  |           |          | 
+
+select * from test_json_as_jsonb;
+    js    |    jb    |     jt      
+----------+----------+-------------
+ {"a": 1} | {"a": 1} | { "a" : 1 }
+(1 row)
+
+select jsonb_object_field(js, 'a') from test_json_as_jsonb;
+ jsonb_object_field 
+--------------------
+ 1
+(1 row)
+
+select jsonb_object_field(jb, 'a') from test_json_as_jsonb;
+ jsonb_object_field 
+--------------------
+ 1
+(1 row)
+
+select jsonb_object_field(jt, 'a') from test_json_as_jsonb;
+ERROR:  function jsonb_object_field(json, unknown) does not exist
+LINE 1: select jsonb_object_field(jt, 'a') from test_json_as_jsonb;
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select json_object_field(jt, 'a') from test_json_as_jsonb;
+ json_object_field 
+-------------------
+ 1
+(1 row)
+
diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out
index 11f5eb2d2c..51bd216120 100644
--- a/src/test/regress/expected/sqljson.out
+++ b/src/test/regress/expected/sqljson.out
@@ -113,6 +113,103 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITHOUT UNIQUE KEYS);
    Output: JSON('123'::json)
 (2 rows)
 
+SELECT JSON('123' RETURNING text);
+ERROR:  cannot use RETURNING type text in JSON()
+LINE 1: SELECT JSON('123' RETURNING text);
+                                    ^
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+         QUERY PLAN          
+-----------------------------
+ Result
+   Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json);
+         QUERY PLAN          
+-----------------------------
+ Result
+   Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb);
+                  QUERY PLAN                  
+----------------------------------------------
+ Result
+   Output: JSON('123'::jsonb RETURNING jsonb)
+(2 rows)
+
+SELECT pg_typeof(JSON('123'));
+ pg_typeof 
+-----------
+ json
+(1 row)
+
+SELECT pg_typeof(JSON('123' RETURNING json));
+ pg_typeof 
+-----------
+ json
+(1 row)
+
+SELECT pg_typeof(JSON('123' RETURNING jsonb));
+ pg_typeof 
+-----------
+ jsonb
+(1 row)
+
+SET sql_json = jsonb;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+         QUERY PLAN          
+-----------------------------
+ Result
+   Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json);
+         QUERY PLAN          
+-----------------------------
+ Result
+   Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb);
+         QUERY PLAN          
+-----------------------------
+ Result
+   Output: JSON('123'::json)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING pg_catalog.json);
+                            QUERY PLAN                            
+------------------------------------------------------------------
+ Result
+   Output: JSON('123'::pg_catalog.json RETURNING pg_catalog.json)
+(2 rows)
+
+SELECT pg_typeof(JSON('123'));
+ pg_typeof 
+-----------
+ json
+(1 row)
+
+SELECT pg_typeof(JSON('123' RETURNING json));
+ pg_typeof 
+-----------
+ json
+(1 row)
+
+SELECT pg_typeof(JSON('123' RETURNING jsonb));
+ pg_typeof 
+-----------
+ json
+(1 row)
+
+SELECT pg_typeof(JSON('123' RETURNING pg_catalog.json));
+    pg_typeof    
+-----------------
+ pg_catalog.json
+(1 row)
+
+SET sql_json = json;
 -- JSON_SCALAR()
 SELECT JSON_SCALAR();
 ERROR:  syntax error at or near ")"
@@ -204,6 +301,50 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123');
    Output: JSON_SCALAR('123'::text)
 (2 rows)
 
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json);
+         QUERY PLAN         
+----------------------------
+ Result
+   Output: JSON_SCALAR(123)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb);
+                 QUERY PLAN                 
+--------------------------------------------
+ Result
+   Output: JSON_SCALAR(123 RETURNING jsonb)
+(2 rows)
+
+SET sql_json = jsonb;
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123);
+         QUERY PLAN         
+----------------------------
+ Result
+   Output: JSON_SCALAR(123)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json);
+         QUERY PLAN         
+----------------------------
+ Result
+   Output: JSON_SCALAR(123)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb);
+         QUERY PLAN         
+----------------------------
+ Result
+   Output: JSON_SCALAR(123)
+(2 rows)
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING pg_catalog.json);
+                      QUERY PLAN                      
+------------------------------------------------------
+ Result
+   Output: JSON_SCALAR(123 RETURNING pg_catalog.json)
+(2 rows)
+
+SET sql_json = json;
 -- JSON_SERIALIZE()
 SELECT JSON_SERIALIZE();
 ERROR:  syntax error at or near ")"
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 5016f29c15..4af2a0cb11 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -1482,3 +1482,41 @@ select '12345.0000000000000000000000000000000000000000000005'::jsonb::float8;
 select '12345.0000000000000000000000000000000000000000000005'::jsonb::int2;
 select '12345.0000000000000000000000000000000000000000000005'::jsonb::int4;
 select '12345.0000000000000000000000000000000000000000000005'::jsonb::int8;
+
+-- test mapping of jsonb to SQL/JSON JSON type
+select json(' { "aa": 1, "b" : 2 }');
+select json ' { "aa": 1, "b" : 2 }';
+select pg_catalog.json ' { "aa": 1, "b" : 2 }';
+
+create table test_json_as_json (js json, jb jsonb);
+\d test_json_as_json
+
+set sql_json = jsonb;
+
+select json(' { "aa": 1, "b" : 2 }');
+select json ' { "aa": 1, "b" : 2 }';
+select pg_catalog.json ' { "aa": 1, "b" : 2 }';
+
+\d test_json_as_json
+
+create table test_json_as_jsonb (js json, jb jsonb, jt pg_catalog.json);
+\d test_json_as_jsonb
+
+insert into test_json_as_jsonb values ('{ "a" : 1 }', '{ "a" : 1 }', '{ "a" : 1 }');
+
+select * from test_json_as_jsonb;
+
+select jsonb_object_field(js, 'a') from test_json_as_jsonb;
+select jsonb_object_field(jb, 'a') from test_json_as_jsonb;
+select jsonb_object_field(jt, 'a') from test_json_as_jsonb;
+select json_object_field(jt, 'a') from test_json_as_jsonb;
+
+set sql_json = json;
+\d test_json_as_jsonb
+
+select * from test_json_as_jsonb;
+
+select jsonb_object_field(js, 'a') from test_json_as_jsonb;
+select jsonb_object_field(jb, 'a') from test_json_as_jsonb;
+select jsonb_object_field(jt, 'a') from test_json_as_jsonb;
+select json_object_field(jt, 'a') from test_json_as_jsonb;
diff --git a/src/test/regress/sql/sqljson.sql b/src/test/regress/sql/sqljson.sql
index 98bd93c110..4ff6076763 100644
--- a/src/test/regress/sql/sqljson.sql
+++ b/src/test/regress/sql/sqljson.sql
@@ -23,6 +23,27 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123'::bytea FORMAT JSON ENCODING UTF8)
 EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITH UNIQUE KEYS);
 EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' WITHOUT UNIQUE KEYS);
 
+SELECT JSON('123' RETURNING text);
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb);
+SELECT pg_typeof(JSON('123'));
+SELECT pg_typeof(JSON('123' RETURNING json));
+SELECT pg_typeof(JSON('123' RETURNING jsonb));
+
+SET sql_json = jsonb;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123');
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING json);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING jsonb);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON('123' RETURNING pg_catalog.json);
+SELECT pg_typeof(JSON('123'));
+SELECT pg_typeof(JSON('123' RETURNING json));
+SELECT pg_typeof(JSON('123' RETURNING jsonb));
+SELECT pg_typeof(JSON('123' RETURNING pg_catalog.json));
+
+SET sql_json = json;
 
 -- JSON_SCALAR()
 SELECT JSON_SCALAR();
@@ -41,6 +62,17 @@ SELECT JSON_SCALAR('{}'::jsonb);
 
 EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123);
 EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR('123');
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb);
+
+SET sql_json = jsonb;
+
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING json);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING jsonb);
+EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_SCALAR(123 RETURNING pg_catalog.json);
+
+SET sql_json = json;
 
 -- JSON_SERIALIZE()
 SELECT JSON_SERIALIZE();
-- 
2.25.4

