static bool checkArrayEquality(JsQueryItem *jsq, JsonbValue *jb) { int32 r; JsonbIterator *it; JsonbValue v; JsQueryItem elem; if (!(jsq->type == jqiArray && JsonbType(jb) == jbvArray)) return false; it = JsonbIteratorInit(jb->val.binary.data); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_BEGIN_ARRAY); if (v.val.array.nElems != jsq->array.nelems) return false; while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r != WJB_ELEM) continue; jsqIterateArray(jsq, &elem); if (checkScalarEquality(&elem, &v) == false) return false; } return true; }
static bool recursiveAll(JsQueryItem *jsq, JsonbValue *jb) { bool res = true; JsonbIterator *it; int32 r; JsonbValue v; check_stack_depth(); it = JsonbIteratorInit(jb->val.binary.data); while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_KEY) { r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_VALUE); } if (r == WJB_VALUE || r == WJB_ELEM) { if ((res = recursiveExecute(jsq, &v, NULL)) == true) { if (v.type == jbvBinary) res = recursiveAll(jsq, &v); } if (res == false) break; } } return res; }
static bool recursiveAny(char *jqBase, int32 jqPos, JsonbValue *jb) { bool res = false; JsonbIterator *it; int32 r; JsonbValue v; check_stack_depth(); it = JsonbIteratorInit(jb->val.binary.data); while(res == false && (r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_KEY) { r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_VALUE); } if (r == WJB_VALUE || r == WJB_ELEM) { res = recursiveExecute(jqBase, jqPos, &v); if (res == false && v.type == jbvBinary) res = recursiveAny(jqBase, jqPos, &v); } } return res; }
Datum jsonb_contains(PG_FUNCTION_ARGS) { Jsonb *val = PG_GETARG_JSONB(0); Jsonb *tmpl = PG_GETARG_JSONB(1); JsonbIterator *it1, *it2; if (JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl)) PG_RETURN_BOOL(false); it1 = JsonbIteratorInit(&val->root); it2 = JsonbIteratorInit(&tmpl->root); PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2)); }
/* * jsonb_concat: * Concatenation of two jsonb. There are few allowed combinations: * - concatenation of two objects * - concatenation of two arrays * - concatenation of object and array * * The result for first two is new object and array accordingly. * The last one return new array, which contains all elements from * original array, and one extra element (which is actually * other argument of this function with type jbvObject) at the first or last position. */ Datum jsonb_concat(PG_FUNCTION_ARGS) { Jsonb *jb1 = PG_GETARG_JSONB(0); Jsonb *jb2 = PG_GETARG_JSONB(1); Jsonb *out = palloc(VARSIZE(jb1) + VARSIZE(jb2)); JsonbParseState *state = NULL; JsonbValue *res; JsonbIterator *it1, *it2; /* * If one of the jsonb is empty, * just return other. */ if (JB_ROOT_COUNT(jb1) == 0) { memcpy(out, jb2, VARSIZE(jb2)); PG_RETURN_POINTER(out); } else if (JB_ROOT_COUNT(jb2) == 0) { memcpy(out, jb1, VARSIZE(jb1)); PG_RETURN_POINTER(out); } it1 = JsonbIteratorInit(&jb1->root); it2 = JsonbIteratorInit(&jb2->root); res = IteratorConcat(&it1, &it2, &state); if (res == NULL || (res->type == jbvArray && res->val.array.nElems == 0) || (res->type == jbvObject && res->val.object.nPairs == 0) ) { SET_VARSIZE(out, VARHDRSZ); } else { if (res->type == jbvArray && res->val.array.nElems > 1) res->val.array.rawScalar = false; out = JsonbValueToJsonb(res); } PG_RETURN_JSONB(out); }
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 gin_extract_jsonb(PG_FUNCTION_ARGS) { Jsonb *jb = (Jsonb *) PG_GETARG_JSONB_P(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); int total = 2 * JB_ROOT_COUNT(jb); JsonbIterator *it; JsonbValue v; JsonbIteratorToken r; int i = 0; Datum *entries; /* If the root level is empty, we certainly have no keys */ if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } /* Otherwise, use 2 * root count as initial estimate of result size */ entries = (Datum *) palloc(sizeof(Datum) * total); it = JsonbIteratorInit(&jb->root); while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { /* Since we recurse into the object, we might need more space */ if (i >= total) { total *= 2; entries = (Datum *) repalloc(entries, sizeof(Datum) * total); } switch (r) { case WJB_KEY: entries[i++] = make_scalar_key(&v, true); break; case WJB_ELEM: /* Pretend string array elements are keys, see jsonb.h */ entries[i++] = make_scalar_key(&v, (v.type == jbvString)); break; case WJB_VALUE: entries[i++] = make_scalar_key(&v, false); break; default: /* we can ignore structural items */ break; } } *nentries = i; PG_RETURN_POINTER(entries); }
/* * jsonb_delete: * Return copy of jsonb with the specified item removed. * Item is a one key or element from jsonb, specified by name. * If there are many keys or elements with than name, * the first one will be removed. */ Datum jsonb_delete(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); text *key = PG_GETARG_TEXT_PP(1); char *keyptr = VARDATA_ANY(key); int keylen = VARSIZE_ANY_EXHDR(key); JsonbParseState *state = NULL; JsonbIterator *it; uint32 r; JsonbValue v, *res = NULL; bool skipped = false; if (JB_ROOT_IS_SCALAR(in)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot delete from scalar"))); if (JB_ROOT_COUNT(in) == 0) { PG_RETURN_JSONB(in); } it = JsonbIteratorInit(&in->root); while((r = JsonbIteratorNext(&it, &v, false)) != 0) { if (!skipped && (r == WJB_ELEM || r == WJB_KEY) && (v.type == jbvString && keylen == v.val.string.len && memcmp(keyptr, v.val.string.val, keylen) == 0)) { /* we should delete only one key/element */ skipped = true; if (r == WJB_KEY) { /* skip corresponding value */ JsonbIteratorNext(&it, &v, true); } continue; } res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); } Assert(res != NULL); PG_RETURN_JSONB(JsonbValueToJsonb(res)); }
static bool executeArrayOp(char *jqBase, int32 jqPos, int32 type, int32 op, JsonbValue *jb) { int32 i, nelems, *arrayPos; int32 r; JsonbIterator *it; JsonbValue v; int32 nres = 0, nval = 0; if (jb->type != jbvBinary) return false; if (type != jqiArray) return false; read_int32(nelems, jqBase, jqPos); arrayPos = (int32*)(jqBase + jqPos); it = JsonbIteratorInit(jb->val.binary.data); while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_BEGIN_ARRAY) nval = v.val.array.nElems; if (r == WJB_ELEM) { bool res = false; for(i=0; i<nelems; i++) { if (executeExpr(jqBase, arrayPos[i], jqiEqual, &v)) { if (op == jqiOverlap) return true; nres++; res = true; break; } } } } if (op == jqiContains) return (nres == nelems && nelems > 0); if (op == jqiContained) return (nres == nval && nval > 0); return false; }
Datum gin_extract_jsonb(PG_FUNCTION_ARGS) { Jsonb *jb = (Jsonb *) PG_GETARG_JSONB_P(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); int total = JB_ROOT_COUNT(jb); JsonbIterator *it; JsonbValue v; JsonbIteratorToken r; GinEntries entries; /* If the root level is empty, we certainly have no keys */ if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } /* Otherwise, use 2 * root count as initial estimate of result size */ init_gin_entries(&entries, 2 * total); it = JsonbIteratorInit(&jb->root); while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (r) { case WJB_KEY: add_gin_entry(&entries, make_scalar_key(&v, true)); break; case WJB_ELEM: /* Pretend string array elements are keys, see jsonb.h */ add_gin_entry(&entries, make_scalar_key(&v, v.type == jbvString)); break; case WJB_VALUE: add_gin_entry(&entries, make_scalar_key(&v, false)); break; default: /* we can ignore structural items */ break; } } *nentries = entries.count; PG_RETURN_POINTER(entries.buf); }
/* * jsonb_set: * Replace/create value of jsonb key or jsonb element, which can be found by the specified path. * Path must be replesented as an array of key names or indexes. If indexes will be used, * the same rules implied as for jsonb_delete_idx (negative indexing and edge cases) */ Datum jsonb_set(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); Jsonb *newval = PG_GETARG_JSONB(2); bool create = PG_GETARG_BOOL(3); JsonbValue *res = NULL; Datum *path_elems; bool *path_nulls; int path_len; JsonbIterator *it; JsonbParseState *st = NULL; if (ARR_NDIM(path) > 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("wrong number of array subscripts"))); if (JB_ROOT_IS_SCALAR(in)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot set path in scalar"))); if (JB_ROOT_COUNT(in) == 0 && !create) { PG_RETURN_JSONB(in); } deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); if (path_len == 0) { PG_RETURN_JSONB(in); } it = JsonbIteratorInit(&in->root); res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newval, create); Assert (res != NULL); PG_RETURN_JSONB(JsonbValueToJsonb(res)); }
/* * SQL function jsonb_typeof(jsonb) -> text * * This function is here because the analog json function is in json.c, since * it uses the json parser internals not exposed elsewhere. */ Datum jsonb_typeof(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); JsonbIterator *it; JsonbValue v; char *result; if (JB_ROOT_IS_OBJECT(in)) result = "object"; else if (JB_ROOT_IS_ARRAY(in) && !JB_ROOT_IS_SCALAR(in)) result = "array"; else { Assert(JB_ROOT_IS_SCALAR(in)); it = JsonbIteratorInit(VARDATA_ANY(in)); /* * A root scalar is stored as an array of one element, so we get the * array and then its first (and only) member. */ (void) JsonbIteratorNext(&it, &v, true); Assert(v.type == jbvArray); (void) JsonbIteratorNext(&it, &v, true); switch (v.type) { case jbvNull: result = "null"; break; case jbvString: result = "string"; break; case jbvNumeric: result = "number"; break; case jbvBool: result = "boolean"; break; default: elog(ERROR, "unknown jsonb scalar type"); } } PG_RETURN_TEXT_P(cstring_to_text(result)); }
Datum jsonb_hash_extended(PG_FUNCTION_ARGS) { Jsonb *jb = PG_GETARG_JSONB(0); uint64 seed = PG_GETARG_INT64(1); JsonbIterator *it; JsonbValue v; JsonbIteratorToken r; uint64 hash = 0; if (JB_ROOT_COUNT(jb) == 0) PG_RETURN_UINT64(seed); it = JsonbIteratorInit(&jb->root); while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (r) { /* Rotation is left to JsonbHashScalarValueExtended() */ case WJB_BEGIN_ARRAY: hash ^= ((uint64) JB_FARRAY) << 32 | JB_FARRAY; break; case WJB_BEGIN_OBJECT: hash ^= ((uint64) JB_FOBJECT) << 32 | JB_FOBJECT; break; case WJB_KEY: case WJB_VALUE: case WJB_ELEM: JsonbHashScalarValueExtended(&v, &hash, seed); break; case WJB_END_ARRAY: case WJB_END_OBJECT: break; default: elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r); } } PG_FREE_IF_COPY(jb, 0); PG_RETURN_UINT64(hash); }
/* * 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); }
static bool checkArrayEquality(char *jqBase, int32 jqPos, int32 type, JsonbValue *jb) { int32 i, nelems, *arrayPos; int32 r; JsonbIterator *it; JsonbValue v; if (!(type == jqiArray && jb->type == jbvBinary)) return false; read_int32(nelems, jqBase, jqPos); arrayPos = (int32*)(jqBase + jqPos); it = JsonbIteratorInit(jb->val.binary.data); r = JsonbIteratorNext(&it, &v, true); if (r != WJB_BEGIN_ARRAY) return false; if (v.val.array.nElems != nelems) return false; i = 0; while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_ELEM && i<nelems) { if (executeExpr(jqBase, arrayPos[i], jqiEqual, &v) == false) return false; i++; } } return true; }
Datum gin_extract_jsonb_path(PG_FUNCTION_ARGS) { Jsonb *jb = PG_GETARG_JSONB_P(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); int total = 2 * JB_ROOT_COUNT(jb); JsonbIterator *it; JsonbValue v; JsonbIteratorToken r; PathHashStack tail; PathHashStack *stack; int i = 0; Datum *entries; /* If the root level is empty, we certainly have no keys */ if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } /* Otherwise, use 2 * root count as initial estimate of result size */ entries = (Datum *) palloc(sizeof(Datum) * total); /* We keep a stack of partial hashes corresponding to parent key levels */ tail.parent = NULL; tail.hash = 0; stack = &tail; it = JsonbIteratorInit(&jb->root); while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { PathHashStack *parent; /* Since we recurse into the object, we might need more space */ if (i >= total) { total *= 2; entries = (Datum *) repalloc(entries, sizeof(Datum) * total); } switch (r) { case WJB_BEGIN_ARRAY: case WJB_BEGIN_OBJECT: /* Push a stack level for this object */ parent = stack; stack = (PathHashStack *) palloc(sizeof(PathHashStack)); /* * We pass forward hashes from outer nesting levels so that * the hashes for nested values will include outer keys as * well as their own keys. * * Nesting an array within another array will not alter * innermost scalar element hash values, but that seems * inconsequential. */ stack->hash = parent->hash; stack->parent = parent; break; case WJB_KEY: /* mix this key into the current outer hash */ JsonbHashScalarValue(&v, &stack->hash); /* hash is now ready to incorporate the value */ break; case WJB_ELEM: case WJB_VALUE: /* mix the element or value's hash into the prepared hash */ JsonbHashScalarValue(&v, &stack->hash); /* and emit an index entry */ entries[i++] = UInt32GetDatum(stack->hash); /* reset hash for next key, value, or sub-object */ stack->hash = stack->parent->hash; break; case WJB_END_ARRAY: case WJB_END_OBJECT: /* Pop the stack */ parent = stack->parent; pfree(stack); stack = parent; /* reset hash for next key, value, or sub-object */ if (stack->parent) stack->hash = stack->parent->hash; else stack->hash = 0; break; default: elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r); } } *nentries = i; PG_RETURN_POINTER(entries); }
/* * Turn a Datum into jsonb, adding it to the result JsonbInState. * * tcategory and outfuncoid are from a previous call to json_categorize_type, * except that if is_null is true then they can be invalid. * * If key_scalar is true, the value is stored as a key, so insist * it's of an acceptable type, and force it to be a jbvString. */ static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result, JsonbTypeCategory tcategory, Oid outfuncoid, bool key_scalar) { char *outputstr; bool numeric_error; JsonbValue jb; bool scalar_jsonb = false; check_stack_depth(); /* Convert val to a JsonbValue in jb (in most cases) */ if (is_null) { Assert(!key_scalar); jb.type = jbvNull; } else if (key_scalar && (tcategory == JSONBTYPE_ARRAY || tcategory == JSONBTYPE_COMPOSITE || tcategory == JSONBTYPE_JSON || tcategory == JSONBTYPE_JSONB || tcategory == JSONBTYPE_JSONCAST)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("key value must be scalar, not array, composite, or json"))); } else { if (tcategory == JSONBTYPE_JSONCAST) val = OidFunctionCall1(outfuncoid, val); switch (tcategory) { case JSONBTYPE_ARRAY: array_to_jsonb_internal(val, result); break; case JSONBTYPE_COMPOSITE: composite_to_jsonb(val, result); break; case JSONBTYPE_BOOL: if (key_scalar) { outputstr = DatumGetBool(val) ? "true" : "false"; jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } else { jb.type = jbvBool; jb.val.boolean = DatumGetBool(val); } break; case JSONBTYPE_NUMERIC: outputstr = OidOutputFunctionCall(outfuncoid, val); if (key_scalar) { /* always quote keys */ jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } else { /* * Make it numeric if it's a valid JSON number, otherwise * a string. Invalid numeric output will always have an * 'N' or 'n' in it (I think). */ numeric_error = (strchr(outputstr, 'N') != NULL || strchr(outputstr, 'n') != NULL); if (!numeric_error) { jb.type = jbvNumeric; jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1)); pfree(outputstr); } else { jb.type = jbvString; jb.val.string.len = strlen(outputstr); jb.val.string.val = outputstr; } } break; case JSONBTYPE_DATE: { DateADT date; struct pg_tm tm; char buf[MAXDATELEN + 1]; date = DatumGetDateADT(val); /* Same as date_out(), but forcing DateStyle */ if (DATE_NOT_FINITE(date)) EncodeSpecialDate(date, buf); else { j2date(date + POSTGRES_EPOCH_JDATE, &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday)); EncodeDateOnly(&tm, USE_XSD_DATES, buf); } jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_TIMESTAMP: { Timestamp timestamp; struct pg_tm tm; fsec_t fsec; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestamp(val); /* Same as timestamp_out(), but forcing DateStyle */ if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0) EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_TIMESTAMPTZ: { TimestampTz timestamp; struct pg_tm tm; int tz; fsec_t fsec; const char *tzn = NULL; char buf[MAXDATELEN + 1]; timestamp = DatumGetTimestampTz(val); /* Same as timestamptz_out(), but forcing DateStyle */ if (TIMESTAMP_NOT_FINITE(timestamp)) EncodeSpecialTimestamp(timestamp, buf); else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0) EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf); else ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); jb.type = jbvString; jb.val.string.len = strlen(buf); jb.val.string.val = pstrdup(buf); } break; case JSONBTYPE_JSONCAST: case JSONBTYPE_JSON: { /* parse the json right into the existing result object */ JsonLexContext *lex; JsonSemAction sem; text *json = DatumGetTextP(val); lex = makeJsonLexContext(json, true); memset(&sem, 0, sizeof(sem)); sem.semstate = (void *) result; sem.object_start = jsonb_in_object_start; sem.array_start = jsonb_in_array_start; sem.object_end = jsonb_in_object_end; sem.array_end = jsonb_in_array_end; sem.scalar = jsonb_in_scalar; sem.object_field_start = jsonb_in_object_field_start; pg_parse_json(lex, &sem); } break; case JSONBTYPE_JSONB: { Jsonb *jsonb = DatumGetJsonb(val); JsonbIterator *it; it = JsonbIteratorInit(&jsonb->root); if (JB_ROOT_IS_SCALAR(jsonb)) { (void) JsonbIteratorNext(&it, &jb, true); Assert(jb.type == jbvArray); (void) JsonbIteratorNext(&it, &jb, true); scalar_jsonb = true; } else { JsonbIteratorToken type; while ((type = JsonbIteratorNext(&it, &jb, false)) != WJB_DONE) { if (type == WJB_END_ARRAY || type == WJB_END_OBJECT || type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT) result->res = pushJsonbValue(&result->parseState, type, NULL); else result->res = pushJsonbValue(&result->parseState, type, &jb); } } } break; default: outputstr = OidOutputFunctionCall(outfuncoid, val); jb.type = jbvString; jb.val.string.len = checkStringLen(strlen(outputstr)); jb.val.string.val = outputstr; break; } } /* Now insert jb into result, unless we did it recursively */ if (!is_null && !scalar_jsonb && tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST) { /* work has been done recursively */ return; } else if (result->parseState == NULL) { /* single root scalar */ JsonbValue va; va.type = jbvArray; va.val.array.rawScalar = true; va.val.array.nElems = 1; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va); result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb); result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL); } else { JsonbValue *o = &result->parseState->contVal; switch (o->type) { case jbvArray: result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb); break; case jbvObject: result->res = pushJsonbValue(&result->parseState, key_scalar ? WJB_KEY : WJB_VALUE, &jb); break; default: elog(ERROR, "unexpected parent of nested structure"); } } }
Datum gin_extract_jsonb_hash(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; PathHashStack tail; PathHashStack *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(VARDATA(jb)); tail.parent = NULL; tail.hash = 0; stack = &tail; while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { PathHashStack *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 = (PathHashStack *) palloc(sizeof(PathHashStack)); /* * Nesting an array within another array will not alter * innermost scalar element hash values, but that seems * inconsequential */ if (tmp->parent) { /* * We pass forward hashes from previous container nesting * levels so that nested arrays with an outermost nested * object will have element hashes mixed with the outermost * key. It's also somewhat useful to have nested objects * innermost values have hashes that are a function of not * just their own key, but outer keys too. */ stack->hash = tmp->hash; } else { /* * At least nested level, initialize with stable container * type proxy value */ stack->hash = (r == WJB_BEGIN_ARRAY)? JB_FARRAY:JB_FOBJECT; } stack->parent = tmp; break; case WJB_KEY: /* Initialize hash from parent */ stack->hash = stack->parent->hash; JsonbHashScalarValue(&v, &stack->hash); break; case WJB_ELEM: /* Elements have parent hash mixed in separately */ stack->hash = stack->parent->hash; case WJB_VALUE: /* Element/value case */ JsonbHashScalarValue(&v, &stack->hash); entries[i++] = stack->hash; 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); }
Datum gin_extract_jsonb(PG_FUNCTION_ARGS) { Jsonb *jb = (Jsonb *) PG_GETARG_JSONB(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; int total = 2 * JB_ROOT_COUNT(jb); int i = 0, r; JsonbIterator *it; JsonbValue v; if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } entries = (Datum *) palloc(sizeof(Datum) * total); it = JsonbIteratorInit(VARDATA(jb)); while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { if (i >= total) { total *= 2; entries = (Datum *) repalloc(entries, sizeof(Datum) * total); } /* * Serialize keys and elements equivalently, but only when elements * are Jsonb strings. Otherwise, serialize elements as values. Array * elements are indexed as keys, for the benefit of * JsonbExistsStrategyNumber. Our definition of existence does not * allow for checking the existence of a non-jbvString element (just * like the definition of the underlying operator), because the * operator takes a text rhs argument (which is taken as a proxy for an * equivalent Jsonb string). * * The way existence is represented does not preclude an alternative * existence operator, that takes as its rhs value an arbitrarily * internally-typed Jsonb. The only reason that isn't the case here is * that the existence operator is only really intended to determine if * an object has a certain key (object pair keys are of course * invariably strings), which is extended to jsonb arrays. You could * think of the default Jsonb definition of existence as being * equivalent to a definition where all types of scalar array elements * are keys that we can check the existence of, while just forbidding * non-string notation. This inflexibility prevents the user from * having to qualify that the rhs string is a raw scalar string (that * is, naturally no internal string quoting in required for the text * argument), and allows us to not set the reset flag for * JsonbExistsStrategyNumber, since we know that keys are strings for * both objects and arrays, and don't have to further account for type * mismatch. Not having to set the reset flag makes it less than * tempting to tighten up the definition of existence to preclude array * elements entirely, which would arguably be a simpler alternative. * In any case the infrastructure used to implement the existence * operator could trivially support this hypothetical, slightly * distinct definition of existence. */ switch (r) { case WJB_KEY: /* Serialize key separately, for existence strategies */ entries[i++] = PointerGetDatum(make_scalar_key(&v, JKEYELEM)); break; case WJB_ELEM: if (v.type == jbvString) entries[i++] = PointerGetDatum(make_scalar_key(&v, JKEYELEM)); else entries[i++] = PointerGetDatum(make_scalar_key(&v, JVAL)); break; case WJB_VALUE: entries[i++] = PointerGetDatum(make_scalar_key(&v, JVAL)); break; default: continue; } } *nentries = i; PG_RETURN_POINTER(entries); }
/* * JsonbToCString * Converts jsonb value to a C-string. * * If 'out' argument is non-null, the resulting C-string is stored inside the * StringBuffer. The resulting string is always returned. * * A typical case for passing the StringInfo in rather than NULL is where the * caller wants access to the len attribute without having to call strlen, e.g. * if they are converting it to a text* object. */ char * JsonbToCString(StringInfo out, JsonbSuperHeader in, int estimated_len) { bool first = true; JsonbIterator *it; int type = 0; JsonbValue v; int level = 0; bool redo_switch = false; if (out == NULL) out = makeStringInfo(); enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64); it = JsonbIteratorInit(in); while (redo_switch || ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)) { redo_switch = false; switch (type) { case WJB_BEGIN_ARRAY: if (!first) appendBinaryStringInfo(out, ", ", 2); first = true; if (!v.val.array.rawScalar) appendStringInfoChar(out, '['); level++; break; case WJB_BEGIN_OBJECT: if (!first) appendBinaryStringInfo(out, ", ", 2); first = true; appendStringInfoCharMacro(out, '{'); level++; break; case WJB_KEY: if (!first) appendBinaryStringInfo(out, ", ", 2); first = true; /* json rules guarantee this is a string */ jsonb_put_escaped_value(out, &v); appendBinaryStringInfo(out, ": ", 2); type = JsonbIteratorNext(&it, &v, false); if (type == WJB_VALUE) { first = false; jsonb_put_escaped_value(out, &v); } else { Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY); /* * We need to rerun the current switch() since we need to * output the object which we just got from the iterator * before calling the iterator again. */ redo_switch = true; } break; case WJB_ELEM: if (!first) appendBinaryStringInfo(out, ", ", 2); else first = false; jsonb_put_escaped_value(out, &v); break; case WJB_END_ARRAY: level--; if (!v.val.array.rawScalar) appendStringInfoChar(out, ']'); first = false; break; case WJB_END_OBJECT: level--; appendStringInfoCharMacro(out, '}'); first = false; break; default: elog(ERROR, "unknown flag of jsonb iterator"); } } Assert(level == 0); return out->data; }
Datum gin_extract_jsonb_path(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; JsonbIteratorToken r; PathHashStack tail; PathHashStack *stack; int i = 0; Datum *entries; /* If the root level is empty, we certainly have no keys */ if (total == 0) { *nentries = 0; PG_RETURN_POINTER(NULL); } /* Otherwise, use 2 * root count as initial estimate of result size */ entries = (Datum *) palloc(sizeof(Datum) * total); /* We keep a stack of partial hashes corresponding to parent key levels */ tail.parent = NULL; tail.hash = 0; stack = &tail; it = JsonbIteratorInit(&jb->root); while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { PathHashStack *parent; /* Since we recurse into the object, we might need more space */ if (i >= total) { total *= 2; entries = (Datum *) repalloc(entries, sizeof(Datum) * total); } switch (r) { case WJB_BEGIN_ARRAY: case WJB_BEGIN_OBJECT: /* Push a stack level for this object */ parent = stack; stack = (PathHashStack *) palloc(sizeof(PathHashStack)); if (parent->parent) { /* * We pass forward hashes from previous container nesting * levels so that nested arrays with an outermost nested * object will have element hashes mixed with the * outermost key. It's also somewhat useful to have * nested objects' innermost values have hashes that are a * function of not just their own key, but outer keys too. * * Nesting an array within another array will not alter * innermost scalar element hash values, but that seems * inconsequential. */ stack->hash = parent->hash; } else { /* * At the outermost level, initialize hash with container * type proxy value. Note that this makes JB_FARRAY and * JB_FOBJECT part of the on-disk representation, but they * are that in the base jsonb object storage already. */ stack->hash = (r == WJB_BEGIN_ARRAY) ? JB_FARRAY : JB_FOBJECT; } stack->parent = parent; break; case WJB_KEY: /* initialize hash from parent */ stack->hash = stack->parent->hash; /* and mix in this key */ JsonbHashScalarValue(&v, &stack->hash); /* hash is now ready to incorporate the value */ break; case WJB_ELEM: /* array elements use parent hash mixed with element's hash */ stack->hash = stack->parent->hash; /* FALL THRU */ case WJB_VALUE: /* mix the element or value's hash into the prepared hash */ JsonbHashScalarValue(&v, &stack->hash); /* and emit an index entry */ entries[i++] = UInt32GetDatum(stack->hash); /* Note: we assume we'll see KEY before another VALUE */ break; case WJB_END_ARRAY: case WJB_END_OBJECT: /* Pop the stack */ parent = stack->parent; pfree(stack); stack = parent; break; default: elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r); } } *nentries = i; PG_RETURN_POINTER(entries); }
/* * jsonb_delete_idx: * Return a copy of jsonb withour specified items. * Delete key (only from the top level of object) or element from jsonb by index (idx). * Negative idx value is supported, and it implies the countdown from the last key/element. * If idx is more, than numbers of keys/elements, or equal - nothing will be deleted. * If idx is negative and -idx is more, than number of keys/elements - the last one will be deleted. * * TODO: take care about nesting values. */ Datum jsonb_delete_idx(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); int idx = PG_GETARG_INT32(1); JsonbParseState *state = NULL; JsonbIterator *it; uint32 r, i = 0, n; JsonbValue v, *res = NULL; if (JB_ROOT_IS_SCALAR(in)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot delete from scalar"))); if (JB_ROOT_COUNT(in) == 0) { PG_RETURN_JSONB(in); } it = JsonbIteratorInit(&in->root); r = JsonbIteratorNext(&it, &v, false); if (r == WJB_BEGIN_ARRAY) n = v.val.array.nElems; else n = v.val.object.nPairs; if (idx < 0) { if (-idx > n) idx = n; else idx = n + idx; } if (idx >= n) { PG_RETURN_JSONB(in); } pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); while((r = JsonbIteratorNext(&it, &v, true)) != 0) { if (r == WJB_ELEM || r == WJB_KEY) { if (i++ == idx) { if (r == WJB_KEY) JsonbIteratorNext(&it, &v, true); /* skip value */ continue; } } res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); } Assert (res != NULL); PG_RETURN_JSONB(JsonbValueToJsonb(res)); }
static bool recursiveExecute(char *jqBase, int32 jqPos, JsonbValue *jb) { int32 type; int32 nextPos; int32 left, right, arg; bool res = false; check_stack_depth(); jqPos = readJsQueryHeader(jqBase, jqPos, &type, &nextPos); switch(type) { case jqiAnd: read_int32(left, jqBase, jqPos); read_int32(right, jqBase, jqPos); Assert(nextPos == 0); res = (recursiveExecute(jqBase, left, jb) && recursiveExecute(jqBase, right, jb)); break; case jqiOr: read_int32(left, jqBase, jqPos); read_int32(right, jqBase, jqPos); Assert(nextPos == 0); res = (recursiveExecute(jqBase, left, jb) || recursiveExecute(jqBase, right, jb)); break; case jqiNot: read_int32(arg, jqBase, jqPos); Assert(nextPos == 0); res = ! recursiveExecute(jqBase, arg, jb); break; case jqiKey: if (jb->type == jbvBinary) { int32 len; JsonbValue *v, key; read_int32(len, jqBase, jqPos); key.type = jbvString; key.val.string.val = jqBase + jqPos; key.val.string.len = len; jqPos += len + 1; v = findJsonbValueFromSuperHeader(jb->val.binary.data, JB_FOBJECT, NULL, &key); Assert(nextPos != 0); res = ((v != NULL) && recursiveExecute(jqBase, nextPos, v)); } break; case jqiAny: Assert(nextPos != 0); if (recursiveExecute(jqBase, nextPos, jb)) res = true; else if (jb->type == jbvBinary) res = recursiveAny(jqBase, nextPos, jb); break; case jqiAnyArray: Assert(nextPos != 0); if (jb->type == jbvBinary) { JsonbIterator *it; int32 r; JsonbValue v; it = JsonbIteratorInit(jb->val.binary.data); while(res == false && (r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_KEY) break; if (r == WJB_ELEM) res = recursiveExecute(jqBase, nextPos, &v); } } break; case jqiEqual: case jqiIn: case jqiLess: case jqiGreater: case jqiLessOrEqual: case jqiGreaterOrEqual: case jqiContains: case jqiContained: case jqiOverlap: read_int32(arg, jqBase, jqPos); res = executeExpr(jqBase, arg, type, jb); break; default: elog(ERROR,"Wrong state: %d", type); } return res; }
static bool executeExpr(JsQueryItem *jsq, int32 op, JsonbValue *jb, JsQueryItem *jsqLeftArg) { bool res = false; /* * read arg type */ Assert(jsqGetNext(jsq, NULL) == false); Assert(jsq->type == jqiAny || jsq->type == jqiString || jsq->type == jqiNumeric || jsq->type == jqiNull || jsq->type == jqiBool || jsq->type == jqiArray); if (jsqLeftArg && jsqLeftArg->type == jqiLength) { if (JsonbType(jb) == jbvArray || JsonbType(jb) == jbvObject) { int32 length; JsonbIterator *it; JsonbValue v; int r; it = JsonbIteratorInit(jb->val.binary.data); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT); length = (r == WJB_BEGIN_ARRAY) ? v.val.array.nElems : v.val.object.nPairs; v.type = jbvNumeric; v.val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(length))); switch(op) { case jqiEqual: case jqiLess: case jqiGreater: case jqiLessOrEqual: case jqiGreaterOrEqual: res = makeCompare(jsq, op, &v); break; case jqiIn: res = checkScalarIn(jsq, &v); break; case jqiOverlap: case jqiContains: case jqiContained: break; default: elog(ERROR, "Unknown operation"); } } } else { switch(op) { case jqiEqual: if (JsonbType(jb) == jbvArray && jsq->type == jqiArray) res = checkArrayEquality(jsq, jb); else res = checkScalarEquality(jsq, jb); break; case jqiIn: res = checkScalarIn(jsq, jb); break; case jqiOverlap: case jqiContains: case jqiContained: res = executeArrayOp(jsq, op, jb); break; case jqiLess: case jqiGreater: case jqiLessOrEqual: case jqiGreaterOrEqual: res = makeCompare(jsq, op, jb); break; default: elog(ERROR, "Unknown operation"); } } return res; }
static bool recursiveExecute(JsQueryItem *jsq, JsonbValue *jb, JsQueryItem *jsqLeftArg) { JsQueryItem elem; bool res = false; check_stack_depth(); switch(jsq->type) { case jqiAnd: jsqGetLeftArg(jsq, &elem); res = recursiveExecute(&elem, jb, jsqLeftArg); if (res == true) { jsqGetRightArg(jsq, &elem); res = recursiveExecute(&elem, jb, jsqLeftArg); } break; case jqiOr: jsqGetLeftArg(jsq, &elem); res = recursiveExecute(&elem, jb, jsqLeftArg); if (res == false) { jsqGetRightArg(jsq, &elem); res = recursiveExecute(&elem, jb, jsqLeftArg); } break; case jqiNot: jsqGetArg(jsq, &elem); res = !recursiveExecute(&elem, jb, jsqLeftArg); break; case jqiKey: if (JsonbType(jb) == jbvObject) { JsonbValue *v, key; key.type = jbvString; key.val.string.val = jsqGetString(jsq, &key.val.string.len); v = findJsonbValueFromContainer(jb->val.binary.data, JB_FOBJECT, &key); if (v != NULL) { jsqGetNext(jsq, &elem); res = recursiveExecute(&elem, v, NULL); pfree(v); } } break; case jqiCurrent: jsqGetNext(jsq, &elem); if (JsonbType(jb) == jbvScalar) { JsonbIterator *it; int32 r; JsonbValue v; it = JsonbIteratorInit(jb->val.binary.data); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_BEGIN_ARRAY); Assert(v.val.array.rawScalar == 1); Assert(v.val.array.nElems == 1); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_ELEM); res = recursiveExecute(&elem, &v, jsqLeftArg); } else { res = recursiveExecute(&elem, jb, jsqLeftArg); } break; case jqiAny: jsqGetNext(jsq, &elem); if (recursiveExecute(&elem, jb, NULL)) res = true; else if (jb->type == jbvBinary) res = recursiveAny(&elem, jb); break; case jqiAll: jsqGetNext(jsq, &elem); if ((res = recursiveExecute(&elem, jb, NULL)) == true) { if (jb->type == jbvBinary) res = recursiveAll(&elem, jb); } break; case jqiAnyArray: case jqiAllArray: if (JsonbType(jb) == jbvArray) { JsonbIterator *it; int32 r; JsonbValue v; jsqGetNext(jsq, &elem); it = JsonbIteratorInit(jb->val.binary.data); if (jsq->type == jqiAllArray) res = true; while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_ELEM) { res = recursiveExecute(&elem, &v, NULL); if (jsq->type == jqiAnyArray) { if (res == true) break; } else if (jsq->type == jqiAllArray) { if (res == false) break; } } } } break; case jqiAnyKey: case jqiAllKey: if (JsonbType(jb) == jbvObject) { JsonbIterator *it; int32 r; JsonbValue v; jsqGetNext(jsq, &elem); it = JsonbIteratorInit(jb->val.binary.data); if (jsq->type == jqiAllKey) res = true; while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_VALUE) { res = recursiveExecute(&elem, &v, NULL); if (jsq->type == jqiAnyKey) { if (res == true) break; } else if (jsq->type == jqiAllKey) { if (res == false) break; } } } } break; case jqiEqual: case jqiIn: case jqiLess: case jqiGreater: case jqiLessOrEqual: case jqiGreaterOrEqual: case jqiContains: case jqiContained: case jqiOverlap: jsqGetArg(jsq, &elem); res = executeExpr(&elem, jsq->type, jb, jsqLeftArg); break; case jqiLength: jsqGetNext(jsq, &elem); res = recursiveExecute(&elem, jb, jsq); break; case jqiIs: if (JsonbType(jb) == jbvScalar) { JsonbIterator *it; int32 r; JsonbValue v; it = JsonbIteratorInit(jb->val.binary.data); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_BEGIN_ARRAY); Assert(v.val.array.rawScalar == 1); Assert(v.val.array.nElems == 1); r = JsonbIteratorNext(&it, &v, true); Assert(r == WJB_ELEM); res = (jsqGetIsType(jsq) == JsonbType(&v)); } else { res = (jsqGetIsType(jsq) == JsonbType(jb)); } break; default: elog(ERROR,"Wrong state: %d", jsq->type); } return res; }
static bool executeArrayOp(JsQueryItem *jsq, int32 op, JsonbValue *jb) { int32 r; JsonbIterator *it; JsonbValue v; JsQueryItem elem; if (JsonbType(jb) != jbvArray) return false; if (jsq->type != jqiArray) return false; if (op == jqiContains) { while(jsqIterateArray(jsq, &elem)) { bool res = false; it = JsonbIteratorInit(jb->val.binary.data); while(res == false && (r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_ELEM && checkScalarEquality(&elem, &v)) res = true; } if (res == false) return false; } } else { it = JsonbIteratorInit(jb->val.binary.data); while((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_ELEM) { bool res = false; jsqIterateInit(jsq); while(jsqIterateArray(jsq, &elem)) { if (checkScalarEquality(&elem, &v)) { if (op == jqiOverlap) return true; res = true; break; } } if (op == jqiContained && res == false) return false; } } if (op == jqiOverlap) return false; } return true; }
/* * PLyObject_FromJsonb * * Transform JsonbContainer to PyObject. */ static PyObject * PLyObject_FromJsonbContainer(JsonbContainer *jsonb) { JsonbIteratorToken r; JsonbValue v; JsonbIterator *it; PyObject *result; it = JsonbIteratorInit(jsonb); r = JsonbIteratorNext(&it, &v, true); switch (r) { case WJB_BEGIN_ARRAY: if (v.val.array.rawScalar) { JsonbValue tmp; if ((r = JsonbIteratorNext(&it, &v, true)) != WJB_ELEM || (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_END_ARRAY || (r = JsonbIteratorNext(&it, &tmp, true)) != WJB_DONE) elog(ERROR, "unexpected jsonb token: %d", r); result = PLyObject_FromJsonbValue(&v); } else { /* array in v */ result = PyList_New(0); if (!result) return NULL; while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_ELEM) { PyObject *elem = PLyObject_FromJsonbValue(&v); PyList_Append(result, elem); Py_XDECREF(elem); } } } break; case WJB_BEGIN_OBJECT: result = PyDict_New(); if (!result) return NULL; while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) { if (r == WJB_KEY) { PyObject *key = PLyString_FromJsonbValue(&v); if (!key) return NULL; r = JsonbIteratorNext(&it, &v, true); if (r == WJB_VALUE) { PyObject *value = PLyObject_FromJsonbValue(&v); if (!value) { Py_XDECREF(key); return NULL; } PyDict_SetItem(result, key, value); Py_XDECREF(value); } Py_XDECREF(key); } } break; default: elog(ERROR, "unexpected jsonb token: %d", r); return NULL; } return result; }
/* * jsonb_agg aggregate function */ Datum jsonb_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext oldcontext, aggcontext; JsonbAggState *state; JsonbInState elem; Datum val; JsonbInState *result; bool single_scalar = false; JsonbIterator *it; Jsonb *jbelem; JsonbValue v; JsonbIteratorToken type; if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "jsonb_agg_transfn called in non-aggregate context"); } /* set up the accumulator on the first go round */ if (PG_ARGISNULL(0)) { Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); if (arg_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); oldcontext = MemoryContextSwitchTo(aggcontext); state = palloc(sizeof(JsonbAggState)); result = palloc0(sizeof(JsonbInState)); state->res = result; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL); MemoryContextSwitchTo(oldcontext); jsonb_categorize_type(arg_type, &state->val_category, &state->val_output_func); } else { state = (JsonbAggState *) PG_GETARG_POINTER(0); result = state->res; } /* turn the argument into jsonb in the normal function context */ val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1); memset(&elem, 0, sizeof(JsonbInState)); datum_to_jsonb(val, PG_ARGISNULL(1), &elem, state->val_category, state->val_output_func, false); jbelem = JsonbValueToJsonb(elem.res); /* switch to the aggregate context for accumulation operations */ oldcontext = MemoryContextSwitchTo(aggcontext); it = JsonbIteratorInit(&jbelem->root); while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (type) { case WJB_BEGIN_ARRAY: if (v.val.array.rawScalar) single_scalar = true; else result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_END_ARRAY: if (!single_scalar) result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_BEGIN_OBJECT: case WJB_END_OBJECT: result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_ELEM: case WJB_KEY: case WJB_VALUE: if (v.type == jbvString) { /* copy string values in the aggregate context */ char *buf = palloc(v.val.string.len + 1); snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val); v.val.string.val = buf; } else if (v.type == jbvNumeric) { /* same for numeric */ v.val.numeric = DatumGetNumeric(DirectFunctionCall1(numeric_uplus, NumericGetDatum(v.val.numeric))); } result->res = pushJsonbValue(&result->parseState, type, &v); break; default: elog(ERROR, "unknown jsonb iterator token type"); } } MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(state); }
/* * jsonb_object_agg aggregate function */ Datum jsonb_object_agg_transfn(PG_FUNCTION_ARGS) { MemoryContext oldcontext, aggcontext; JsonbInState elem; JsonbAggState *state; Datum val; JsonbInState *result; bool single_scalar; JsonbIterator *it; Jsonb *jbkey, *jbval; JsonbValue v; JsonbIteratorToken type; if (!AggCheckCallContext(fcinfo, &aggcontext)) { /* cannot be called directly because of internal-type argument */ elog(ERROR, "jsonb_object_agg_transfn called in non-aggregate context"); } /* set up the accumulator on the first go round */ if (PG_ARGISNULL(0)) { Oid arg_type; oldcontext = MemoryContextSwitchTo(aggcontext); state = palloc(sizeof(JsonbAggState)); result = palloc0(sizeof(JsonbInState)); state->res = result; result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL); MemoryContextSwitchTo(oldcontext); arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); if (arg_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); jsonb_categorize_type(arg_type, &state->key_category, &state->key_output_func); arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2); if (arg_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); jsonb_categorize_type(arg_type, &state->val_category, &state->val_output_func); } else { state = (JsonbAggState *) PG_GETARG_POINTER(0); result = state->res; } /* turn the argument into jsonb in the normal function context */ if (PG_ARGISNULL(1)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("field name must not be null"))); val = PG_GETARG_DATUM(1); memset(&elem, 0, sizeof(JsonbInState)); datum_to_jsonb(val, false, &elem, state->key_category, state->key_output_func, true); jbkey = JsonbValueToJsonb(elem.res); val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2); memset(&elem, 0, sizeof(JsonbInState)); datum_to_jsonb(val, PG_ARGISNULL(2), &elem, state->val_category, state->val_output_func, false); jbval = JsonbValueToJsonb(elem.res); it = JsonbIteratorInit(&jbkey->root); /* switch to the aggregate context for accumulation operations */ oldcontext = MemoryContextSwitchTo(aggcontext); /* * keys should be scalar, and we should have already checked for that * above when calling datum_to_jsonb, so we only need to look for these * things. */ while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (type) { case WJB_BEGIN_ARRAY: if (!v.val.array.rawScalar) elog(ERROR, "unexpected structure for key"); break; case WJB_ELEM: if (v.type == jbvString) { /* copy string values in the aggregate context */ char *buf = palloc(v.val.string.len + 1); snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val); v.val.string.val = buf; } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("object keys must be strings"))); } result->res = pushJsonbValue(&result->parseState, WJB_KEY, &v); break; case WJB_END_ARRAY: break; default: elog(ERROR, "unexpected structure for key"); break; } } it = JsonbIteratorInit(&jbval->root); single_scalar = false; /* * values can be anything, including structured and null, so we treat them * as in json_agg_transfn, except that single scalars are always pushed as * WJB_VALUE items. */ while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { switch (type) { case WJB_BEGIN_ARRAY: if (v.val.array.rawScalar) single_scalar = true; else result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_END_ARRAY: if (!single_scalar) result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_BEGIN_OBJECT: case WJB_END_OBJECT: result->res = pushJsonbValue(&result->parseState, type, NULL); break; case WJB_ELEM: case WJB_KEY: case WJB_VALUE: if (v.type == jbvString) { /* copy string values in the aggregate context */ char *buf = palloc(v.val.string.len + 1); snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val); v.val.string.val = buf; } else if (v.type == jbvNumeric) { /* same for numeric */ v.val.numeric = DatumGetNumeric(DirectFunctionCall1(numeric_uplus, NumericGetDatum(v.val.numeric))); } result->res = pushJsonbValue(&result->parseState, single_scalar ? WJB_VALUE : type, &v); break; default: elog(ERROR, "unknown jsonb iterator token type"); } } MemoryContextSwitchTo(oldcontext); PG_RETURN_POINTER(state); }
/* * common worker for above two functions */ static char * JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent) { bool first = true; JsonbIterator *it; JsonbValue v; JsonbIteratorToken type = WJB_DONE; int level = 0; bool redo_switch = false; /* If we are indenting, don't add a space after a comma */ int ispaces = indent ? 1 : 2; /* * Don't indent the very first item. This gets set to the indent flag at * the bottom of the loop. */ bool use_indent = false; bool raw_scalar = false; bool last_was_key = false; if (out == NULL) out = makeStringInfo(); enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64); it = JsonbIteratorInit(in); while (redo_switch || ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)) { redo_switch = false; switch (type) { case WJB_BEGIN_ARRAY: if (!first) appendBinaryStringInfo(out, ", ", ispaces); if (!v.val.array.rawScalar) { add_indent(out, use_indent && !last_was_key, level); appendStringInfoCharMacro(out, '['); } else raw_scalar = true; first = true; level++; break; case WJB_BEGIN_OBJECT: if (!first) appendBinaryStringInfo(out, ", ", ispaces); add_indent(out, use_indent && !last_was_key, level); appendStringInfoCharMacro(out, '{'); first = true; level++; break; case WJB_KEY: if (!first) appendBinaryStringInfo(out, ", ", ispaces); first = true; add_indent(out, use_indent, level); /* json rules guarantee this is a string */ jsonb_put_escaped_value(out, &v); appendBinaryStringInfo(out, ": ", 2); type = JsonbIteratorNext(&it, &v, false); if (type == WJB_VALUE) { first = false; jsonb_put_escaped_value(out, &v); } else { Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY); /* * We need to rerun the current switch() since we need to * output the object which we just got from the iterator * before calling the iterator again. */ redo_switch = true; } break; case WJB_ELEM: if (!first) appendBinaryStringInfo(out, ", ", ispaces); first = false; if (!raw_scalar) add_indent(out, use_indent, level); jsonb_put_escaped_value(out, &v); break; case WJB_END_ARRAY: level--; if (!raw_scalar) { add_indent(out, use_indent, level); appendStringInfoCharMacro(out, ']'); } first = false; break; case WJB_END_OBJECT: level--; add_indent(out, use_indent, level); appendStringInfoCharMacro(out, '}'); first = false; break; default: elog(ERROR, "unknown jsonb iterator token type"); } use_indent = indent; last_was_key = redo_switch; } Assert(level == 0); return out->data; }