From 91f7fc0e4a4eb6be3a5d7a356badbb47fbea9a3e Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Wed, 14 May 2014 00:26:31 +0400 Subject: [PATCH 1/7] Vodka opclass. --- Makefile | 3 +- jsonb_gin_ops.c | 1 - jsonb_vodka_ops.c | 596 ++++++++++++++++++++++++++++++++++++++++++++++ jsquery--1.0.sql | 37 +++ jsquery.h | 2 + 5 files changed, 637 insertions(+), 2 deletions(-) create mode 100644 jsonb_vodka_ops.c diff --git a/Makefile b/Makefile index cfee7be..90fc8a1 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ MODULE_big = jsquery OBJS = jsquery_io.o jsquery_gram.o jsquery_op.o \ - jsquery_constr.o jsquery_extract.o jsonb_gin_ops.o + jsquery_constr.o jsquery_extract.o jsonb_gin_ops.o \ + jsonb_vodka_ops.o EXTENSION = jsquery DATA = jsquery--1.0.sql diff --git a/jsonb_gin_ops.c b/jsonb_gin_ops.c index be93f31..82a1892 100644 --- a/jsonb_gin_ops.c +++ b/jsonb_gin_ops.c @@ -36,7 +36,6 @@ typedef struct #define BLOOM_BITS 2 #define JsonbNestedContainsStrategyNumber 13 -#define JsQueryMatchStrategyNumber 14 typedef struct { diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c new file mode 100644 index 0000000..5269d87 --- /dev/null +++ b/jsonb_vodka_ops.c @@ -0,0 +1,596 @@ +/*------------------------------------------------------------------------- + * + * vodkaarrayproc.c + * support functions for VODKA's indexing of any array + * + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/access/vodka/vodkaarrayproc.c + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/hash.h" +#include "access/vodka.h" +#include "access/skey.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_opclass.h" +#include "utils/builtins.h" +#include "utils/jsonb.h" +#include "utils/numeric.h" +#include "utils/lsyscache.h" + +#include "jsquery.h" + +typedef struct +{ + VodkaKey *entries; + int count, total; +} Entries; + +#define JSONB_VODKA_FLAG_VALUE 0x01 + +#define JSONB_VODKA_FLAG_NULL 0x00 +#define JSONB_VODKA_FLAG_STRING 0x02 +#define JSONB_VODKA_FLAG_NUMERIC 0x04 +#define JSONB_VODKA_FLAG_BOOL 0x06 +#define JSONB_VODKA_FLAG_TRUE 0x08 +#define JSONB_VODKA_FLAG_NAN 0x08 +#define JSONB_VODKA_FLAG_NEGATIVE 0x10 + +#define JSONB_VODKA_FLAG_ARRAY 0x02 + +PG_FUNCTION_INFO_V1(vodkajsonbconfig); +PG_FUNCTION_INFO_V1(vodkajsonbextract); +PG_FUNCTION_INFO_V1(vodkaqueryjsonbextract); +PG_FUNCTION_INFO_V1(vodkajsonbconsistent); +PG_FUNCTION_INFO_V1(vodkajsonbtriconsistent); + +Datum vodkajsonbconfig(PG_FUNCTION_ARGS); +Datum vodkajsonbextract(PG_FUNCTION_ARGS); +Datum vodkaqueryjsonbextract(PG_FUNCTION_ARGS); +Datum vodkajsonbconsistent(PG_FUNCTION_ARGS); +Datum vodkajsonbtriconsistent(PG_FUNCTION_ARGS); + +static int +add_entry(Entries *e, Datum value, Pointer extra, Oid operator, bool isnull) +{ + VodkaKey *key; + int entryNum; + if (!e->entries) + { + e->total = 16; + e->entries = (VodkaKey *)palloc(e->total * sizeof(VodkaKey)); + } + if (e->count + 1 > e->total) + { + e->total *= 2; + e->entries = (VodkaKey *)repalloc(e->entries, e->total * sizeof(VodkaKey)); + } + entryNum = e->count; + e->count++; + key = &e->entries[entryNum]; + key->value = value; + key->extra = extra; + key->operator = operator; + key->isnull = isnull; + return entryNum; +} + + +Datum +vodkajsonbconfig(PG_FUNCTION_ARGS) +{ + /* VodkaConfigIn *in = (VodkaConfigIn *)PG_GETARG_POINTER(0); */ + VodkaConfigOut *out = (VodkaConfigOut *)PG_GETARG_POINTER(1); + + out->entryOpclass = BYTEA_SPGIST_OPS_OID; + out->entryEqualOperator = ByteaEqualOperator; + PG_RETURN_VOID(); +} + +typedef struct PathStack +{ + char *s; + int len; + struct PathStack *parent; +} PathStack; + +static int +get_ndigits(Numeric val) +{ + const NumericDigit *digits; + int ndigits; + + ndigits = NUMERIC_NDIGITS(val); + digits = NUMERIC_DIGITS(val); + + while (ndigits > 0 && *digits == 0) + { + ndigits--; + digits++; + } + return ndigits; +} + +static void +write_numeric_key(Pointer ptr, Numeric val) +{ + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NUMERIC; + if (NUMERIC_IS_NAN(val)) + { + *ptr |= JSONB_VODKA_FLAG_NAN; + } + else + { + const NumericDigit *digits = NUMERIC_DIGITS(val); + int ndigits = NUMERIC_NDIGITS(val); + int weight = NUMERIC_WEIGHT(val); + int sign = NUMERIC_SIGN(val); + + if (sign == NUMERIC_NEG) + *ptr |= JSONB_VODKA_FLAG_NEGATIVE; + ptr++; + + while (ndigits > 0 && *digits == 0) + { + ndigits--; + digits++; + } + + memcpy(ptr, &weight, sizeof(weight)); + ptr += sizeof(weight); + + memcpy(ptr, digits, sizeof(NumericDigit) * ndigits); + ptr += sizeof(NumericDigit) * ndigits; + + *ptr = 0; + } +} + +static bytea * +get_vodka_key(PathStack *stack, const JsonbValue *val) +{ + bytea *result; + int totallen = VARHDRSZ, vallen; + PathStack *tmp; + Pointer ptr; + uint32 hash; + + tmp = stack; + while (tmp) + { + if (tmp->s) + { + totallen += tmp->len + 2; + } + else + { + totallen++; + } + tmp = tmp->parent; + } + + switch (val->type) + { + case jbvNull: + case jbvBool: + vallen = 1; + break; + case jbvString: + vallen = 5; + break; + case jbvNumeric: + if (NUMERIC_IS_NAN(val->val.numeric)) + vallen = 1; + else + vallen = get_ndigits(val->val.numeric) + 6; + break; + default: + elog(ERROR, "invalid jsonb scalar type"); + } + + totallen += vallen; + result = (bytea *)palloc(totallen); + SET_VARSIZE(result, totallen); + ptr = (Pointer)result + totallen - vallen; + + tmp = stack; + while (tmp) + { + if (tmp->s) + { + ptr -= tmp->len + 2; + ptr[0] = 0; + memcpy(ptr + 1, tmp->s, tmp->len); + ptr[tmp->len + 1] = 0; + } + else + { + ptr--; + *ptr = JSONB_VODKA_FLAG_ARRAY; + } + tmp = tmp->parent; + } + + ptr = (Pointer)result + totallen - vallen; + + switch (val->type) + { + case jbvNull: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NULL; + break; + case jbvBool: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_BOOL; + if (val->val.boolean) + *ptr |= JSONB_VODKA_FLAG_TRUE; + break; + case jbvString: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; + hash = hash_any((unsigned char *)val->val.string.val, val->val.string.len); + memcpy(ptr + 1, &hash, sizeof(hash)); + break; + case jbvNumeric: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; + write_numeric_key(ptr, val->val.numeric); + break; + default: + elog(ERROR, "invalid jsonb scalar type"); + } + + return result; +} + +static int +make_entry_handler(ExtractedNode *node, Pointer extra) +{ + Entries *e = (Entries *)extra; + PathItem *item; + int totallen = VARHDRSZ, vallen, jqPos, len; + Pointer ptr; + JsQueryValue *value; + Numeric numeric = NULL; + char *jqBase; + uint32 hash; + bytea *result; + + Assert(node->type == eScalar); + + if (node->bounds.inequality || node->bounds.exact->type == jqiAny) + return -1; + + item = node->path; + while (item) + { + if (item->type == iKey) + totallen += item->len + 2; + else if (item->type == iAnyArray) + totallen += 1; + else + return -1; + item = item->parent; + } + + value = node->bounds.exact; + switch(value->type) + { + case jqiNull: + case jqiBool: + vallen = 1; + break; + case jqiString: + vallen = 5; + break; + case jqiNumeric: + numeric = (Numeric)(value->jqBase + value->jqPos); + if (NUMERIC_IS_NAN(numeric)) + vallen = 1; + else + vallen = get_ndigits(numeric) + 6; + break; + default: + elog(ERROR,"Wrong state"); + } + + totallen += vallen; + result = (bytea *)palloc(totallen); + SET_VARSIZE(result, totallen); + ptr = (Pointer)result + totallen - vallen; + + item = node->path; + while (item) + { + if (item->type == iKey) + { + ptr -= item->len + 2; + ptr[0] = 0; + memcpy(ptr + 1, item->s, item->len); + ptr[item->len + 1] = 0; + } + else if (item->type == iAnyArray) + { + ptr--; + *ptr = JSONB_VODKA_FLAG_ARRAY; + } + else + { + return -1; + } + item = item->parent; + } + + ptr = (Pointer)result + totallen - vallen; + jqBase = value->jqBase; + jqPos = value->jqPos; + + switch (value->type) + { + case jbvNull: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NULL; + break; + case jbvBool: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_BOOL; + read_byte(len, jqBase, jqPos); + if (len) + *ptr |= JSONB_VODKA_FLAG_TRUE; + break; + case jbvString: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; + read_int32(len, jqBase, jqPos); + hash = hash_any((unsigned char *)jqBase + jqPos, len); + memcpy(ptr + 1, &hash, sizeof(hash)); + break; + case jbvNumeric: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; + write_numeric_key(ptr, numeric); + break; + default: + elog(ERROR, "invalid jsonb scalar type"); + } + + return add_entry(e, PointerGetDatum(result), NULL, ByteaEqualOperator, false); +} + + +/* + * extractValue support function + */ +Datum +vodkajsonbextract(PG_FUNCTION_ARGS) +{ + Jsonb *jb = PG_GETARG_JSONB(0); + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + int total = 2 * JB_ROOT_COUNT(jb); + JsonbIterator *it; + JsonbValue v; + PathStack *stack; + int i = 0, + r; + Datum *entries = NULL; + + if (total == 0) + { + *nentries = 0; + PG_RETURN_POINTER(NULL); + } + + entries = (Datum *) palloc(sizeof(Datum) * total); + + it = JsonbIteratorInit(&jb->root); + stack = NULL; + + while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) + { + PathStack *tmp; + + if (i >= total) + { + total *= 2; + entries = (Datum *) repalloc(entries, sizeof(Datum) * total); + } + + switch (r) + { + case WJB_BEGIN_ARRAY: + case WJB_BEGIN_OBJECT: + tmp = stack; + stack = (PathStack *) palloc(sizeof(PathStack)); + stack->s = NULL; + stack->len = 0; + stack->parent = tmp; + break; + case WJB_KEY: + /* Initialize hash from parent */ + stack->s = v.val.string.val; + stack->len = v.val.string.len; + break; + case WJB_ELEM: + case WJB_VALUE: + entries[i++] = PointerGetDatum(get_vodka_key(stack, &v)); + break; + case WJB_END_ARRAY: + case WJB_END_OBJECT: + /* Pop the stack */ + tmp = stack->parent; + pfree(stack); + stack = tmp; + break; + default: + elog(ERROR, "invalid JsonbIteratorNext rc: %d", r); + } + } + + *nentries = i; + + PG_RETURN_POINTER(entries); +} + +/* + * extractQuery support function + */ +Datum +vodkaqueryjsonbextract(PG_FUNCTION_ARGS) +{ + int32 *nentries = (int32 *) PG_GETARG_POINTER(1); + StrategyNumber strategy = PG_GETARG_UINT16(2); + int32 *searchMode = (int32 *) PG_GETARG_POINTER(3); + Datum *entries; + VodkaKey *keys; + Entries e = {0}; + int i; + JsQuery *jq; + ExtractedNode *root; + + switch (strategy) + { + case JsonbContainsStrategyNumber: + /* Query is a jsonb, so just apply gin_extract_jsonb... */ + entries = (Datum *) + DatumGetPointer(DirectFunctionCall2(vodkajsonbextract, + PG_GETARG_DATUM(0), + PointerGetDatum(nentries))); + + keys = (VodkaKey *)palloc(sizeof(VodkaKey) * (*nentries)); + + for (i = 0; i < *nentries; i++) + { + keys[i].value = entries[i]; + keys[i].isnull = false; + keys[i].extra = NULL; + keys[i].operator = ByteaEqualOperator; + } + break; + + case JsQueryMatchStrategyNumber: + jq = PG_GETARG_JSQUERY(0); + root = extractJsQuery(jq, make_entry_handler, (Pointer)&e); + + *nentries = e.count; + keys = e.entries; + for (i = 0; i < e.count; i++) + keys[i].extra = (Pointer)root; + break; + + + break; + + default: + elog(ERROR, "unrecognized strategy number: %d", strategy); + break; + } + + + /* ...although "contains {}" requires a full index scan */ + if (*nentries == 0) + *searchMode = VODKA_SEARCH_MODE_ALL; + + PG_RETURN_POINTER(keys); +} + +/* + * consistent support function + */ +Datum +vodkajsonbconsistent(PG_FUNCTION_ARGS) +{ + bool *check = (bool *) PG_GETARG_POINTER(0); + StrategyNumber strategy = PG_GETARG_UINT16(1); + + /* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */ + int32 nkeys = PG_GETARG_INT32(3); + bool *recheck = (bool *) PG_GETARG_POINTER(4); + VodkaKey *queryKeys = (VodkaKey *) PG_GETARG_POINTER(5); + bool res; + int32 i; + + switch (strategy) + { + case JsonbContainsStrategyNumber: + /* result is not lossy */ + *recheck = false; + /* must have all elements in check[] true, and no nulls */ + res = true; + for (i = 0; i < nkeys; i++) + { + if (!check[i] || queryKeys[i].isnull) + { + res = false; + break; + } + } + break; + + case JsQueryMatchStrategyNumber: + if (nkeys == 0) + res = true; + else + res = execRecursive((ExtractedNode *)queryKeys[0].extra, check); + break; + + default: + elog(ERROR, "vodkajsonbconsistent: unknown strategy number: %d", + strategy); + res = false; + } + + PG_RETURN_BOOL(res); +} + +/* + * triconsistent support function + */ +Datum +vodkajsonbtriconsistent(PG_FUNCTION_ARGS) +{ + VodkaTernaryValue *check = (VodkaTernaryValue *) PG_GETARG_POINTER(0); + StrategyNumber strategy = PG_GETARG_UINT16(1); + + /* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */ + int32 nkeys = PG_GETARG_INT32(3); + + /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ + VodkaKey *queryKeys = (VodkaKey *) PG_GETARG_POINTER(4); + VodkaTernaryValue res; + int32 i; + + switch (strategy) + { + case JsonbContainsStrategyNumber: + /* must have all elements in check[] true, and no nulls */ + res = VODKA_TRUE; + for (i = 0; i < nkeys; i++) + { + if (check[i] == VODKA_FALSE || queryKeys[i].isnull) + { + res = VODKA_FALSE; + break; + } + if (check[i] == VODKA_MAYBE) + { + res = VODKA_MAYBE; + } + } + break; + + case JsQueryMatchStrategyNumber: + if (nkeys == 0) + res = GIN_MAYBE; + else + res = execRecursiveTristate((ExtractedNode *)queryKeys[0].extra, check); + + if (res == GIN_TRUE) + res = GIN_MAYBE; + + break; + + default: + elog(ERROR, "vodkajsonbconsistent: unknown strategy number: %d", + strategy); + res = false; + } + + PG_RETURN_VODKA_TERNARY_VALUE(res); +} diff --git a/jsquery--1.0.sql b/jsquery--1.0.sql index 50a171e..e3302fd 100644 --- a/jsquery--1.0.sql +++ b/jsquery--1.0.sql @@ -281,3 +281,40 @@ CREATE OPERATOR CLASS jsonb_hash_value_ops FUNCTION 5 gin_compare_partial_jsonb_hash_value(bytea, bytea, smallint, internal), FUNCTION 6 gin_triconsistent_jsonb_hash_value(internal, smallint, anyarray, integer, internal, internal, internal), STORAGE bytea; + +CREATE OR REPLACE FUNCTION vodkajsonbconfig(internal, internal) + RETURNS void + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION vodkajsonbextract(anyarray, internal, internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION vodkaqueryjsonbextract(anyarray, internal, smallint, internal) + RETURNS internal + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION vodkajsonbconsistent(internal, smallint, anyarray, integer, internal, internal, internal) + RETURNS boolean + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OR REPLACE FUNCTION vodkajsonbtriconsistent(internal, smallint, anyarray, integer, internal, internal) + RETURNS boolean + AS 'MODULE_PATHNAME' + LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR CLASS jsonb_ops DEFAULT FOR TYPE jsonb USING vodka AS + OPERATOR 7 @>, + OPERATOR 14 @@ (jsonb, jsquery), + FUNCTION 1 vodkajsonbconfig(internal, internal), + FUNCTION 2 byteacmp(bytea, bytea), + FUNCTION 3 vodkajsonbextract(anyarray, internal, internal), + FUNCTION 4 vodkaqueryjsonbextract(anyarray, internal, smallint, internal), + FUNCTION 5 vodkajsonbconsistent(internal, smallint, anyarray, integer, internal, internal, internal), + FUNCTION 6 vodkajsonbtriconsistent(internal, smallint, anyarray, integer, internal, internal), + STORAGE bytea; + \ No newline at end of file diff --git a/jsquery.h b/jsquery.h index 2c016ac..bf8ef80 100644 --- a/jsquery.h +++ b/jsquery.h @@ -165,6 +165,8 @@ struct ExtractedNode typedef int (*MakeEntryHandler)(ExtractedNode *node, Pointer extra); +#define JsQueryMatchStrategyNumber 14 + ExtractedNode *extractJsQuery(JsQuery *jq, MakeEntryHandler handler, Pointer extra); bool execRecursive(ExtractedNode *node, bool *check); bool execRecursiveTristate(ExtractedNode *node, GinTernaryValue *check); From 390f7df5012797b90d853f301304fef7415bee19 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Thu, 15 May 2014 18:51:28 +0400 Subject: [PATCH 2/7] Partly working vodka opclass... --- jsonb_vodka_ops.c | 169 ++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 94 deletions(-) diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c index 5269d87..9142512 100644 --- a/jsonb_vodka_ops.c +++ b/jsonb_vodka_ops.c @@ -37,6 +37,7 @@ typedef struct #define JSONB_VODKA_FLAG_STRING 0x02 #define JSONB_VODKA_FLAG_NUMERIC 0x04 #define JSONB_VODKA_FLAG_BOOL 0x06 +#define JSONB_VODKA_FLAG_TYPE 0x06 #define JSONB_VODKA_FLAG_TRUE 0x08 #define JSONB_VODKA_FLAG_NAN 0x08 #define JSONB_VODKA_FLAG_NEGATIVE 0x10 @@ -184,10 +185,7 @@ get_vodka_key(PathStack *stack, const JsonbValue *val) vallen = 5; break; case jbvNumeric: - if (NUMERIC_IS_NAN(val->val.numeric)) - vallen = 1; - else - vallen = get_ndigits(val->val.numeric) + 6; + vallen = 1 + VARSIZE_ANY(val->val.numeric); break; default: elog(ERROR, "invalid jsonb scalar type"); @@ -234,8 +232,8 @@ get_vodka_key(PathStack *stack, const JsonbValue *val) memcpy(ptr + 1, &hash, sizeof(hash)); break; case jbvNumeric: - *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; - write_numeric_key(ptr, val->val.numeric); + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NUMERIC; + memcpy(ptr + 1, val->val.numeric, VARSIZE_ANY(val->val.numeric)); break; default: elog(ERROR, "invalid jsonb scalar type"); @@ -244,116 +242,103 @@ get_vodka_key(PathStack *stack, const JsonbValue *val) return result; } -static int -make_entry_handler(ExtractedNode *node, Pointer extra) +typedef struct { - Entries *e = (Entries *)extra; - PathItem *item; - int totallen = VARHDRSZ, vallen, jqPos, len; - Pointer ptr; - JsQueryValue *value; - Numeric numeric = NULL; - char *jqBase; - uint32 hash; - bytea *result; - - Assert(node->type == eScalar); - - if (node->bounds.inequality || node->bounds.exact->type == jqiAny) - return -1; - - item = node->path; - while (item) - { - if (item->type == iKey) - totallen += item->len + 2; - else if (item->type == iAnyArray) - totallen += 1; - else - return -1; - item = item->parent; - } + uint8 type; + uint32 hash; + Numeric n; +} JsonbVodkaValue; - value = node->bounds.exact; - switch(value->type) - { - case jqiNull: - case jqiBool: - vallen = 1; - break; - case jqiString: - vallen = 5; - break; - case jqiNumeric: - numeric = (Numeric)(value->jqBase + value->jqPos); - if (NUMERIC_IS_NAN(numeric)) - vallen = 1; - else - vallen = get_ndigits(numeric) + 6; - break; - default: - elog(ERROR,"Wrong state"); - } +typedef struct +{ + int pathLength; + PathItem *path; + JsonbVodkaValue *exact, *leftBound, *rightBound; + bool inequality, leftInclusive, rightInclusive; +} JsonbVodkaKey; + +static JsonbVodkaValue * +make_vodka_value(JsQueryValue *value) +{ + JsonbVodkaValue *result; + int32 len, jqPos; + char *jqBase; - totallen += vallen; - result = (bytea *)palloc(totallen); - SET_VARSIZE(result, totallen); - ptr = (Pointer)result + totallen - vallen; + if (!value) + return NULL; - item = node->path; - while (item) - { - if (item->type == iKey) - { - ptr -= item->len + 2; - ptr[0] = 0; - memcpy(ptr + 1, item->s, item->len); - ptr[item->len + 1] = 0; - } - else if (item->type == iAnyArray) - { - ptr--; - *ptr = JSONB_VODKA_FLAG_ARRAY; - } - else - { - return -1; - } - item = item->parent; - } + result = (JsonbVodkaValue *)palloc(sizeof(JsonbVodkaValue)); - ptr = (Pointer)result + totallen - vallen; jqBase = value->jqBase; jqPos = value->jqPos; switch (value->type) { case jbvNull: - *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NULL; + result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NULL; break; case jbvBool: - *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_BOOL; + result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_BOOL; read_byte(len, jqBase, jqPos); if (len) - *ptr |= JSONB_VODKA_FLAG_TRUE; + result->type |= JSONB_VODKA_FLAG_TRUE; break; case jbvString: - *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; + result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; read_int32(len, jqBase, jqPos); - hash = hash_any((unsigned char *)jqBase + jqPos, len); - memcpy(ptr + 1, &hash, sizeof(hash)); + result->hash = hash_any((unsigned char *)jqBase + jqPos, len); break; case jbvNumeric: - *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; - write_numeric_key(ptr, numeric); + result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NUMERIC; + result->n = (Numeric)(jqBase + jqPos); break; default: elog(ERROR, "invalid jsonb scalar type"); } - - return add_entry(e, PointerGetDatum(result), NULL, ByteaEqualOperator, false); + return result; } +static int +make_entry_handler(ExtractedNode *node, Pointer extra) +{ + Entries *e = (Entries *)extra; + PathItem *item; + JsonbVodkaKey *key = (JsonbVodkaKey *)palloc(sizeof(JsonbVodkaKey)); + int length = 0, i; + + item = node->path; + while (item) + { + length++; + item = item->parent; + } + key->pathLength = length; + key->path = (PathItem *)palloc(sizeof(PathItem) * length); + + i = length - 1; + item = node->path; + while (item) + { + key->path[i] = *item; + i--; + item = item->parent; + } + + key->inequality = node->bounds.inequality; + key->leftInclusive = node->bounds.leftInclusive; + key->rightInclusive = node->bounds.rightInclusive; + if (key->inequality) + { + key->leftBound = make_vodka_value(node->bounds.leftBound); + key->rightBound = make_vodka_value(node->bounds.rightBound); + } + else + { + key->exact = make_vodka_value(node->bounds.exact); + } + + return add_entry(e, PointerGetDatum(key), NULL, VodkaMatchOperator, false); +} /* * extractValue support function @@ -474,15 +459,11 @@ vodkaqueryjsonbextract(PG_FUNCTION_ARGS) keys[i].extra = (Pointer)root; break; - - break; - default: elog(ERROR, "unrecognized strategy number: %d", strategy); break; } - /* ...although "contains {}" requires a full index scan */ if (*nentries == 0) *searchMode = VODKA_SEARCH_MODE_ALL; From 14ecaa2ebce52acd9d2e065a069ad59689bfc247 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Thu, 15 May 2014 21:25:36 +0400 Subject: [PATCH 3/7] Bug fixes and * support. --- jsonb_vodka_ops.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c index 9142512..0ec479b 100644 --- a/jsonb_vodka_ops.c +++ b/jsonb_vodka_ops.c @@ -264,7 +264,7 @@ make_vodka_value(JsQueryValue *value) int32 len, jqPos; char *jqBase; - if (!value) + if (!value || value->type == jqiAny) return NULL; result = (JsonbVodkaValue *)palloc(sizeof(JsonbVodkaValue)); @@ -274,21 +274,21 @@ make_vodka_value(JsQueryValue *value) switch (value->type) { - case jbvNull: + case jqiNull: result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NULL; break; - case jbvBool: + case jqiBool: result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_BOOL; read_byte(len, jqBase, jqPos); if (len) result->type |= JSONB_VODKA_FLAG_TRUE; break; - case jbvString: + case jqiString: result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; read_int32(len, jqBase, jqPos); result->hash = hash_any((unsigned char *)jqBase + jqPos, len); break; - case jbvNumeric: + case jqiNumeric: result->type = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NUMERIC; result->n = (Numeric)(jqBase + jqPos); break; From d87a1e138b0ac9718a1f8a86c08e9e18ccb4947b Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Mon, 19 May 2014 20:59:16 +0400 Subject: [PATCH 4/7] Fix = * for arrays and objects. --- jsonb_vodka_ops.c | 101 +++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 63 deletions(-) diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c index 0ec479b..59259ed 100644 --- a/jsonb_vodka_ops.c +++ b/jsonb_vodka_ops.c @@ -33,14 +33,15 @@ typedef struct #define JSONB_VODKA_FLAG_VALUE 0x01 -#define JSONB_VODKA_FLAG_NULL 0x00 -#define JSONB_VODKA_FLAG_STRING 0x02 -#define JSONB_VODKA_FLAG_NUMERIC 0x04 -#define JSONB_VODKA_FLAG_BOOL 0x06 -#define JSONB_VODKA_FLAG_TYPE 0x06 -#define JSONB_VODKA_FLAG_TRUE 0x08 -#define JSONB_VODKA_FLAG_NAN 0x08 -#define JSONB_VODKA_FLAG_NEGATIVE 0x10 +#define JSONB_VODKA_FLAG_NULL 0x00 +#define JSONB_VODKA_FLAG_STRING 0x02 +#define JSONB_VODKA_FLAG_NUMERIC 0x04 +#define JSONB_VODKA_FLAG_BOOL 0x06 +#define JSONB_VODKA_FLAG_EMPTY_ARRAY 0x08 +#define JSONB_VODKA_FLAG_EMPTY_OBJECT 0x0A +#define JSONB_VODKA_FLAG_TYPE 0x0E +#define JSONB_VODKA_FLAG_TRUE 0x10 +#define JSONB_VODKA_FLAG_NAN 0x10 #define JSONB_VODKA_FLAG_ARRAY 0x02 @@ -100,58 +101,6 @@ typedef struct PathStack struct PathStack *parent; } PathStack; -static int -get_ndigits(Numeric val) -{ - const NumericDigit *digits; - int ndigits; - - ndigits = NUMERIC_NDIGITS(val); - digits = NUMERIC_DIGITS(val); - - while (ndigits > 0 && *digits == 0) - { - ndigits--; - digits++; - } - return ndigits; -} - -static void -write_numeric_key(Pointer ptr, Numeric val) -{ - *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_NUMERIC; - if (NUMERIC_IS_NAN(val)) - { - *ptr |= JSONB_VODKA_FLAG_NAN; - } - else - { - const NumericDigit *digits = NUMERIC_DIGITS(val); - int ndigits = NUMERIC_NDIGITS(val); - int weight = NUMERIC_WEIGHT(val); - int sign = NUMERIC_SIGN(val); - - if (sign == NUMERIC_NEG) - *ptr |= JSONB_VODKA_FLAG_NEGATIVE; - ptr++; - - while (ndigits > 0 && *digits == 0) - { - ndigits--; - digits++; - } - - memcpy(ptr, &weight, sizeof(weight)); - ptr += sizeof(weight); - - memcpy(ptr, digits, sizeof(NumericDigit) * ndigits); - ptr += sizeof(NumericDigit) * ndigits; - - *ptr = 0; - } -} - static bytea * get_vodka_key(PathStack *stack, const JsonbValue *val) { @@ -179,6 +128,8 @@ get_vodka_key(PathStack *stack, const JsonbValue *val) { case jbvNull: case jbvBool: + case jbvObject: + case jbvArray: vallen = 1; break; case jbvString: @@ -226,6 +177,12 @@ get_vodka_key(PathStack *stack, const JsonbValue *val) if (val->val.boolean) *ptr |= JSONB_VODKA_FLAG_TRUE; break; + case jbvArray: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_EMPTY_ARRAY; + break; + case jbvObject: + *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_EMPTY_OBJECT; + break; case jbvString: *ptr = JSONB_VODKA_FLAG_VALUE | JSONB_VODKA_FLAG_STRING; hash = hash_any((unsigned char *)val->val.string.val, val->val.string.len); @@ -302,11 +259,19 @@ static int make_entry_handler(ExtractedNode *node, Pointer extra) { Entries *e = (Entries *)extra; - PathItem *item; + PathItem *item, *leaf = node->path; JsonbVodkaKey *key = (JsonbVodkaKey *)palloc(sizeof(JsonbVodkaKey)); int length = 0, i; - item = node->path; + if (!node->bounds.inequality && node->bounds.exact && node->bounds.exact->type == jqiAny) + { + item = (PathItem *)palloc(sizeof(PathItem)); + item->type = iAny; + item->parent = leaf; + leaf = item; + } + + item = leaf; while (item) { length++; @@ -316,7 +281,7 @@ make_entry_handler(ExtractedNode *node, Pointer extra) key->path = (PathItem *)palloc(sizeof(PathItem) * length); i = length - 1; - item = node->path; + item = leaf; while (item) { key->path[i] = *item; @@ -380,7 +345,17 @@ vodkajsonbextract(PG_FUNCTION_ARGS) switch (r) { case WJB_BEGIN_ARRAY: + if (v.val.array.nElems == 0) + entries[i++] = PointerGetDatum(get_vodka_key(stack, &v)); + tmp = stack; + stack = (PathStack *) palloc(sizeof(PathStack)); + stack->s = NULL; + stack->len = 0; + stack->parent = tmp; + break; case WJB_BEGIN_OBJECT: + if (v.val.object.nPairs == 0) + entries[i++] = PointerGetDatum(get_vodka_key(stack, &v)); tmp = stack; stack = (PathStack *) palloc(sizeof(PathStack)); stack->s = NULL; From 2aeacb9a5d38694ba63453e3a215226e082b6065 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Wed, 21 May 2014 17:49:26 +0400 Subject: [PATCH 5/7] Correct fallback if not positive quals for vodka opclass. --- jsonb_vodka_ops.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c index 59259ed..ef1aba1 100644 --- a/jsonb_vodka_ops.c +++ b/jsonb_vodka_ops.c @@ -427,11 +427,18 @@ vodkaqueryjsonbextract(PG_FUNCTION_ARGS) case JsQueryMatchStrategyNumber: jq = PG_GETARG_JSQUERY(0); root = extractJsQuery(jq, make_entry_handler, (Pointer)&e); - - *nentries = e.count; - keys = e.entries; - for (i = 0; i < e.count; i++) - keys[i].extra = (Pointer)root; + if (root) + { + *nentries = e.count; + keys = e.entries; + for (i = 0; i < e.count; i++) + keys[i].extra = (Pointer)root; + } + else + { + *nentries = 0; + keys = NULL; + } break; default: From 61dd5f6340bcabd9ff837c2a412b82e46949377c Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Wed, 21 May 2014 20:08:02 +0400 Subject: [PATCH 6/7] Use recheck only when it's needed. --- jsonb_vodka_ops.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c index ef1aba1..f4804bc 100644 --- a/jsonb_vodka_ops.c +++ b/jsonb_vodka_ops.c @@ -466,19 +466,18 @@ vodkajsonbconsistent(PG_FUNCTION_ARGS) int32 nkeys = PG_GETARG_INT32(3); bool *recheck = (bool *) PG_GETARG_POINTER(4); VodkaKey *queryKeys = (VodkaKey *) PG_GETARG_POINTER(5); + ExtractedNode *root; bool res; int32 i; switch (strategy) { case JsonbContainsStrategyNumber: - /* result is not lossy */ - *recheck = false; - /* must have all elements in check[] true, and no nulls */ + *recheck = true; res = true; for (i = 0; i < nkeys; i++) { - if (!check[i] || queryKeys[i].isnull) + if (!check[i]) { res = false; break; @@ -487,10 +486,12 @@ vodkajsonbconsistent(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: + root = (ExtractedNode *)queryKeys[0].extra; + *recheck = root->indirect; if (nkeys == 0) res = true; else - res = execRecursive((ExtractedNode *)queryKeys[0].extra, check); + res = execRecursive(root, check); break; default: @@ -516,6 +517,7 @@ vodkajsonbtriconsistent(PG_FUNCTION_ARGS) /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ VodkaKey *queryKeys = (VodkaKey *) PG_GETARG_POINTER(4); + ExtractedNode *root; VodkaTernaryValue res; int32 i; @@ -539,12 +541,13 @@ vodkajsonbtriconsistent(PG_FUNCTION_ARGS) break; case JsQueryMatchStrategyNumber: + root = (ExtractedNode *)queryKeys[0].extra; if (nkeys == 0) res = GIN_MAYBE; else - res = execRecursiveTristate((ExtractedNode *)queryKeys[0].extra, check); + res = execRecursiveTristate(root, check); - if (res == GIN_TRUE) + if (root->indirect && res == GIN_TRUE) res = GIN_MAYBE; break; From eaaf5da50525908e83aec5c43117d9db7dc7328a Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Fri, 30 May 2014 22:41:32 +0400 Subject: [PATCH 7/7] Code alignment fix. --- jsonb_vodka_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsonb_vodka_ops.c b/jsonb_vodka_ops.c index 437374f..5121057 100644 --- a/jsonb_vodka_ops.c +++ b/jsonb_vodka_ops.c @@ -31,7 +31,7 @@ typedef struct int count, total; } Entries; -#define JSONB_VODKA_FLAG_VALUE 0x01 +#define JSONB_VODKA_FLAG_VALUE 0x01 #define JSONB_VODKA_FLAG_NULL 0x00 #define JSONB_VODKA_FLAG_STRING 0x02 @@ -43,7 +43,7 @@ typedef struct #define JSONB_VODKA_FLAG_TRUE 0x10 #define JSONB_VODKA_FLAG_NAN 0x10 -#define JSONB_VODKA_FLAG_ARRAY 0x02 +#define JSONB_VODKA_FLAG_ARRAY 0x02 PG_FUNCTION_INFO_V1(vodkajsonbconfig); PG_FUNCTION_INFO_V1(vodkajsonbextract); 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