Skip to content

Commit 4d212ba

Browse files
committed
json_typeof function.
Andrew Tipton.
1 parent 4b7b9a7 commit 4d212ba

File tree

8 files changed

+158
-4
lines changed

8 files changed

+158
-4
lines changed

doc/src/sgml/func.sgml

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10095,7 +10095,7 @@ table2-mapping
1009510095
</entry>
1009610096
<entry><type>json</type></entry>
1009710097
<entry>
10098-
Returns JSON object pointed to by <parameter>path_elems</parameter>.
10098+
Returns JSON value pointed to by <parameter>path_elems</parameter>.
1009910099
</entry>
1010010100
<entry><literal>json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4')</literal></entry>
1010110101
<entry><literal>{"f5":99,"f6":"foo"}</literal></entry>
@@ -10109,7 +10109,7 @@ table2-mapping
1010910109
</entry>
1011010110
<entry><type>text</type></entry>
1011110111
<entry>
10112-
Returns JSON object pointed to by <parameter>path_elems</parameter>.
10112+
Returns JSON value pointed to by <parameter>path_elems</parameter>.
1011310113
</entry>
1011410114
<entry><literal>json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4', 'f6')</literal></entry>
1011510115
<entry><literal>foo</literal></entry>
@@ -10192,7 +10192,7 @@ table2-mapping
1019210192
</entry>
1019310193
<entry><type>SETOF json</type></entry>
1019410194
<entry>
10195-
Expands a JSON array to a set of JSON elements.
10195+
Expands a JSON array to a set of JSON values.
1019610196
</entry>
1019710197
<entry><literal>json_array_elements('[1,true, [2,false]]')</literal></entry>
1019810198
<entry>
@@ -10205,6 +10205,23 @@ table2-mapping
1020510205
</programlisting>
1020610206
</entry>
1020710207
</row>
10208+
<row>
10209+
<entry>
10210+
<indexterm>
10211+
<primary>json_typeof</primary>
10212+
</indexterm>
10213+
<literal>json_typeof(json)</literal>
10214+
</entry>
10215+
<entry><type>text</type></entry>
10216+
<entry>
10217+
Returns the type of the outermost JSON value as a text string. The types are
10218+
<literal>object</>, <literal>array</>, <literal>string</>, <literal>number</>,
10219+
<literal>boolean</>, and <literal>null</>. (See note below regarding the
10220+
distinction between a JSON <literal>null</> and a SQL NULL.)
10221+
</entry>
10222+
<entry><literal>json_typeof('-123.4')</literal></entry>
10223+
<entry><literal>number</literal></entry>
10224+
</row>
1020810225
</tbody>
1020910226
</tgroup>
1021010227
</table>
@@ -10237,6 +10254,14 @@ table2-mapping
1023710254
</para>
1023810255
</note>
1023910256

10257+
<note>
10258+
<para>
10259+
The <literal>json_typeof</> function's <literal>null</> return value should not be confused
10260+
with a SQL NULL. While calling <literal>json_typeof('null'::json)</> will return <literal>null</>,
10261+
calling <literal>json_typeof(NULL::json)</> will return a SQL NULL.
10262+
</para>
10263+
</note>
10264+
1024010265
<para>
1024110266
See also <xref linkend="functions-aggregate"> about the aggregate
1024210267
function <function>json_agg</function> which aggregates record

src/backend/utils/adt/json.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,3 +1826,55 @@ escape_json(StringInfo buf, const char *str)
18261826
}
18271827
appendStringInfoCharMacro(buf, '\"');
18281828
}
1829+
1830+
/*
1831+
* SQL function json_typeof(json) -> text
1832+
*
1833+
* Returns the type of the outermost JSON value as TEXT. Possible types are
1834+
* "object", "array", "string", "number", "boolean", and "null".
1835+
*
1836+
* Performs a single call to json_lex() to get the first token of the supplied
1837+
* value. This initial token uniquely determines the value's type. As our
1838+
* input must already have been validated by json_in() or json_recv(), the
1839+
* initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
1840+
* JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
1841+
*/
1842+
Datum
1843+
json_typeof(PG_FUNCTION_ARGS)
1844+
{
1845+
text *json = PG_GETARG_TEXT_P(0);
1846+
1847+
JsonLexContext *lex = makeJsonLexContext(json, false);
1848+
JsonTokenType tok;
1849+
char *type;
1850+
1851+
/* Lex exactly one token from the input and check its type. */
1852+
json_lex(lex);
1853+
tok = lex_peek(lex);
1854+
switch (tok)
1855+
{
1856+
case JSON_TOKEN_OBJECT_START:
1857+
type = "object";
1858+
break;
1859+
case JSON_TOKEN_ARRAY_START:
1860+
type = "array";
1861+
break;
1862+
case JSON_TOKEN_STRING:
1863+
type = "string";
1864+
break;
1865+
case JSON_TOKEN_NUMBER:
1866+
type = "number";
1867+
break;
1868+
case JSON_TOKEN_TRUE:
1869+
case JSON_TOKEN_FALSE:
1870+
type = "boolean";
1871+
break;
1872+
case JSON_TOKEN_NULL:
1873+
type = "null";
1874+
break;
1875+
default:
1876+
elog(ERROR, "unexpected json token: %d", tok);
1877+
}
1878+
1879+
PG_RETURN_TEXT_P(cstring_to_text(type));
1880+
}

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 201310091
56+
#define CATALOG_VERSION_NO 201310101
5757

5858
#endif

src/include/catalog/pg_proc.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4150,6 +4150,8 @@ DATA(insert OID = 3960 ( json_populate_record PGNSP PGUID 12 1 0 0 0 f f f f
41504150
DESCR("get record fields from a json object");
41514151
DATA(insert OID = 3961 ( json_populate_recordset PGNSP PGUID 12 1 100 0 0 f f f f f t s 3 0 2283 "2283 114 16" _null_ _null_ _null_ _null_ json_populate_recordset _null_ _null_ _null_ ));
41524152
DESCR("get set of records with fields from a json array of objects");
4153+
DATA(insert OID = 3968 ( json_typeof PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "114" _null_ _null_ _null_ _null_ json_typeof _null_ _null_ _null_ ));
4154+
DESCR("get the type of a json value");
41534155

41544156
/* uuid */
41554157
DATA(insert OID = 2952 ( uuid_in PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2950 "2275" _null_ _null_ _null_ _null_ uuid_in _null_ _null_ _null_ ));

src/include/utils/json.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ extern Datum json_agg_finalfn(PG_FUNCTION_ARGS);
3333

3434
extern void escape_json(StringInfo buf, const char *str);
3535

36+
extern Datum json_typeof(PG_FUNCTION_ARGS);
37+
3638
/* functions in jsonfuncs.c */
3739
extern Datum json_object_field(PG_FUNCTION_ARGS);
3840
extern Datum json_object_field_text(PG_FUNCTION_ARGS);

src/test/regress/expected/json.out

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,3 +962,32 @@ select json '{ "a": "null \u0000 escape" }' ->> 'a' as not_unescaped;
962962
null \u0000 escape
963963
(1 row)
964964

965+
--json_typeof() function
966+
select value, json_typeof(value)
967+
from (values (json '123.4'),
968+
(json '-1'),
969+
(json '"foo"'),
970+
(json 'true'),
971+
(json 'false'),
972+
(json 'null'),
973+
(json '[1, 2, 3]'),
974+
(json '[]'),
975+
(json '{"x":"foo", "y":123}'),
976+
(json '{}'),
977+
(NULL::json))
978+
as data(value);
979+
value | json_typeof
980+
----------------------+-------------
981+
123.4 | number
982+
-1 | number
983+
"foo" | string
984+
true | boolean
985+
false | boolean
986+
null | null
987+
[1, 2, 3] | array
988+
[] | array
989+
{"x":"foo", "y":123} | object
990+
{} | object
991+
|
992+
(11 rows)
993+

src/test/regress/expected/json_1.out

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,3 +958,32 @@ select json '{ "a": "null \u0000 escape" }' ->> 'a' as not_unescaped;
958958
null \u0000 escape
959959
(1 row)
960960

961+
--json_typeof() function
962+
select value, json_typeof(value)
963+
from (values (json '123.4'),
964+
(json '-1'),
965+
(json '"foo"'),
966+
(json 'true'),
967+
(json 'false'),
968+
(json 'null'),
969+
(json '[1, 2, 3]'),
970+
(json '[]'),
971+
(json '{"x":"foo", "y":123}'),
972+
(json '{}'),
973+
(NULL::json))
974+
as data(value);
975+
value | json_typeof
976+
----------------------+-------------
977+
123.4 | number
978+
-1 | number
979+
"foo" | string
980+
true | boolean
981+
false | boolean
982+
null | null
983+
[1, 2, 3] | array
984+
[] | array
985+
{"x":"foo", "y":123} | object
986+
{} | object
987+
|
988+
(11 rows)
989+

src/test/regress/sql/json.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,18 @@ select json '{ "a": "\ude04X" }' -> 'a'; -- orphan low surrogate
310310
select json '{ "a": "the Copyright \u00a9 sign" }' ->> 'a' as correct_in_utf8;
311311
select json '{ "a": "dollar \u0024 character" }' ->> 'a' as correct_everywhere;
312312
select json '{ "a": "null \u0000 escape" }' ->> 'a' as not_unescaped;
313+
314+
--json_typeof() function
315+
select value, json_typeof(value)
316+
from (values (json '123.4'),
317+
(json '-1'),
318+
(json '"foo"'),
319+
(json 'true'),
320+
(json 'false'),
321+
(json 'null'),
322+
(json '[1, 2, 3]'),
323+
(json '[]'),
324+
(json '{"x":"foo", "y":123}'),
325+
(json '{}'),
326+
(NULL::json))
327+
as data(value);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy