From f9c6d72cbf49257fea4265d994b96e66f25b2474 Mon Sep 17 00:00:00 2001 From: Andrew Dunstan <andrew@dunslane.net> Date: Wed, 26 Mar 2014 10:18:24 -0400 Subject: [PATCH] Cleanup around json_to_record/json_to_recordset Set function parameter names and defaults. Add jsonb versions (which the code already provided for so the actual new code is trivial). Add jsonb regression tests and docs. Bump catalog version (which I apparently forgot to do when jsonb was committed). --- doc/src/sgml/func.sgml | 18 +++++++++----- src/backend/catalog/system_views.sql | 16 ++++++++++++ src/backend/utils/adt/jsonfuncs.c | 36 ++++++++++++++++++--------- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 4 +++ src/include/utils/json.h | 2 ++ src/test/regress/expected/jsonb.out | 16 ++++++++++++ src/test/regress/expected/jsonb_1.out | 16 ++++++++++++ src/test/regress/sql/jsonb.sql | 8 ++++++ 9 files changed, 99 insertions(+), 19 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4e2fff7cd74..6e2fbda6c23 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -10443,9 +10443,15 @@ table2-mapping <indexterm> <primary>json_to_record</primary> </indexterm> + <indexterm> + <primary>jsonb_to_record</primary> + </indexterm> <indexterm> <primary>json_to_recordset</primary> </indexterm> + <indexterm> + <primary>jsonb_to_recordset</primary> + </indexterm> <table id="functions-json-processing-table"> <title>JSON Processing Functions</title> @@ -10649,9 +10655,9 @@ table2-mapping <entry><literal>number</literal></entry> </row> <row> - <entry> - <literal>json_to_record(json, nested_as_text bool)</literal> - </entry> + <entry><para><literal>json_to_record(json [, nested_as_text bool=false])</literal> + </para><para><literal>jsonb_to_record(jsonb [, nested_as_text bool=false])</literal> + </para></entry> <entry><type>record</type></entry> <entry> Returns an arbitrary record from a JSON object. As with all functions @@ -10670,9 +10676,9 @@ table2-mapping </entry> </row> <row> - <entry> - <literal>json_to_recordset(json, nested_as_text bool)</literal> - </entry> + <entry><para><literal>json_to_recordset(json [, nested_as_text bool=false])</literal> + </para><para><literal>jsonb_to_recordset(jsonb [, nested_as_text bool=false])</literal> + </para></entry> <entry><type>setof record</type></entry> <entry> Returns an arbitrary set of records from a JSON object. As with diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 662040261e9..42a4c00a1ed 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -833,6 +833,22 @@ CREATE OR REPLACE FUNCTION jsonb_populate_recordset(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false) RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100 AS 'jsonb_populate_recordset'; +CREATE OR REPLACE FUNCTION + json_to_record(from_json json, nested_as_text boolean DEFAULT false) + RETURNS record LANGUAGE internal STABLE AS 'json_to_record'; + +CREATE OR REPLACE FUNCTION + json_to_recordset(from_json json, nested_as_text boolean DEFAULT false) + RETURNS SETOF record LANGUAGE internal STABLE ROWS 100 AS 'json_to_recordset'; + +CREATE OR REPLACE FUNCTION + jsonb_to_record(from_json jsonb, nested_as_text boolean DEFAULT false) + RETURNS record LANGUAGE internal STABLE AS 'jsonb_to_record'; + +CREATE OR REPLACE FUNCTION + jsonb_to_recordset(from_json jsonb, nested_as_text boolean DEFAULT false) + RETURNS SETOF record LANGUAGE internal STABLE ROWS 100 AS 'jsonb_to_recordset'; + CREATE OR REPLACE FUNCTION pg_logical_slot_get_changes( IN slotname name, IN upto_lsn pg_lsn, IN upto_nchanges int, VARIADIC options text[] DEFAULT '{}', OUT location pg_lsn, OUT xid xid, OUT data text) diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 94595998029..bf164677142 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -2014,6 +2014,12 @@ jsonb_populate_record(PG_FUNCTION_ARGS) return populate_record_worker(fcinfo, true); } +Datum +jsonb_to_record(PG_FUNCTION_ARGS) +{ + return populate_record_worker(fcinfo, false); +} + Datum json_populate_record(PG_FUNCTION_ARGS) { @@ -2449,6 +2455,24 @@ jsonb_populate_recordset(PG_FUNCTION_ARGS) return populate_recordset_worker(fcinfo, true); } +Datum +jsonb_to_recordset(PG_FUNCTION_ARGS) +{ + return populate_recordset_worker(fcinfo, false); +} + +Datum +json_populate_recordset(PG_FUNCTION_ARGS) +{ + return populate_recordset_worker(fcinfo, true); +} + +Datum +json_to_recordset(PG_FUNCTION_ARGS) +{ + return populate_recordset_worker(fcinfo, false); +} + static void make_row_from_rec_and_jsonb(Jsonb * element, PopulateRecordsetState *state) { @@ -2571,18 +2595,6 @@ make_row_from_rec_and_jsonb(Jsonb * element, PopulateRecordsetState *state) tuplestore_puttuple(state->tuple_store, rettuple); } -Datum -json_populate_recordset(PG_FUNCTION_ARGS) -{ - return populate_recordset_worker(fcinfo, true); -} - -Datum -json_to_recordset(PG_FUNCTION_ARGS) -{ - return populate_recordset_worker(fcinfo, false); -} - /* * common worker for json_populate_recordset() and json_to_recordset() */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 60f78d24a96..3c74fa0a515 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201403121 +#define CATALOG_VERSION_NO 201403261 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 56f0f11ebe2..334e6b8d155 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4546,6 +4546,10 @@ DATA(insert OID = 3209 ( jsonb_populate_record PGNSP PGUID 12 1 0 0 0 f f f DESCR("get record fields from a jsonb object"); DATA(insert OID = 3475 ( jsonb_populate_recordset PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 3802 16" _null_ _null_ _null_ _null_ jsonb_populate_recordset _null_ _null_ _null_ )); DESCR("get set of records with fields from a jsonb array of objects"); +DATA(insert OID = 3490 ( jsonb_to_record PGNSP PGUID 12 1 0 0 0 f f f f f f s 2 0 2249 "3802 16" _null_ _null_ _null_ _null_ jsonb_to_record _null_ _null_ _null_ )); +DESCR("get record fields from a json object"); +DATA(insert OID = 3491 ( jsonb_to_recordset PGNSP PGUID 12 1 100 0 0 f f f f f t s 2 0 2249 "3802 16" _null_ _null_ _null_ _null_ jsonb_to_recordset _null_ _null_ _null_ )); +DESCR("get set of records with fields from a json array of objects"); DATA(insert OID = 3210 ( jsonb_typeof PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ jsonb_typeof _null_ _null_ _null_ )); DESCR("get the type of a jsonb value"); DATA(insert OID = 4038 ( jsonb_ne PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "3802 3802" _null_ _null_ _null_ _null_ jsonb_ne _null_ _null_ _null_ )); diff --git a/src/include/utils/json.h b/src/include/utils/json.h index b5e947bd7af..82cc48b7113 100644 --- a/src/include/utils/json.h +++ b/src/include/utils/json.h @@ -78,5 +78,7 @@ extern Datum jsonb_array_elements_text(PG_FUNCTION_ARGS); extern Datum jsonb_array_elements(PG_FUNCTION_ARGS); extern Datum jsonb_populate_record(PG_FUNCTION_ARGS); extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS); +extern Datum jsonb_to_record(PG_FUNCTION_ARGS); +extern Datum jsonb_to_recordset(PG_FUNCTION_ARGS); #endif /* JSON_H */ diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index f368530a193..8bd0131100d 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -1430,6 +1430,22 @@ SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' AS not_unescaped; null \u0000 escape (1 row) +-- jsonb_to_record and jsonb_to_recordset +select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}',true) + as x(a int, b text, d text); + a | b | d +---+-----+--- + 1 | foo | +(1 row) + +select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]',false) + as x(a int, b text, c boolean); + a | b | c +---+-----+--- + 1 | foo | + 2 | bar | t +(2 rows) + -- indexing SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; count diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out index 856c55af717..35524fb9a7e 100644 --- a/src/test/regress/expected/jsonb_1.out +++ b/src/test/regress/expected/jsonb_1.out @@ -1430,6 +1430,22 @@ SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' AS not_unescaped; null \u0000 escape (1 row) +-- jsonb_to_record and jsonb_to_recordset +select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}',true) + as x(a int, b text, d text); + a | b | d +---+-----+--- + 1 | foo | +(1 row) + +select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]',false) + as x(a int, b text, c boolean); + a | b | c +---+-----+--- + 1 | foo | + 2 | bar | t +(2 rows) + -- indexing SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; count diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index e460b1bb2a7..3ee43e93470 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -319,6 +319,14 @@ SELECT jsonb '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' AS correct_in_utf8; SELECT jsonb '{ "a": "dollar \u0024 character" }' ->> 'a' AS correct_everyWHERE; SELECT jsonb '{ "a": "null \u0000 escape" }' ->> 'a' AS not_unescaped; +-- jsonb_to_record and jsonb_to_recordset + +select * from jsonb_to_record('{"a":1,"b":"foo","c":"bar"}',true) + as x(a int, b text, d text); + +select * from jsonb_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]',false) + as x(a int, b text, c boolean); + -- indexing SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":"CC"}'; -- GitLab