From: Peter Geoghegan Date: Thu, 13 Mar 2014 01:10:12 +0000 (-0700) Subject: Introduce "superheader" abstraction X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=ccc6cbea9393e1419211e58bf171f101330def2c;p=users%2Fandresfreund%2Fpostgres.git Introduce "superheader" abstraction This makes it clearer how the nested structure can be interacted with through either a varlena Jsonb buffer pointer, or a (nested) pointer to a Jentry (which has a header...so we might be accessing that through what is nominally a superheader pointer, and it doesn't matter). This makes the structure of much of jsonb_support.c a lot clearer. --- diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c index b0b1e3b70d..728f4c8557 100644 --- a/src/backend/utils/adt/jsonb_op.c +++ b/src/backend/utils/adt/jsonb_op.c @@ -27,11 +27,11 @@ jsonb_exists(PG_FUNCTION_ARGS) JsonbValue *v = NULL; if (!JB_ISEMPTY(jb)) - v = findUncompressedJsonbValue(VARDATA(jb), - JB_FLAG_OBJECT | JB_FLAG_ARRAY, - NULL, - VARDATA_ANY(key), - VARSIZE_ANY_EXHDR(key)); + v = findJsonbValueFromSuperHeaderLen(VARDATA(jb), + JB_FLAG_OBJECT | JB_FLAG_ARRAY, + NULL, + VARDATA_ANY(key), + VARSIZE_ANY_EXHDR(key)); PG_RETURN_BOOL(v != NULL); } @@ -60,10 +60,10 @@ jsonb_exists_any(PG_FUNCTION_ARGS) */ for (i = 0; i < v->array.nElems; i++) { - if (findUncompressedJsonbValueByValue(VARDATA(jb), - JB_FLAG_OBJECT | JB_FLAG_ARRAY, - plowbound, - v->array.elems + i) != NULL) + if (findJsonbValueFromSuperHeader(VARDATA(jb), + JB_FLAG_OBJECT | JB_FLAG_ARRAY, + plowbound, + v->array.elems + i) != NULL) PG_RETURN_BOOL(true); } @@ -99,10 +99,10 @@ jsonb_exists_all(PG_FUNCTION_ARGS) */ for (i = 0; i < v->array.nElems; i++) { - if (findUncompressedJsonbValueByValue(VARDATA(js), - JB_FLAG_OBJECT | JB_FLAG_ARRAY, - plowbound, - v->array.elems + i) == NULL) + if (findJsonbValueFromSuperHeader(VARDATA(js), + JB_FLAG_OBJECT | JB_FLAG_ARRAY, + plowbound, + v->array.elems + i) == NULL) PG_RETURN_BOOL(false); } @@ -235,7 +235,7 @@ jsonb_cmp(PG_FUNCTION_ARGS) /* * TODO: Think harder about the memory leaked here */ - res = compareJsonbBinaryValue(VARDATA(jb1), VARDATA(jb2)); + res = compareJsonbSuperHeaderValue(VARDATA(jb1), VARDATA(jb2)); } /* @@ -348,9 +348,10 @@ deepContains(JsonbIterator ** it1, JsonbIterator ** it2) Assert(r2 == WJB_KEY); - v = findUncompressedJsonbValueByValue((*it1)->buffer, - JB_FLAG_OBJECT, - &lowbound, &v2); + v = findJsonbValueFromSuperHeader((*it1)->buffer, + JB_FLAG_OBJECT, + &lowbound, + &v2); if (v == NULL) { @@ -406,9 +407,10 @@ deepContains(JsonbIterator ** it1, JsonbIterator ** it2) if (v2.type >= jbvNull && v2.type < jbvArray) { - v = findUncompressedJsonbValueByValue((*it1)->buffer, - JB_FLAG_ARRAY, NULL, - &v2); + v = findJsonbValueFromSuperHeader((*it1)->buffer, + JB_FLAG_ARRAY, + NULL, + &v2); if (v == NULL) { res = false; diff --git a/src/backend/utils/adt/jsonb_support.c b/src/backend/utils/adt/jsonb_support.c index 256c3823cb..37d972783d 100644 --- a/src/backend/utils/adt/jsonb_support.c +++ b/src/backend/utils/adt/jsonb_support.c @@ -27,19 +27,24 @@ #define JENTRY_ISTRUE (0x10000000 | 0x20000000 | 0x40000000) /* Note possible multiple evaluations, also access to prior array element */ -#define JBE_ISFIRST(he_) (((he_).entry & JENTRY_ISFIRST) != 0) -#define JBE_ISSTRING(he_) (((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISSTRING) -#define JBE_ISNUMERIC(he_) (((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC) -#define JBE_ISNEST(he_) (((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNEST) -#define JBE_ISNULL(he_) (((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISNULL) -#define JBE_ISBOOL(he_) (((he_).entry & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL) -#define JBE_ISBOOL_TRUE(he_) (((he_).entry & JENTRY_TYPEMASK) == JENTRY_ISTRUE) +#define JBE_ISFIRST(he_) (((he_).header & JENTRY_ISFIRST) != 0) +#define JBE_ISSTRING(he_) (((he_).header & JENTRY_TYPEMASK) == JENTRY_ISSTRING) +#define JBE_ISNUMERIC(he_) (((he_).header & JENTRY_TYPEMASK) == JENTRY_ISNUMERIC) +#define JBE_ISNEST(he_) (((he_).header & JENTRY_TYPEMASK) == JENTRY_ISNEST) +#define JBE_ISNULL(he_) (((he_).header & JENTRY_TYPEMASK) == JENTRY_ISNULL) +#define JBE_ISBOOL(he_) (((he_).header & JENTRY_TYPEMASK & JENTRY_ISBOOL) == JENTRY_ISBOOL) +#define JBE_ISBOOL_TRUE(he_) (((he_).header & JENTRY_TYPEMASK) == JENTRY_ISTRUE) #define JBE_ISBOOL_FALSE(he_) (JBE_ISBOOL(he_) && !JBE_ISBOOL_TRUE(he_)) +/* + * State used while rolling up an arbitrary JsonbValue into a varlena/Jsonb + * value + */ typedef struct CompressState { - char *begin; - char *ptr; + /* Preallocated buffer in which to form varlena/Jsonb value */ + char *buffer; + JsonbSuperHeader ptr; struct { @@ -54,6 +59,7 @@ typedef struct CompressState } CompressState; static int lexicalCompareJsonbStringValue(const void *a, const void *b); +static uint32 compressJsonb(JsonbValue * v, JsonbSuperHeader sheader); static void walkUncompressedJsonb(JsonbValue * value, CompressState * state, uint32 nestlevel); static void parseBuffer(JsonbIterator * it, char *buffer); @@ -64,7 +70,6 @@ static void compressJsonbValue(CompressState * state, JsonbValue * value, uint32 flags, uint32 level); static void putJEntryString(CompressState * state, JsonbValue * value, uint32 level, uint32 i); -static uint32 compressJsonb(JsonbValue * v, char *buffer); static ToJsonbState *pushState(ToJsonbState ** state); static void appendKey(ToJsonbState * state, JsonbValue * v); static void appendValue(ToJsonbState * state, JsonbValue * v); @@ -86,8 +91,7 @@ JsonbValueToJsonb(JsonbValue * v) { out = NULL; } - else if (v->type == jbvString || v->type == jbvBool || - v->type == jbvNumeric || v->type == jbvNull) + else if (v->type >= jbvNull && v->type < jbvArray) { /* Scalar value */ ToJsonbState *state = NULL; @@ -119,9 +123,9 @@ JsonbValueToJsonb(JsonbValue * v) } else { - out = palloc(VARHDRSZ + v->binary.len); - Assert(v->type == jbvBinary); + + out = palloc(VARHDRSZ + v->binary.len); SET_VARSIZE(out, VARHDRSZ + v->binary.len); memcpy(VARDATA(out), v->binary.data, v->binary.len); } @@ -186,7 +190,7 @@ compareJsonbValue(JsonbValue * a, JsonbValue * b) break; case jbvBinary: /* This wastes a few cycles on unneeded lexical comparisons */ - return compareJsonbBinaryValue(a->binary.data, b->binary.data) == 0; + return compareJsonbSuperHeaderValue(a->binary.data, b->binary.data) == 0; default: elog(ERROR, "invalid jsonb scalar type"); } @@ -203,7 +207,7 @@ compareJsonbValue(JsonbValue * a, JsonbValue * b) * This is called from B-Tree support function 1. */ int -compareJsonbBinaryValue(char *a, char *b) +compareJsonbSuperHeaderValue(JsonbSuperHeader a, JsonbSuperHeader b) { JsonbIterator *it1, *it2; @@ -273,11 +277,11 @@ compareJsonbBinaryValue(char *a, char *b) } /* - * findUncompressedJsonbValueByValue() wrapper that sets up JsonbValue key. + * findJsonbValueFromSuperHeader() wrapper that sets up JsonbValue key. */ JsonbValue * -findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound, - char *key, uint32 keylen) +findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, uint32 flags, uint32 *lowbound, + char *key, uint32 keylen) { JsonbValue v; @@ -292,29 +296,29 @@ findUncompressedJsonbValue(char *buffer, uint32 flags, uint32 *lowbound, v.string.len = keylen; } - return findUncompressedJsonbValueByValue(buffer, flags, lowbound, &v); + return findJsonbValueFromSuperHeader(sheader, flags, lowbound, &v); } /* - * Find string key in object or element by value in array (packed format) + * Find string key in object or element by value in array */ JsonbValue * -findUncompressedJsonbValueByValue(char *buffer, uint32 flags, - uint32 *lowbound, JsonbValue * key) +findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags, + uint32 *lowbound, JsonbValue * key) { - uint32 header = *(uint32 *) buffer; + uint32 superheader = *(uint32 *) sheader; + JEntry *array = (JEntry *) (sheader + sizeof(uint32)); static JsonbValue r; - Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != + Assert((superheader & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != (JB_FLAG_ARRAY | JB_FLAG_OBJECT)); - if (flags & JB_FLAG_ARRAY & header) + if (flags & JB_FLAG_ARRAY & superheader) { - JEntry *array = (JEntry *) (buffer + sizeof(header)); - char *data = (char *) (array + (header & JB_COUNT_MASK)); + char *data = (char *) (array + (superheader & JB_COUNT_MASK)); int i; - for (i = (lowbound) ? *lowbound : 0; i < (header & JB_COUNT_MASK); i++) + for (i = (lowbound) ? *lowbound : 0; i < (superheader & JB_COUNT_MASK); i++) { JEntry *e = array + i; @@ -376,12 +380,11 @@ findUncompressedJsonbValueByValue(char *buffer, uint32 flags, } } } - else if (flags & JB_FLAG_OBJECT & header) + else if (flags & JB_FLAG_OBJECT & superheader) { - JEntry *array = (JEntry *) (buffer + sizeof(header)); - char *data = (char *) (array + (header & JB_COUNT_MASK) * 2); + char *data = (char *) (array + (superheader & JB_COUNT_MASK) * 2); uint32 stopLow = lowbound ? *lowbound : 0, - stopHigh = (header & JB_COUNT_MASK), + stopHigh = (superheader & JB_COUNT_MASK), stopMiddle; /* Object keys must be strings */ @@ -472,33 +475,34 @@ findUncompressedJsonbValueByValue(char *buffer, uint32 flags, * Note: returns pointer to statically allocated JsonbValue. */ JsonbValue * -getJsonbValue(char *buffer, uint32 flags, int32 i) +getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, uint32 flags, + int32 i) { - uint32 header = *(uint32 *) buffer; + uint32 superheader = *(uint32 *) sheader; static JsonbValue r; JEntry *array, *e; char *data; - Assert((header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != + Assert((superheader & (JB_FLAG_ARRAY | JB_FLAG_OBJECT)) != (JB_FLAG_ARRAY | JB_FLAG_OBJECT)); Assert(i >= 0); - if (i >= (header & JB_COUNT_MASK)) + if (i >= (superheader & JB_COUNT_MASK)) return NULL; - array = (JEntry *) (buffer + sizeof(header)); + array = (JEntry *) (sheader + sizeof(uint32)); - if (flags & JB_FLAG_ARRAY & header) + if (flags & JB_FLAG_ARRAY & superheader) { e = array + i; - data = (char *) (array + (header & JB_COUNT_MASK)); + data = (char *) (array + (superheader & JB_COUNT_MASK)); } - else if (flags & JB_FLAG_OBJECT & header) + else if (flags & JB_FLAG_OBJECT & superheader) { e = array + i * 2 + 1; - data = (char *) (array + (header & JB_COUNT_MASK) * 2); + data = (char *) (array + (superheader & JB_COUNT_MASK) * 2); } else { @@ -639,14 +643,15 @@ pushJsonbValue(ToJsonbState ** state, int r, JsonbValue * v) } /* - * Given an unparsed varlena buffer, expand to JsonbIterator. + * Given a varlena buffer, expand to JsonbIterator to iterate over items fully + * expanded to in-memory representation for manipulation. */ JsonbIterator * -JsonbIteratorInit(char *buffer) +JsonbIteratorInit(JsonbSuperHeader sheader) { - JsonbIterator *it = palloc(sizeof(*it)); + JsonbIterator *it = palloc(sizeof(JsonbIterator)); - parseBuffer(it, buffer); + parseBuffer(it, sheader); it->next = NULL; return it; @@ -805,6 +810,30 @@ lexicalCompareJsonbStringValue(const void *a, const void *b) vb->string.len, DEFAULT_COLLATION_OID); } +/* + * puts JsonbValue tree into a preallocated buffer + */ +static uint32 +compressJsonb(JsonbValue * v, char *buffer) +{ + uint32 l = 0; + CompressState state; + + /* Should not already have binary representation */ + Assert(v->type != jbvBinary); + + state.buffer = state.ptr = buffer; + state.maxlevel = 8; + state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel); + + walkUncompressedJsonb(v, &state, 0); + + l = state.ptr - buffer; + Assert(l <= v->size); + + return l; +} + /* * Walk the tree representation of jsonb */ @@ -814,6 +843,8 @@ walkUncompressedJsonb(JsonbValue * value, CompressState * state, { int i; + check_stack_depth(); + if (!value) return; @@ -862,19 +893,19 @@ walkUncompressedJsonb(JsonbValue * value, CompressState * state, */ /* - * Initialize iterator from buffer + * Initialize iterator from superheader */ static void -parseBuffer(JsonbIterator * it, char *buffer) +parseBuffer(JsonbIterator * it, JsonbSuperHeader sheader) { - uint32 header = *(uint32 *) buffer; + uint32 superheader = *(uint32 *) sheader; - it->containerType = header & (JB_FLAG_ARRAY | JB_FLAG_OBJECT); - it->nElems = header & JB_COUNT_MASK; - it->buffer = buffer; + it->containerType = superheader & (JB_FLAG_ARRAY | JB_FLAG_OBJECT); + it->nElems = superheader & JB_COUNT_MASK; + it->buffer = sheader; /* Array starts just after header */ - it->metaArray = (JEntry *) (buffer + sizeof(uint32)); + it->metaArray = (JEntry *) (sheader + sizeof(uint32)); it->state = jbi_start; switch (it->containerType) @@ -882,7 +913,7 @@ parseBuffer(JsonbIterator * it, char *buffer) case JB_FLAG_ARRAY: it->dataProper = (char *) it->metaArray + it->nElems * sizeof(JEntry); - it->isScalar = (header & JB_FLAG_SCALAR) != 0; + it->isScalar = (superheader & JB_FLAG_SCALAR) != 0; /* This is either a "raw scalar", or an array */ Assert(!it->isScalar || it->nElems == 1); break; @@ -995,8 +1026,8 @@ compressJsonbValue(CompressState * state, JsonbValue * value, uint32 flags, curLevelState->begin = state->ptr; - padlen = INTALIGN(state->ptr - state->begin) - (state->ptr - - state->begin); + padlen = INTALIGN(state->ptr - state->buffer) - (state->ptr - + state->buffer); /* * Add padding as necessary */ @@ -1064,22 +1095,22 @@ compressJsonbValue(CompressState * state, JsonbValue * value, uint32 flags, { i = prevLevelState->i; - prevLevelState->array[i].entry = JENTRY_ISNEST; + prevLevelState->array[i].header = JENTRY_ISNEST; if (i == 0) - prevLevelState->array[0].entry |= JENTRY_ISFIRST | len; + prevLevelState->array[0].header |= JENTRY_ISFIRST | len; else - prevLevelState->array[i].entry |= - (prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len; + prevLevelState->array[i].header |= + (prevLevelState->array[i - 1].header & JENTRY_POSMASK) + len; } else if (*prevLevelState->header & JB_FLAG_OBJECT) { i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */ - prevLevelState->array[i].entry = JENTRY_ISNEST; + prevLevelState->array[i].header = JENTRY_ISNEST; - prevLevelState->array[i].entry |= - (prevLevelState->array[i - 1].entry & JENTRY_POSMASK) + len; + prevLevelState->array[i].header |= + (prevLevelState->array[i - 1].header & JENTRY_POSMASK) + len; } else { @@ -1096,51 +1127,52 @@ compressJsonbValue(CompressState * state, JsonbValue * value, uint32 flags, } static void -putJEntryString(CompressState * state, JsonbValue * value, uint32 level, uint32 i) +putJEntryString(CompressState * state, JsonbValue * value, uint32 level, + uint32 i) { short p, padlen; curLevelState = state->levelstate + level; if (i == 0) - curLevelState->array[0].entry = JENTRY_ISFIRST; + curLevelState->array[0].header = JENTRY_ISFIRST; else - curLevelState->array[i].entry = 0; + curLevelState->array[i].header = 0; switch (value->type) { case jbvNull: - curLevelState->array[i].entry |= JENTRY_ISNULL; + curLevelState->array[i].header |= JENTRY_ISNULL; if (i > 0) - curLevelState->array[i].entry |= - curLevelState->array[i - 1].entry & JENTRY_POSMASK; + curLevelState->array[i].header |= + curLevelState->array[i - 1].header & JENTRY_POSMASK; break; case jbvString: memcpy(state->ptr, value->string.val, value->string.len); state->ptr += value->string.len; if (i == 0) - curLevelState->array[i].entry |= value->string.len; + curLevelState->array[i].header |= value->string.len; else - curLevelState->array[i].entry |= - (curLevelState->array[i - 1].entry & JENTRY_POSMASK) + + curLevelState->array[i].header |= + (curLevelState->array[i - 1].header & JENTRY_POSMASK) + value->string.len; break; case jbvBool: - curLevelState->array[i].entry |= (value->boolean) ? + curLevelState->array[i].header |= (value->boolean) ? JENTRY_ISTRUE : JENTRY_ISFALSE; if (i > 0) - curLevelState->array[i].entry |= - curLevelState->array[i - 1].entry & JENTRY_POSMASK; + curLevelState->array[i].header |= + curLevelState->array[i - 1].header & JENTRY_POSMASK; break; case jbvNumeric: { int numlen = VARSIZE_ANY(value->numeric); - padlen = INTALIGN(state->ptr - state->begin) - - (state->ptr - state->begin); + padlen = INTALIGN(state->ptr - state->buffer) - + (state->ptr - state->buffer); /* * Add padding as necessary @@ -1154,19 +1186,19 @@ putJEntryString(CompressState * state, JsonbValue * value, uint32 level, uint32 memcpy(state->ptr, value->numeric, numlen); state->ptr += numlen; - curLevelState->array[i].entry |= JENTRY_ISNUMERIC; + curLevelState->array[i].header |= JENTRY_ISNUMERIC; if (i == 0) - curLevelState->array[i].entry |= padlen + numlen; + curLevelState->array[i].header |= padlen + numlen; else - curLevelState->array[i].entry |= - (curLevelState->array[i - 1].entry & JENTRY_POSMASK) + + curLevelState->array[i].header |= + (curLevelState->array[i - 1].header & JENTRY_POSMASK) + padlen + numlen; break; } case jbvBinary: { - padlen = INTALIGN(state->ptr - state->begin) - (state->ptr - - state->begin); + padlen = INTALIGN(state->ptr - state->buffer) - (state->ptr - + state->buffer); /* * Add padding as necessary */ @@ -1179,13 +1211,13 @@ putJEntryString(CompressState * state, JsonbValue * value, uint32 level, uint32 memcpy(state->ptr, value->binary.data, value->binary.len); state->ptr += value->binary.len; - curLevelState->array[i].entry |= JENTRY_ISNEST; + curLevelState->array[i].header |= JENTRY_ISNEST; if (i == 0) - curLevelState->array[i].entry |= value->binary.len + padlen; + curLevelState->array[i].header |= value->binary.len + padlen; else - curLevelState->array[i].entry |= - (curLevelState->array[i - 1].entry & JENTRY_POSMASK) + + curLevelState->array[i].header |= + (curLevelState->array[i - 1].header & JENTRY_POSMASK) + value->binary.len + padlen; } break; @@ -1194,34 +1226,13 @@ putJEntryString(CompressState * state, JsonbValue * value, uint32 level, uint32 } } -/* - * puts JsonbValue tree into preallocated buffer - */ -static uint32 -compressJsonb(JsonbValue * v, char *buffer) -{ - uint32 l = 0; - CompressState state; - - state.begin = state.ptr = buffer; - state.maxlevel = 8; - state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel); - - walkUncompressedJsonb(v, &state, 0); - - l = state.ptr - buffer; - Assert(l <= v->size); - - return l; -} - /* * Iteration-like forming jsonb */ static ToJsonbState * pushState(ToJsonbState ** state) { - ToJsonbState *ns = palloc(sizeof(*ns)); + ToJsonbState *ns = palloc(sizeof(ToJsonbState)); ns->next = *state; return ns; diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index ac38ad2c63..1b23f63a13 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -1189,9 +1189,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) { if (have_object) { - jbvp = findUncompressedJsonbValue((char *) jbvp, JB_FLAG_OBJECT, NULL, - VARDATA_ANY(pathtext[i]), - VARSIZE_ANY_EXHDR(pathtext[i])); + jbvp = findJsonbValueFromSuperHeaderLen((JsonbSuperHeader) jbvp, + JB_FLAG_OBJECT, NULL, + VARDATA_ANY(pathtext[i]), + VARSIZE_ANY_EXHDR(pathtext[i])); } else if (have_array) { @@ -1204,7 +1205,8 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) if (*endptr != '\0' || lindex > INT_MAX || lindex < 0) PG_RETURN_NULL(); index = (uint32) lindex; - jbvp = getJsonbValue((char *) jbvp, JB_FLAG_ARRAY, index); + jbvp = getIthJsonbValueFromSuperHeader((JsonbSuperHeader) jbvp, + JB_FLAG_ARRAY, index); } else { @@ -2223,7 +2225,9 @@ populate_record_worker(FunctionCallInfo fcinfo, bool have_record_arg) { char *key = NameStr(tupdesc->attrs[i]->attname); - v = findUncompressedJsonbValue(VARDATA(jb), JB_FLAG_OBJECT, NULL, key, strlen(key)); + v = findJsonbValueFromSuperHeaderLen(VARDATA(jb), + JB_FLAG_OBJECT, NULL, key, + strlen(key)); } } @@ -2516,7 +2520,9 @@ make_row_from_rec_and_jsonb(Jsonb * element, PopulateRecordsetState *state) { char *key = NameStr(tupdesc->attrs[i]->attname); - v = findUncompressedJsonbValue(VARDATA(element), JB_FLAG_OBJECT, NULL, key, strlen(key)); + v = findJsonbValueFromSuperHeaderLen(VARDATA(element), + JB_FLAG_OBJECT, NULL, key, + strlen(key)); } /* diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index 06bf3af200..9c0d175a35 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -49,7 +49,7 @@ #define WJB_END_OBJECT (0x040) /* Macros give offset */ -#define JBE_ENDPOS(he_) ((he_).entry & JENTRY_POSMASK) +#define JBE_ENDPOS(he_) ((he_).header & JENTRY_POSMASK) #define JBE_OFF(he_) (JBE_ISFIRST(he_) ? 0 : JBE_ENDPOS((&(he_))[-1])) #define JBE_LEN(he_) (JBE_ISFIRST(he_) \ ? JBE_ENDPOS(he_) \ @@ -82,6 +82,32 @@ typedef struct JsonbPair JsonbPair; typedef struct JsonbValue JsonbValue; +typedef char* JsonbSuperHeader; + +/* + * We have an abstraction called a "superheader". This is a pointer that + * conventionally points to the first item after our 4-byte uncompressed + * varlena header, from which we can read uint32 values through bitwise + * operations. + * + * Sometimes we pass a superheader reference to a function, and it doesn't + * matter if it points to just after the start of a Jsonb, or to a Jentry. + * Either way, the type punning works and the superheader/header metadata is + * used to operate on an underlying JsonbValue. + * + * In a few contexts, when passing a superheader, there actually is an + * assumption that it really does point to just past vl_len_ in a Jsonb. We + * assert that it's "superheader sane" in those contexts. In general, this is + * expected to work just fine, as care has been taken to make the nested layout + * consistent to the extent that it matters between the least nested level + * (Jsonb), and deeper nested levels (Jentry). + */ +typedef struct +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + /* (uint32/Jsonb superheader of top-level Jsonb object/array follows) */ + /* (array of JEntry follows, size determined using uint32 header) */ +} Jsonb; /* * JEntry: there is one of these for each key _and_ value in a jsonb object @@ -92,16 +118,9 @@ typedef struct JsonbValue JsonbValue; */ typedef struct { - uint32 entry; + uint32 header; /* May be accessed as superheader */ } JEntry; -typedef struct -{ - int32 vl_len_; /* varlena header (do not touch directly!) */ - /* header of jsonb object or array */ - /* array of JEntry follows */ -} Jsonb; - struct JsonbValue { enum @@ -244,15 +263,21 @@ extern Datum gin_extract_jsonb_query_hash(PG_FUNCTION_ARGS); extern Datum gin_consistent_jsonb_hash(PG_FUNCTION_ARGS); /* Support functions */ -extern int compareJsonbBinaryValue(char *a, char *b); +extern int compareJsonbSuperHeaderValue(JsonbSuperHeader a, + JsonbSuperHeader b); extern bool compareJsonbValue(JsonbValue *a, JsonbValue *b); -extern JsonbValue *findUncompressedJsonbValueByValue(char *buffer, uint32 flags, - uint32 *lowbound, JsonbValue *key); -extern JsonbValue *findUncompressedJsonbValue(char *buffer, uint32 flags, - uint32 *lowbound, char *key, uint32 keylen); -extern JsonbValue *getJsonbValue(char *buffer, uint32 flags, int32 i); +extern JsonbValue *findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, + uint32 flags, + uint32 *lowbound, + char *key, uint32 keylen); +extern JsonbValue *findJsonbValueFromSuperHeader(JsonbSuperHeader sheader, + uint32 flags, + uint32 *lowbound, + JsonbValue *key); +extern JsonbValue *getIthJsonbValueFromSuperHeader(JsonbSuperHeader sheader, + uint32 flags, int32 i); extern JsonbValue *pushJsonbValue(ToJsonbState ** state, int r, JsonbValue *v); -extern JsonbIterator *JsonbIteratorInit(char *buffer); +extern JsonbIterator *JsonbIteratorInit(JsonbSuperHeader buffer); extern int JsonbIteratorNext(JsonbIterator **it, JsonbValue *v, bool skipNested); extern Jsonb *JsonbValueToJsonb(JsonbValue *v); extern JsonbValue *arrayToJsonbSortedArray(ArrayType *a);