diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 29f058c..eca1684 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -57,6 +57,7 @@ #include "utils/memutils.h" #include "utils/typcache.h" #include "utils/xml.h" +#include "utils/jsonb.h" /* static function decls */ @@ -272,6 +273,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, IntArray upper, lower; int *lIndex; + text **path; array_source = ExecEvalExpr(astate->refexpr, econtext, @@ -290,6 +292,11 @@ ExecEvalArrayRef(ArrayRefExprState *astate, return (Datum) NULL; } + if (arrayRef->refarraytype == JSONBOID) + { + path = (text **) palloc(astate->refupperindexpr->length * sizeof(text*)); + } + foreach(l, astate->refupperindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); @@ -300,10 +307,21 @@ ExecEvalArrayRef(ArrayRefExprState *astate, errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i + 1, MAXDIM))); - upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, - econtext, - &eisnull, - NULL)); + if (arrayRef->refarraytype == JSONBOID) + { + path[i++] = cstring_to_text((char *)DatumGetPointer(ExecEvalExpr(eltstate, + econtext, + &eisnull, + NULL))); + } + else + { + upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, + econtext, + &eisnull, + NULL)); + } + /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { @@ -445,14 +463,24 @@ ExecEvalArrayRef(ArrayRefExprState *astate, } if (lIndex == NULL) - return array_set_element(array_source, i, - upper.indx, - sourceData, - eisnull, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); + { + if (arrayRef->refarraytype == JSONBOID && path != NULL) + { + return jsonb_set_element(array_source, path, i, sourceData, + ((const ArrayRef *) astate->xprstate.expr)->refelemtype); + } + else + { + return array_set_element(array_source, i, + upper.indx, + sourceData, + eisnull, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign); + } + } else return array_set_slice(array_source, i, upper.indx, lower.indx, @@ -465,13 +493,22 @@ ExecEvalArrayRef(ArrayRefExprState *astate, } if (lIndex == NULL) - return array_get_element(array_source, i, - upper.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign, - isNull); + { + if (arrayRef->refarraytype == JSONBOID && path != NULL) + { + return jsonb_get_element(array_source, path, i, isNull); + } + else + { + return array_get_element(array_source, i, + upper.indx, + astate->refattrlength, + astate->refelemlength, + astate->refelembyval, + astate->refelemalign, + isNull); + } + } else return array_get_slice(array_source, i, upper.indx, lower.indx, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index fa77ef1..a08eaab 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -470,13 +470,29 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) } /* process trailing subscripts, if any */ if (subscripts) - result = (Node *) transformArraySubscripts(pstate, - result, - exprType(result), - InvalidOid, - exprTypmod(result), - subscripts, - NULL); + { + if (exprType(result) == JSONBOID) + { + result = (Node *) transformJsonbSubscripts(pstate, + result, + exprType(result), + JSONBOID, + exprTypmod(result), + subscripts, + NULL); + } + else + { + result = (Node *) transformArraySubscripts(pstate, + result, + exprType(result), + InvalidOid, + exprTypmod(result), + subscripts, + NULL); + } + + } return result; } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 4130cbf..3433532 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -258,6 +258,51 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) return elementType; } +ArrayRef * +transformJsonbSubscripts(ParseState *pstate, + Node *arrayBase, + Oid arrayType, + Oid elementType, + int32 arrayTypMod, + List *indirection, + Node *assignFrom) +{ + List *upperIndexpr = NIL; + List *lowerIndexpr = NIL; + ListCell *idx; + ArrayRef *aref; + + /* + * Transform the subscript expressions. + */ + foreach(idx, indirection) + { + A_Indices *ai = (A_Indices *) lfirst(idx); + Node *subexpr; + + Assert(IsA(ai, A_Indices)); + subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); + upperIndexpr = lappend(upperIndexpr, subexpr); + } + + /* + * Ready to build the ArrayRef node. + */ + aref = makeNode(ArrayRef); + aref->refarraytype = arrayType; + aref->refelemtype = elementType; + aref->reftypmod = arrayTypMod; + /* refcollid will be set by parse_collate.c */ + aref->refupperindexpr = upperIndexpr; + aref->reflowerindexpr = lowerIndexpr; + aref->refexpr = (Expr *) arrayBase; + aref->refassgnexpr = (Expr *) assignFrom; + + return aref; +} + + + /* * transformArraySubscripts() * Transform array subscripting. This is used for both diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 1b3fcd6..5f72a66 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -45,6 +45,19 @@ static Node *transformAssignmentIndirection(ParseState *pstate, ListCell *indirection, Node *rhs, int location); + +static Node *transformJsonbAssignmentSubscripts(ParseState *pstate, + Node *basenode, + const char *targetName, + Oid targetTypeId, + int32 targetTypMod, + Oid targetCollation, + List *subscripts, + bool isSlice, + ListCell *next_indirection, + Node *rhs, + int location); + static Node *transformAssignmentSubscripts(ParseState *pstate, Node *basenode, const char *targetName, @@ -744,27 +757,52 @@ transformAssignmentIndirection(ParseState *pstate, if (subscripts) { /* recurse, and then return because we're done */ - return transformAssignmentSubscripts(pstate, - basenode, - targetName, - targetTypeId, - targetTypMod, - targetCollation, - subscripts, - isSlice, - NULL, - rhs, - location); + if (exprType(basenode) == JSONBOID) + { + return transformJsonbAssignmentSubscripts(pstate, + basenode, + targetName, + targetTypeId, + targetTypMod, + targetCollation, + subscripts, + isSlice, + i, + rhs, + location); + } + else + { + return transformAssignmentSubscripts(pstate, + basenode, + targetName, + targetTypeId, + targetTypMod, + targetCollation, + subscripts, + isSlice, + NULL, + rhs, + location); + } } /* base case: just coerce RHS to match target type ID */ - result = coerce_to_target_type(pstate, - rhs, exprType(rhs), - targetTypeId, targetTypMod, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); + if (targetTypeId != InvalidOid) + { + result = coerce_to_target_type(pstate, + rhs, exprType(rhs), + targetTypeId, targetTypMod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + } + else + { + result = rhs; + } + if (result == NULL) { if (targetIsArray) @@ -792,6 +830,92 @@ transformAssignmentIndirection(ParseState *pstate, return result; } +static Node * +transformJsonbAssignmentSubscripts(ParseState *pstate, + Node *basenode, + const char *targetName, + Oid targetTypeId, + int32 targetTypMod, + Oid targetCollation, + List *subscripts, + bool isSlice, + ListCell *next_indirection, + Node *rhs, + int location) +{ + Node *result; + Oid arrayType; + int32 arrayTypMod; + Oid elementTypeId; + Oid typeNeeded; + Oid collationNeeded; + + Assert(subscripts != NIL); + + /* Identify the actual array type and element type involved */ + arrayType = targetTypeId; + arrayTypMod = targetTypMod; + elementTypeId = InvalidOid; + + /* Identify type that RHS must provide */ + typeNeeded = isSlice ? arrayType : elementTypeId; + + /* + * Array normally has same collation as elements, but there's an + * exception: we might be subscripting a domain over an array type. In + * that case use collation of the base type. + */ + if (arrayType == targetTypeId) + collationNeeded = targetCollation; + else + collationNeeded = get_typcollation(arrayType); + + /* recurse to create appropriate RHS for array assign */ + rhs = transformAssignmentIndirection(pstate, + NULL, + targetName, + true, + typeNeeded, + arrayTypMod, + collationNeeded, + next_indirection, + rhs, + location); + + /* process subscripts */ + result = (Node *) transformJsonbSubscripts(pstate, + basenode, + arrayType, + exprType(rhs), + arrayTypMod, + subscripts, + rhs); + + /* If target was a domain over array, need to coerce up to the domain */ + if (arrayType != targetTypeId) + { + Oid resulttype = exprType(result); + + result = coerce_to_target_type(pstate, + result, resulttype, + targetTypeId, targetTypMod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + /* can fail if we had int2vector/oidvector, but not for true domains */ + if (result == NULL) + ereport(ERROR, + (errcode(ERRCODE_CANNOT_COERCE), + errmsg("cannot cast type %s to %s", + format_type_be(resulttype), + format_type_be(targetTypeId)), + parser_errposition(pstate, location))); + } + + return result; +} + + /* * helper for transformAssignmentIndirection: process array assignment */ diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c index 154bc36..523a0ca 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -1943,3 +1943,45 @@ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS) PG_RETURN_POINTER(out); } + + +Datum +jsonb_set_element(Datum jsonbdatum, + text **path, int path_len, Datum sourceData, Oid source_type) +{ + Jsonb *jb = DatumGetJsonb(jsonbdatum); + JsonbInState result; + JsonbTypeCategory tcategory; + Oid outfuncoid; + JsonbValue v, newkey, newval; + JsonbParseState *state = NULL; + JsonbIterator *it; + uint32 r; + JsonbValue *res = NULL; + int i, level = 0; + bool *path_nulls = palloc(path_len * sizeof(bool)); + + jsonb_categorize_type(source_type, + &tcategory, &outfuncoid); + memset(&result, 0, sizeof(JsonbInState)); + result.parseState = NULL; + datum_to_jsonb(sourceData, false, &result, tcategory, outfuncoid, false); + + it = JsonbIteratorInit(&jb->root); + + newval = *result.res; + + if (newval.type == jbvArray && newval.val.array.rawScalar == true) + { + newval = newval.val.array.elems[0]; + } + + for(i = 0; i < path_len; i++) + { + path_nulls[i]= false; + } + + res = setPath(&it, path, path_nulls, path_len, &state, 0, (void *)&newval, true, true); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index 4d73315..aa2384b 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -408,7 +408,6 @@ findJsonbValueFromContainer(JsonbContainer *container, uint32 flags, pfree(result); return NULL; } - /* * Get i-th value of a Jsonb array. * diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 3b8d42e..1b4074e 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -33,6 +33,12 @@ #include "utils/memutils.h" #include "utils/typcache.h" +#define add_newval(state, newval, unpacked) \ + if (unpacked) \ + (void) pushJsonbValue(st, WJB_VALUE, (JsonbValue *)newval); \ + else \ + addJsonbToParseState(st, (Jsonb *)newval); + /* semantic action functions for json_object_keys */ static void okeys_object_field_start(void *state, char *fname, bool isnull); static void okeys_array_start(void *state); @@ -127,17 +133,13 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container, /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */ static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2, JsonbParseState **state); -static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, - bool *path_nulls, int path_len, - JsonbParseState **st, int level, Jsonb *newval, - bool create); static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 npairs, bool create); + void *newval, bool unpacked, uint32 npairs, bool create); static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, - int level, Jsonb *newval, uint32 nelems, bool create); + int level, void *newval, bool unpacked, uint32 nelems, bool create); static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb); /* state for json_object_keys */ @@ -3538,7 +3540,7 @@ jsonb_set(PG_FUNCTION_ARGS) it = JsonbIteratorInit(&in->root); res = setPath(&it, path_elems, path_nulls, path_len, &st, - 0, newval, create); + 0, (void *)newval, false, create); Assert(res != NULL); @@ -3582,7 +3584,7 @@ jsonb_delete_path(PG_FUNCTION_ARGS) it = JsonbIteratorInit(&in->root); - res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, NULL, false); + res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, NULL, false, false); Assert(res != NULL); @@ -3709,10 +3711,10 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2, * does not exist. All path elements before the last must already exist * whether or not create is true, or nothing is done. */ -static JsonbValue * +JsonbValue * setPath(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, - JsonbParseState **st, int level, Jsonb *newval, bool create) + JsonbParseState **st, int level, void *newval, bool unpacked, bool create) { JsonbValue v; JsonbValue *res = NULL; @@ -3725,7 +3727,7 @@ setPath(JsonbIterator **it, Datum *path_elems, case WJB_BEGIN_ARRAY: (void) pushJsonbValue(st, r, NULL); setPathArray(it, path_elems, path_nulls, path_len, st, level, - newval, v.val.array.nElems, create); + newval, unpacked, v.val.array.nElems, create); r = JsonbIteratorNext(it, &v, false); Assert(r == WJB_END_ARRAY); res = pushJsonbValue(st, r, NULL); @@ -3734,7 +3736,7 @@ setPath(JsonbIterator **it, Datum *path_elems, case WJB_BEGIN_OBJECT: (void) pushJsonbValue(st, r, NULL); setPathObject(it, path_elems, path_nulls, path_len, st, level, - newval, v.val.object.nPairs, create); + newval, unpacked, v.val.object.nPairs, create); r = JsonbIteratorNext(it, &v, true); Assert(r == WJB_END_OBJECT); res = pushJsonbValue(st, r, NULL); @@ -3757,7 +3759,7 @@ setPath(JsonbIterator **it, Datum *path_elems, static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 npairs, bool create) + void *newval, bool unpacked, uint32 npairs, bool create) { JsonbValue v; int i; @@ -3767,17 +3769,34 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (level >= path_len || path_nulls[level]) done = true; - /* empty object is a special case for create */ - if ((npairs == 0) && create && (level == path_len - 1)) + if ((npairs == 0) && create) { + int i; + for (i = 0; i < path_len - 1 - level; i++) + { + JsonbValue newkey; + + newkey.type = jbvString; + newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level + i]); + newkey.val.string.val = VARDATA_ANY(path_elems[level + i]); + + (void) pushJsonbValue(st, WJB_KEY, &newkey); + (void) pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL); + } + JsonbValue newkey; newkey.type = jbvString; - newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]); - newkey.val.string.val = VARDATA_ANY(path_elems[level]); + newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level + i]); + newkey.val.string.val = VARDATA_ANY(path_elems[level + i]); (void) pushJsonbValue(st, WJB_KEY, &newkey); - addJsonbToParseState(st, newval); + add_newval(st, newval, unpacked); + + for (i = 0; i < path_len - 1 - level; i++) + { + (void) pushJsonbValue(st, WJB_END_OBJECT, NULL); + } } for (i = 0; i < npairs; i++) @@ -3797,7 +3816,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (newval != NULL) { (void) pushJsonbValue(st, WJB_KEY, &k); - addJsonbToParseState(st, newval); + add_newval(st, newval, unpacked); } done = true; } @@ -3805,7 +3824,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, { (void) pushJsonbValue(st, r, &k); setPath(it, path_elems, path_nulls, path_len, - st, level + 1, newval, create); + st, level + 1, newval, unpacked, create); } } else @@ -3819,7 +3838,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, newkey.val.string.val = VARDATA_ANY(path_elems[level]); (void) pushJsonbValue(st, WJB_KEY, &newkey); - addJsonbToParseState(st, newval); + add_newval(st, newval, unpacked); } (void) pushJsonbValue(st, r, &k); @@ -3851,7 +3870,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 nelems, bool create) + void *newval, bool unpacked, uint32 nelems, bool create) { JsonbValue v; int idx, @@ -3896,7 +3915,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if ((idx == INT_MIN || nelems == 0) && create && (level == path_len - 1)) { Assert(newval != NULL); - addJsonbToParseState(st, newval); + add_newval(st, newval, unpacked); done = true; } @@ -3911,13 +3930,13 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, { r = JsonbIteratorNext(it, &v, true); /* skip */ if (newval != NULL) - addJsonbToParseState(st, newval); + add_newval(st, newval, unpacked); done = true; } else (void) setPath(it, path_elems, path_nulls, path_len, - st, level + 1, newval, create); + st, level + 1, newval, unpacked, create); } else { @@ -3944,9 +3963,46 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (create && !done && level == path_len - 1 && i == nelems - 1) { - addJsonbToParseState(st, newval); + add_newval(st, newval, unpacked); } } } } + + +Datum +jsonb_get_element(Datum jsonbdatum, + text **path, int path_len, bool *isNull) +{ + Jsonb *jb = DatumGetJsonb(jsonbdatum); + JsonbValue *v; + int level = 1; + + if (!JB_ROOT_IS_OBJECT(jb)) + { + *isNull = true; + return (Datum) 0; + } + + v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT, + VARDATA_ANY(path[0]), + VARSIZE_ANY_EXHDR(path[0])); + + while (v != NULL && + v->type == jbvBinary && level < path_len) + { + v = findJsonbValueFromContainerLen(v->val.binary.data, JB_FOBJECT, + VARDATA_ANY(path[level]), + VARSIZE_ANY_EXHDR(path[level])); + level++; + } + + if (v != NULL && level == path_len) + { + PG_RETURN_JSONB(JsonbValueToJsonb(v)); + } + + *isNull = true; + return (Datum) 0; +} diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 5249945..8c50e2b 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -224,6 +224,13 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate); extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location); extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod); +extern ArrayRef *transformJsonbSubscripts(ParseState *pstate, + Node *arrayBase, + Oid arrayType, + Oid elementType, + int32 arrayTypMod, + List *indirection, + Node *assignFrom); extern ArrayRef *transformArraySubscripts(ParseState *pstate, Node *arrayBase, Oid arrayType, diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index 3049a87..124b6b4 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -433,5 +433,11 @@ extern char *JsonbToCString(StringInfo out, JsonbContainer *in, extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in, int estimated_len); +extern Datum jsonb_set_element(Datum datum, text **path, int path_len, Datum sourceData, Oid source_type); +extern Datum jsonb_get_element(Datum datum, text **path, int path_len, bool *isNull); +extern JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, + JsonbParseState **st, int level, void *newval, + bool unpacked, bool create); #endif /* __JSONB_H__ */ diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 17656d4..915f97b 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -3295,3 +3295,97 @@ select jsonb_set('[]','{-99}','{"foo":123}'); [{"foo": 123}] (1 row) +-- jsonb subscript +select ('{"a": 1}'::jsonb)['a']; + jsonb +------- + 1 +(1 row) + +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b']; + jsonb +------- + "c" +(1 row) + +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']; + jsonb +----------- + [1, 2, 3] +(1 row) + +select ('{"a": 1}'::jsonb)['not_exist']; + jsonb +------- + +(1 row) + +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']; + jsonb +--------------- + {"a2": "aaa"} +(1 row) + +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']; + jsonb +------- + "aaa" +(1 row) + +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3']; + jsonb +------- + +(1 row) + +create TEMP TABLE test_jsonb_subscript ( + id int, + test_json jsonb +); +insert into test_jsonb_subscript values +(1, '{}'), +(2, '{}'); +update test_jsonb_subscript set test_json['a'] = 1; +select * from test_jsonb_subscript; + id | test_json +----+----------- + 1 | {"a": 1} + 2 | {"a": 1} +(2 rows) + +update test_jsonb_subscript set test_json['a'] = 'test'; +select * from test_jsonb_subscript; + id | test_json +----+--------------- + 1 | {"a": "test"} + 2 | {"a": "test"} +(2 rows) + +update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb; +select * from test_jsonb_subscript; + id | test_json +----+----------------- + 1 | {"a": {"b": 1}} + 2 | {"a": {"b": 1}} +(2 rows) + +update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb; +select * from test_jsonb_subscript; + id | test_json +----+------------------ + 1 | {"a": [1, 2, 3]} + 2 | {"a": [1, 2, 3]} +(2 rows) + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values +(1, '{}'), +(2, '{}'); +update test_jsonb_subscript set test_json['b']['b1']['b2'] = 1; +select * from test_jsonb_subscript; + id | test_json +----+-------------------------- + 1 | {"b": {"b1": {"b2": 1}}} + 2 | {"b": {"b1": {"b2": 1}}} +(2 rows) + diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index 83ed4eb..d7e3aa7 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -809,3 +809,41 @@ select jsonb_set('{}','{x}','{"foo":123}'); select jsonb_set('[]','{0}','{"foo":123}'); select jsonb_set('[]','{99}','{"foo":123}'); select jsonb_set('[]','{-99}','{"foo":123}'); + +-- jsonb subscript +select ('{"a": 1}'::jsonb)['a']; +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b']; +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']; +select ('{"a": 1}'::jsonb)['not_exist']; +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']; +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']; +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3']; + +create TEMP TABLE test_jsonb_subscript ( + id int, + test_json jsonb +); + +insert into test_jsonb_subscript values +(1, '{}'), +(2, '{}'); + +update test_jsonb_subscript set test_json['a'] = 1; +select * from test_jsonb_subscript; + +update test_jsonb_subscript set test_json['a'] = 'test'; +select * from test_jsonb_subscript; + +update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb; +select * from test_jsonb_subscript; + +update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb; +select * from test_jsonb_subscript; + +delete from test_jsonb_subscript; +insert into test_jsonb_subscript values +(1, '{}'), +(2, '{}'); + +update test_jsonb_subscript set test_json['b']['b1']['b2'] = 1; +select * from test_jsonb_subscript;