Commit d9134d0a authored by Andrew Dunstan's avatar Andrew Dunstan

Introduce jsonb, a structured format for storing json.

The new format accepts exactly the same data as the json type. However, it is
stored in a format that does not require reparsing the orgiginal text in order
to process it, making it much more suitable for indexing and other operations.
Insignificant whitespace is discarded, and the order of object keys is not
preserved. Neither are duplicate object keys kept - the later value for a given
key is the only one stored.

The new type has all the functions and operators that the json type has,
with the exception of the json generation functions (to_json, json_agg etc.)
and with identical semantics. In addition, there are operator classes for
hash and btree indexing, and two classes for GIN indexing, that have no
equivalent in the json type.

This feature grew out of previous work by Oleg Bartunov and Teodor Sigaev, which
was intended to provide similar facilities to a nested hstore type, but which
in the end proved to have some significant compatibility issues.

Authors: Oleg Bartunov,  Teodor Sigaev, Peter Geoghegan and Andrew Dunstan.
Review: Andres Freund
parent b2b2491b
......@@ -5,7 +5,8 @@ OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \
crc32.o
EXTENSION = hstore
DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
DATA = hstore--1.3.sql hstore--1.2--1.3.sql \
hstore--1.1--1.2.sql hstore--1.0--1.1.sql \
hstore--unpackaged--1.0.sql
REGRESS = hstore
......
......@@ -1453,7 +1453,7 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe
1
(1 row)
-- json
-- json and jsonb
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
hstore_to_json
-------------------------------------------------------------------------------------------------
......@@ -1472,6 +1472,24 @@ select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012
{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}
(1 row)
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
hstore_to_jsonb
-------------------------------------------------------------------------------------------------
{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
(1 row)
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
jsonb
-------------------------------------------------------------------------------------------------
{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}
(1 row)
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
hstore_to_jsonb_loose
---------------------------------------------------------------------------------------
{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}
(1 row)
create table test_json_agg (f1 text, f2 hstore);
insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
......
/* contrib/hstore/hstore--1.2--1.3.sql */
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
\echo Use "ALTER EXTENSION hstore UPDATE TO '1.3'" to load this file. \quit
CREATE FUNCTION hstore_to_jsonb(hstore)
RETURNS jsonb
AS 'MODULE_PATHNAME', 'hstore_to_jsonb'
LANGUAGE C IMMUTABLE STRICT;
CREATE CAST (hstore AS jsonb)
WITH FUNCTION hstore_to_jsonb(hstore);
CREATE FUNCTION hstore_to_jsonb_loose(hstore)
RETURNS jsonb
AS 'MODULE_PATHNAME', 'hstore_to_jsonb_loose'
LANGUAGE C IMMUTABLE STRICT;
/* contrib/hstore/hstore--1.1.sql */
/* contrib/hstore/hstore--1.3.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION hstore" to load this file. \quit
......@@ -247,6 +247,19 @@ RETURNS json
AS 'MODULE_PATHNAME', 'hstore_to_json_loose'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION hstore_to_jsonb(hstore)
RETURNS jsonb
AS 'MODULE_PATHNAME', 'hstore_to_jsonb'
LANGUAGE C IMMUTABLE STRICT;
CREATE CAST (hstore AS jsonb)
WITH FUNCTION hstore_to_jsonb(hstore);
CREATE FUNCTION hstore_to_jsonb_loose(hstore)
RETURNS jsonb
AS 'MODULE_PATHNAME', 'hstore_to_jsonb_loose'
LANGUAGE C IMMUTABLE STRICT;
CREATE FUNCTION hstore(record)
RETURNS hstore
AS 'MODULE_PATHNAME', 'hstore_from_record'
......
# hstore extension
comment = 'data type for storing sets of (key, value) pairs'
default_version = '1.2'
default_version = '1.3'
module_pathname = '$libdir/hstore'
relocatable = true
......@@ -12,6 +12,7 @@
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/json.h"
#include "utils/jsonb.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
......@@ -1374,3 +1375,167 @@ hstore_to_json(PG_FUNCTION_ARGS)
PG_RETURN_TEXT_P(cstring_to_text(dst.data));
}
PG_FUNCTION_INFO_V1(hstore_to_jsonb);
Datum hstore_to_jsonb(PG_FUNCTION_ARGS);
Datum
hstore_to_jsonb(PG_FUNCTION_ARGS)
{
HStore *in = PG_GETARG_HS(0);
int i;
int count = HS_COUNT(in);
char *base = STRPTR(in);
HEntry *entries = ARRPTR(in);
JsonbParseState *state = NULL;
JsonbValue *res;
res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
for (i = 0; i < count; i++)
{
JsonbValue key, val;
key.estSize = sizeof(JEntry);
key.type = jbvString;
key.string.len = HS_KEYLEN(entries, i);
key.string.val = pnstrdup(HS_KEY(entries, base, i), key.string.len);
key.estSize += key.string.len;
res = pushJsonbValue(&state, WJB_KEY, &key);
if (HS_VALISNULL(entries, i))
{
val.estSize = sizeof(JEntry);
val.type = jbvNull;
}
else
{
val.estSize = sizeof(JEntry);
val.type = jbvString;
val.string.len = HS_VALLEN(entries, i);
val.string.val = pnstrdup(HS_VAL(entries, base, i), val.string.len);
val.estSize += val.string.len;
}
res = pushJsonbValue(&state, WJB_VALUE, &val);
}
res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
PG_RETURN_POINTER(JsonbValueToJsonb(res));
}
PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
Datum hstore_to_jsonb_loose(PG_FUNCTION_ARGS);
Datum
hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
{
HStore *in = PG_GETARG_HS(0);
int i;
int count = HS_COUNT(in);
char *base = STRPTR(in);
HEntry *entries = ARRPTR(in);
JsonbParseState *state = NULL;
JsonbValue *res;
StringInfoData tmp;
bool is_number;
initStringInfo(&tmp);
res = pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
for (i = 0; i < count; i++)
{
JsonbValue key, val;
key.estSize = sizeof(JEntry);
key.type = jbvString;
key.string.len = HS_KEYLEN(entries, i);
key.string.val = pnstrdup(HS_KEY(entries, base, i), key.string.len);
key.estSize += key.string.len;
res = pushJsonbValue(&state, WJB_KEY, &key);
val.estSize = sizeof(JEntry);
if (HS_VALISNULL(entries, i))
{
val.type = jbvNull;
}
/* guess that values of 't' or 'f' are booleans */
else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't')
{
val.type = jbvBool;
val.boolean = true;
}
else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f')
{
val.type = jbvBool;
val.boolean = false;
}
else
{
is_number = false;
resetStringInfo(&tmp);
appendBinaryStringInfo(&tmp, HS_VAL(entries, base, i), HS_VALLEN(entries, i));
/*
* don't treat something with a leading zero followed by another
* digit as numeric - could be a zip code or similar
*/
if (tmp.len > 0 &&
!(tmp.data[0] == '0' &&
isdigit((unsigned char) tmp.data[1])) &&
strspn(tmp.data, "+-0123456789Ee.") == tmp.len)
{
/*
* might be a number. See if we can input it as a numeric
* value. Ignore any actual parsed value.
*/
char *endptr = "junk";
long lval;
lval = strtol(tmp.data, &endptr, 10);
(void) lval;
if (*endptr == '\0')
{
/*
* strol man page says this means the whole string is
* valid
*/
is_number = true;
}
else
{
/* not an int - try a double */
double dval;
dval = strtod(tmp.data, &endptr);
(void) dval;
if (*endptr == '\0')
is_number = true;
}
}
if (is_number)
{
val.type = jbvNumeric;
val.numeric = DatumGetNumeric(
DirectFunctionCall3(numeric_in, CStringGetDatum(tmp.data), 0, -1));
val.estSize += VARSIZE_ANY(val.numeric) +sizeof(JEntry);
}
else
{
val.estSize = sizeof(JEntry);
val.type = jbvString;
val.string.len = HS_VALLEN(entries, i);
val.string.val = pnstrdup(HS_VAL(entries, base, i), val.string.len);
val.estSize += val.string.len;
}
}
res = pushJsonbValue(&state, WJB_VALUE, &val);
}
res = pushJsonbValue(&state, WJB_END_OBJECT, NULL);
PG_RETURN_POINTER(JsonbValueToJsonb(res));
}
......@@ -331,11 +331,15 @@ set enable_seqscan=off;
select count(*) from testhstore where h #># 'p=>1';
select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t';
-- json
-- json and jsonb
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json);
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
select hstore_to_jsonb('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as jsonb);
select hstore_to_jsonb_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4');
create table test_json_agg (f1 text, f2 hstore);
insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'),
('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4');
......
......@@ -139,7 +139,13 @@
<row>
<entry><type>json</type></entry>
<entry></entry>
<entry>JSON data</entry>
<entry>textual JSON data</entry>
</row>
<row>
<entry><type>jsonb</type></entry>
<entry></entry>
<entry>binary JSON data, decomposed</entry>
</row>
<row>
......@@ -4220,34 +4226,7 @@ SET xmloption TO { DOCUMENT | CONTENT };
</sect2>
</sect1>
<sect1 id="datatype-json">
<title><acronym>JSON</> Type</title>
<indexterm zone="datatype-json">
<primary>JSON</primary>
</indexterm>
<para>
The <type>json</type> data type can be used to store JSON (JavaScript
Object Notation) data, as specified in <ulink
url="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</ulink>. Such
data can also be stored as <type>text</type>, but the
<type>json</type> data type has the advantage of checking that each
stored value is a valid JSON value. There are also related support
functions available; see <xref linkend="functions-json">.
</para>
<para>
<productname>PostgreSQL</productname> allows only one server encoding
per database. It is therefore not possible for JSON to conform rigidly
to the specification unless the server encoding is UTF-8. Attempts to
directly include characters which cannot be represented in the server
encoding will fail; conversely, characters which can be represented in
the server encoding but not in UTF-8 will be allowed.
<literal>\uXXXX</literal> escapes are allowed regardless of the server
encoding, and are checked only for syntactic correctness.
</para>
</sect1>
&json;
&array;
......
......@@ -22,6 +22,7 @@
<!ENTITY dml SYSTEM "dml.sgml">
<!ENTITY func SYSTEM "func.sgml">
<!ENTITY indices SYSTEM "indices.sgml">
<!ENTITY json SYSTEM "json.sgml">
<!ENTITY mvcc SYSTEM "mvcc.sgml">
<!ENTITY perform SYSTEM "perform.sgml">
<!ENTITY queries SYSTEM "queries.sgml">
......
This diff is collapsed.
This diff is collapsed.
......@@ -825,6 +825,14 @@ CREATE OR REPLACE FUNCTION
json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100 AS 'json_populate_recordset';
CREATE OR REPLACE FUNCTION
jsonb_populate_record(base anyelement, from_json jsonb, use_json_as_text boolean DEFAULT false)
RETURNS anyelement LANGUAGE internal STABLE AS 'jsonb_populate_record';
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 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)
......
......@@ -21,11 +21,11 @@ OBJS = acl.o arrayfuncs.o array_selfuncs.o array_typanalyze.o \
cash.o char.o date.o datetime.o datum.o dbsize.o domains.o \
encode.o enum.o float.o format_type.o formatting.o genfile.o \
geo_ops.o geo_selfuncs.o inet_cidr_ntop.o inet_net_pton.o int.o \
int8.o json.o jsonfuncs.o like.o \
lockfuncs.o mac.o misc.o nabstime.o name.o network.o numeric.o \
numutils.o oid.o oracle_compat.o orderedsetaggs.o \
pg_lzcompress.o pg_locale.o pg_lsn.o pgstatfuncs.o \
pseudotypes.o quote.o rangetypes.o rangetypes_gist.o \
int8.o json.o jsonb.o jsonb_gin.o jsonb_op.o jsonb_util.o \
jsonfuncs.o like.o lockfuncs.o mac.o misc.o nabstime.o name.o \
network.o numeric.o numutils.o oid.o oracle_compat.o \
orderedsetaggs.o pg_lzcompress.o pg_locale.o pg_lsn.o \
pgstatfuncs.o pseudotypes.o quote.o rangetypes.o rangetypes_gist.o \
rangetypes_selfuncs.o rangetypes_spgist.o rangetypes_typanalyze.o \
regexp.o regproc.o ri_triggers.o rowtypes.o ruleutils.o \
selfuncs.o tid.o timestamp.o trigfuncs.o \
......
......@@ -210,22 +210,17 @@ Datum
json_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
text *result;
char *str;
int nbytes;
JsonLexContext *lex;
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
result = palloc(nbytes + VARHDRSZ);
SET_VARSIZE(result, nbytes + VARHDRSZ);
memcpy(VARDATA(result), str, nbytes);
/* Validate it. */
lex = makeJsonLexContext(result, false);
lex = makeJsonLexContextCstringLen(str, nbytes, false);
pg_parse_json(lex, &nullSemAction);
PG_RETURN_TEXT_P(result);
PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
}
/*
......@@ -236,15 +231,26 @@ json_recv(PG_FUNCTION_ARGS)
*
* Without is better as it makes the processing faster, so only make one
* if really required.
*
* If you already have the json as a text* value, use the first of these
* functions, otherwise use makeJsonLexContextCstringLen().
*/
JsonLexContext *
makeJsonLexContext(text *json, bool need_escapes)
{
return makeJsonLexContextCstringLen(VARDATA(json),
VARSIZE(json) - VARHDRSZ,
need_escapes);
}
JsonLexContext *
makeJsonLexContextCstringLen(char *json, int len, bool need_escapes)
{
JsonLexContext *lex = palloc0(sizeof(JsonLexContext));
lex->input = lex->token_terminator = lex->line_start = VARDATA(json);
lex->input = lex->token_terminator = lex->line_start = json;
lex->line_number = 1;
lex->input_length = VARSIZE(json) - VARHDRSZ;
lex->input_length = len;
if (need_escapes)
lex->strval = makeStringInfo();
return lex;
......@@ -1274,7 +1280,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
pfree(outputstr);
break;
case TYPCATEGORY_JSON:
/* JSON will already be escaped */
/* JSON and JSONB will already be escaped */
outputstr = OidOutputFunctionCall(typoutputfunc, val);
appendStringInfoString(result, outputstr);
pfree(outputstr);
......@@ -1406,7 +1412,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
tcategory = TYPCATEGORY_JSON_CAST;
else if (element_type == RECORDOID)
tcategory = TYPCATEGORY_COMPOSITE;
else if (element_type == JSONOID)
else if (element_type == JSONOID || element_type == JSONBOID)
tcategory = TYPCATEGORY_JSON;
else
tcategory = TypeCategory(element_type);
......@@ -1501,7 +1507,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
tcategory = TYPCATEGORY_ARRAY;
else if (tupdesc->attrs[i]->atttypid == RECORDOID)
tcategory = TYPCATEGORY_COMPOSITE;
else if (tupdesc->attrs[i]->atttypid == JSONOID)
else if (tupdesc->attrs[i]->atttypid == JSONOID ||
tupdesc->attrs[i]->atttypid == JSONBOID)
tcategory = TYPCATEGORY_JSON;
else
tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
......@@ -1689,7 +1696,7 @@ to_json(PG_FUNCTION_ARGS)
tcategory = TYPCATEGORY_ARRAY;
else if (val_type == RECORDOID)
tcategory = TYPCATEGORY_COMPOSITE;
else if (val_type == JSONOID)
else if (val_type == JSONOID || val_type == JSONBOID)
tcategory = TYPCATEGORY_JSON;
else
tcategory = TypeCategory(val_type);
......@@ -1783,7 +1790,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
tcategory = TYPCATEGORY_ARRAY;
else if (val_type == RECORDOID)
tcategory = TYPCATEGORY_COMPOSITE;
else if (val_type == JSONOID)
else if (val_type == JSONOID || val_type == JSONBOID)
tcategory = TYPCATEGORY_JSON;
else
tcategory = TypeCategory(val_type);
......@@ -2346,12 +2353,15 @@ escape_json(StringInfo buf, const char *str)
Datum
json_typeof(PG_FUNCTION_ARGS)
{
text *json = PG_GETARG_TEXT_P(0);
text *json;
JsonLexContext *lex = makeJsonLexContext(json, false);
JsonLexContext *lex;
JsonTokenType tok;
char *type;
json = PG_GETARG_TEXT_P(0);
lex = makeJsonLexContext(json, false);
/* Lex exactly one token from the input and check its type. */
json_lex(lex);
tok = lex_peek(lex);
......
This diff is collapsed.
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* jsonb_op.c
* Special operators for jsonb only, used by various index access methods
*
* Copyright (c) 2014, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* src/backend/utils/adt/jsonb_op.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "utils/jsonb.h"
Datum
jsonb_exists(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB(0);
text *key = PG_GETARG_TEXT_PP(1);
JsonbValue kval;
JsonbValue *v = NULL;
/*
* We only match Object keys (which are naturally always Strings), or
* string elements in arrays. In particular, we do not match non-string
* scalar elements. Existence of a key/element is only considered at the
* top level. No recursion occurs.
*/
kval.type = jbvString;
kval.string.val = VARDATA_ANY(key);
kval.string.len = VARSIZE_ANY_EXHDR(key);
v = findJsonbValueFromSuperHeader(VARDATA(jb),
JB_FOBJECT | JB_FARRAY,
NULL,
&kval);
PG_RETURN_BOOL(v != NULL);
}
Datum
jsonb_exists_any(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
JsonbValue *arrKey = arrayToJsonbSortedArray(keys);
uint32 *plowbound = NULL,
lowbound = 0;
int i;
if (arrKey == NULL || arrKey->object.nPairs == 0)
PG_RETURN_BOOL(false);
if (JB_ROOT_IS_OBJECT(jb))
plowbound = &lowbound;
/*
* We exploit the fact that the pairs list is already sorted into strictly
* increasing order to narrow the findJsonbValueFromSuperHeader search;
* each search can start one entry past the previous "found" entry, or at
* the lower bound of the last search.
*/
for (i = 0; i < arrKey->array.nElems; i++)
{
if (findJsonbValueFromSuperHeader(VARDATA(jb),
JB_FOBJECT | JB_FARRAY,
plowbound,
arrKey->array.elems + i) != NULL)
PG_RETURN_BOOL(true);
}
PG_RETURN_BOOL(false);
}
Datum
jsonb_exists_all(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
JsonbValue *arrKey = arrayToJsonbSortedArray(keys);
uint32 *plowbound = NULL;
uint32 lowbound = 0;
int i;
if (arrKey == NULL || arrKey->array.nElems == 0)
PG_RETURN_BOOL(true);
if (JB_ROOT_IS_OBJECT(jb))
plowbound = &lowbound;
/*
* We exploit the fact that the pairs list is already sorted into strictly
* increasing order to narrow the findJsonbValueFromSuperHeader search;
* each search can start one entry past the previous "found" entry, or at
* the lower bound of the last search.
*/
for (i = 0; i < arrKey->array.nElems; i++)
{
if (findJsonbValueFromSuperHeader(VARDATA(jb),
JB_FOBJECT | JB_FARRAY,
plowbound,
arrKey->array.elems + i) == NULL)
PG_RETURN_BOOL(false);
}
PG_RETURN_BOOL(true);
}
Datum
jsonb_contains(PG_FUNCTION_ARGS)
{
Jsonb *val = PG_GETARG_JSONB(0);
Jsonb *tmpl = PG_GETARG_JSONB(1);
JsonbIterator *it1, *it2;
if (JB_ROOT_COUNT(val) < JB_ROOT_COUNT(tmpl) ||
JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
PG_RETURN_BOOL(false);
it1 = JsonbIteratorInit(VARDATA(val));
it2 = JsonbIteratorInit(VARDATA(tmpl));
PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
}
Datum
jsonb_contained(PG_FUNCTION_ARGS)
{
/* Commutator of "contains" */
Jsonb *tmpl = PG_GETARG_JSONB(0);
Jsonb *val = PG_GETARG_JSONB(1);
JsonbIterator *it1, *it2;
if (JB_ROOT_COUNT(val) < JB_ROOT_COUNT(tmpl) ||
JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl))
PG_RETURN_BOOL(false);
it1 = JsonbIteratorInit(VARDATA(val));
it2 = JsonbIteratorInit(VARDATA(tmpl));
PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2));
}
Datum
jsonb_ne(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) != 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_BOOL(res);
}
/*
* B-Tree operator class operators, support function
*/
Datum
jsonb_lt(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) < 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_BOOL(res);
}
Datum
jsonb_gt(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) > 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_BOOL(res);
}
Datum
jsonb_le(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) <= 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_BOOL(res);
}
Datum
jsonb_ge(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) >= 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_BOOL(res);
}
Datum
jsonb_eq(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
bool res;
res = (compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb)) == 0);
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_BOOL(res);
}
Datum
jsonb_cmp(PG_FUNCTION_ARGS)
{
Jsonb *jba = PG_GETARG_JSONB(0);
Jsonb *jbb = PG_GETARG_JSONB(1);
int res;
res = compareJsonbSuperHeaderValue(VARDATA(jba), VARDATA(jbb));
PG_FREE_IF_COPY(jba, 0);
PG_FREE_IF_COPY(jbb, 1);
PG_RETURN_INT32(res);
}
/*
* Hash operator class jsonb hashing function
*/
Datum
jsonb_hash(PG_FUNCTION_ARGS)
{
Jsonb *jb = PG_GETARG_JSONB(0);
JsonbIterator *it;
int32 r;
JsonbValue v;
uint32 hash = 0;
if (JB_ROOT_COUNT(jb) == 0)
PG_RETURN_INT32(0);
it = JsonbIteratorInit(VARDATA(jb));
while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
{
switch (r)
{
/* Rotation is left to JsonbHashScalarValue() */
case WJB_BEGIN_ARRAY:
hash ^= JB_FARRAY;
break;
case WJB_BEGIN_OBJECT:
hash ^= JB_FOBJECT;
break;
case WJB_KEY:
case WJB_VALUE:
case WJB_ELEM:
JsonbHashScalarValue(&v, &hash);
break;
case WJB_END_ARRAY:
case WJB_END_OBJECT:
break;
default:
elog(ERROR, "invalid JsonbIteratorNext rc: %d", r);
}
}
PG_FREE_IF_COPY(jb, 0);
PG_RETURN_INT32(hash);
}
This diff is collapsed.
This diff is collapsed.
......@@ -626,6 +626,44 @@ numeric_out_sci(Numeric num, int scale)
return str;
}
/*
* numeric_normalize() -
*
* Output function for numeric data type without trailing zeroes.
*/
char *
numeric_normalize(Numeric num)
{
NumericVar x;
char *str;
int orig, last;
/*
* Handle NaN
*/
if (NUMERIC_IS_NAN(num))
return pstrdup("NaN");
init_var_from_num(num, &x);
str = get_str_from_var(&x);
orig = last = strlen(str) - 1;
for (;;)
{
if (last == 0 || str[last] != '0')
break;
last--;
}
if (last > 0 && last != orig)
str[last] = '\0';
return str;
}
/*
* numeric_recv - converts external binary format to numeric
*
......
......@@ -777,6 +777,33 @@ DATA(insert ( 4017 25 25 12 s 665 4000 0 ));
DATA(insert ( 4017 25 25 14 s 667 4000 0 ));
DATA(insert ( 4017 25 25 15 s 666 4000 0 ));
/*
* btree jsonb_ops
*/
DATA(insert ( 4033 3802 3802 1 s 3242 403 0 ));
DATA(insert ( 4033 3802 3802 2 s 3244 403 0 ));
DATA(insert ( 4033 3802 3802 3 s 3240 403 0 ));
DATA(insert ( 4033 3802 3802 4 s 3245 403 0 ));
DATA(insert ( 4033 3802 3802 5 s 3243 403 0 ));
/*
* hash jsonb ops
*/
DATA(insert ( 4034 3802 3802 1 s 3240 405 0 ));
/*
* GIN jsonb ops
*/
DATA(insert ( 4036 3802 3802 7 s 3246 2742 0 ));
DATA(insert ( 4036 3802 25 9 s 3247 2742 0 ));
DATA(insert ( 4036 3802 1009 10 s 3248 2742 0 ));
DATA(insert ( 4036 3802 1009 11 s 3249 2742 0 ));
/*
* GIN jsonb hash ops
*/
DATA(insert ( 4037 3802 3802 7 s 3246 2742 0 ));
/*
* SP-GiST range_ops
*/
......
......@@ -138,6 +138,7 @@ DATA(insert ( 3522 3500 3500 1 3514 ));
DATA(insert ( 3626 3614 3614 1 3622 ));
DATA(insert ( 3683 3615 3615 1 3668 ));
DATA(insert ( 3901 3831 3831 1 3870 ));
DATA(insert ( 4033 3802 3802 1 4044 ));
/* hash */
......@@ -175,6 +176,7 @@ DATA(insert ( 2235 1033 1033 1 329 ));
DATA(insert ( 2969 2950 2950 1 2963 ));
DATA(insert ( 3523 3500 3500 1 3515 ));
DATA(insert ( 3903 3831 3831 1 3902 ));
DATA(insert ( 4034 3802 3802 1 4045 ));
/* gist */
......@@ -387,7 +389,16 @@ DATA(insert ( 3659 3614 3614 3 3657 ));
DATA(insert ( 3659 3614 3614 4 3658 ));
DATA(insert ( 3659 3614 3614 5 2700 ));
DATA(insert ( 3659 3614 3614 6 3921 ));
DATA(insert ( 4036 3802 3802 1 3480 ));
DATA(insert ( 4036 3802 3802 2 3482 ));
DATA(insert ( 4036 3802 3802 3 3483 ));
DATA(insert ( 4036 3802 3802 4 3484 ));
DATA(insert ( 4036 3802 3802 6 3488 ));
DATA(insert ( 4037 3802 3802 1 351 ));
DATA(insert ( 4037 3802 3802 2 3485 ));
DATA(insert ( 4037 3802 3802 3 3486 ));
DATA(insert ( 4037 3802 3802 4 3487 ));
DATA(insert ( 4037 3802 3802 6 3489 ));
/* sp-gist */
DATA(insert ( 3474 3831 3831 1 3469 ));
......
......@@ -359,4 +359,8 @@ DATA(insert ( 1560 1560 1685 i f ));
DATA(insert ( 1562 1562 1687 i f ));
DATA(insert ( 1700 1700 1703 i f ));
/* json to/from jsonb */
DATA(insert ( 114 3802 0 e i ));
DATA(insert ( 3802 114 0 e i ));
#endif /* PG_CAST_H */
......@@ -228,5 +228,9 @@ DATA(insert ( 4000 range_ops PGNSP PGUID 3474 3831 t 0 ));
DATA(insert ( 4000 quad_point_ops PGNSP PGUID 4015 600 t 0 ));
DATA(insert ( 4000 kd_point_ops PGNSP PGUID 4016 600 f 0 ));
DATA(insert ( 4000 text_ops PGNSP PGUID 4017 25 t 0 ));
DATA(insert ( 403 jsonb_ops PGNSP PGUID 4033 3802 t 0 ));
DATA(insert ( 405 jsonb_ops PGNSP PGUID 4034 3802 t 0 ));
DATA(insert ( 2742 jsonb_ops PGNSP PGUID 4036 3802 t 25 ));
DATA(insert ( 2742 jsonb_hash_ops PGNSP PGUID 4037 3802 f 23 ));
#endif /* PG_OPCLASS_H */
......@@ -1769,8 +1769,41 @@ DATA(insert OID = 3966 ( "#>" PGNSP PGUID b f f 114 1009 114 0 0 json_extrac
DESCR("get value from json with path elements");
DATA(insert OID = 3967 ( "#>>" PGNSP PGUID b f f 114 1009 25 0 0 json_extract_path_text_op - - ));
DESCR("get value from json as text with path elements");
DATA(insert OID = 3211 ( "->" PGNSP PGUID b f f 3802 25 3802 0 0 jsonb_object_field - - ));
DESCR("get jsonb object field");
DATA(insert OID = 3477 ( "->>" PGNSP PGUID b f f 3802 25 25 0 0 jsonb_object_field_text - - ));
DESCR("get jsonb object field as text");
DATA(insert OID = 3212 ( "->" PGNSP PGUID b f f 3802 23 3802 0 0 jsonb_array_element - - ));
DESCR("get jsonb array element");
DATA(insert OID = 3481 ( "->>" PGNSP PGUID b f f 3802 23 25 0 0 jsonb_array_element_text - - ));
DESCR("get jsonb array element as text");
DATA(insert OID = 3213 ( "#>" PGNSP PGUID b f f 3802 1009 3802 0 0 jsonb_extract_path_op - - ));
DESCR("get value from jsonb with path elements");
DATA(insert OID = 3206 ( "#>>" PGNSP PGUID b f f 3802 1009 25 0 0 jsonb_extract_path_text_op - - ));
DESCR("get value from jsonb as text with path elements");
DATA(insert OID = 3240 ( "=" PGNSP PGUID b t t 3802 3802 16 3240 3241 jsonb_eq eqsel eqjoinsel ));
DESCR("equal");
DATA(insert OID = 3241 ( "<>" PGNSP PGUID b f f 3802 3802 16 3241 3240 jsonb_ne neqsel neqjoinsel ));
DESCR("not equal");
DATA(insert OID = 3242 ( "<" PGNSP PGUID b f f 3802 3802 16 3243 3245 jsonb_lt scalarltsel scalarltjoinsel ));
DESCR("less than");
DATA(insert OID = 3243 ( ">" PGNSP PGUID b f f 3802 3802 16 3242 3244 jsonb_gt scalargtsel scalargtjoinsel ));
DESCR("greater than");
DATA(insert OID = 3244 ( "<=" PGNSP PGUID b f f 3802 3802 16 3245 3243 jsonb_le scalarltsel scalarltjoinsel ));
DESCR("less than or equal to");
DATA(insert OID = 3245 ( ">=" PGNSP PGUID b f f 3802 3802 16 3244 3242 jsonb_ge scalargtsel scalargtjoinsel ));
DESCR("greater than or equal to");
/* No commutator? */
DATA(insert OID = 3246 ( "@>" PGNSP PGUID b f f 3802 3802 16 0 3250 jsonb_contains contsel contjoinsel ));
DESCR("contains");
DATA(insert OID = 3247 ( "?" PGNSP PGUID b f f 3802 25 16 0 0 jsonb_exists contsel contjoinsel ));
DESCR("exists");
DATA(insert OID = 3248 ( "?|" PGNSP PGUID b f f 3802 1009 16 0 0 jsonb_exists_any contsel contjoinsel ));
DESCR("exists any");
DATA(insert OID = 3249 ( "?&" PGNSP PGUID b f f 3802 1009 16 0 0 jsonb_exists_all contsel contjoinsel ));
DESCR("exists all");
DATA(insert OID = 3250 ( "<@" PGNSP PGUID b f f 3802 3802 16 0 3246 jsonb_contained contsel contjoinsel ));
DESCR("contained");
/*
* function prototypes
......
......@@ -147,6 +147,11 @@ DATA(insert OID = 3474 ( 4000 range_ops PGNSP PGUID ));
DATA(insert OID = 4015 ( 4000 quad_point_ops PGNSP PGUID ));
DATA(insert OID = 4016 ( 4000 kd_point_ops PGNSP PGUID ));
DATA(insert OID = 4017 ( 4000 text_ops PGNSP PGUID ));
DATA(insert OID = 4033 ( 403 jsonb_ops PGNSP PGUID ));
DATA(insert OID = 4034 ( 405 jsonb_ops PGNSP PGUID ));
DATA(insert OID = 4035 ( 783 jsonb_ops PGNSP PGUID ));
DATA(insert OID = 4036 ( 2742 jsonb_ops PGNSP PGUID ));
DATA(insert OID = 4037 ( 2742 jsonb_hash_ops PGNSP PGUID ));
#define TEXT_SPGIST_FAM_OID 4017
#endif /* PG_OPFAMILY_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -58,5 +58,6 @@ typedef struct NumericData *Numeric;
extern bool numeric_is_nan(Numeric num);
int32 numeric_maximum_size(int32 typmod);
extern char *numeric_out_sci(Numeric num, int scale);
extern char *numeric_normalize(Numeric num);
#endif /* _PG_NUMERIC_H_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment