From 9c06570dec06e168a8cce87ca79a853dd6ed6df4 Mon Sep 17 00:00:00 2001 From: nnuanyang Date: Thu, 16 Nov 2023 23:01:07 -0800 Subject: [PATCH 1/2] jsonb_insert & jsonb_set function --- src/common/backend/catalog/builtin_funcs.ini | 14 + src/common/backend/utils/adt/jsonfuncs.cpp | 732 ++++++++++++++++++ src/common/backend/utils/init/globals.cpp | 2 +- src/include/catalog/pg_operator.data | 6 + src/include/catalog/pg_type.h | 5 + .../rollback-post_catalog_maindb_92_918.sql | 16 + .../rollback-post_catalog_otherdb_92_918.sql | 16 + .../upgrade-post_catalog_maindb_92_918.sql | 41 + .../upgrade-post_catalog_otherdb_92_918.sql | 41 + src/include/utils/json.h | 5 + src/test/regress/expected/json_and_jsonb.out | 12 +- src/test/regress/expected/jsonb.out | 168 ++++ src/test/regress/sql/jsonb.sql | 43 + 13 files changed, 1098 insertions(+), 3 deletions(-) create mode 100644 src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_918.sql create mode 100644 src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_918.sql create mode 100644 src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_918.sql create mode 100644 src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_918.sql diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index cfcadfa03f..06c87fcd1d 100644 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -6325,6 +6325,12 @@ "jsonb_contains", 1, AddBuiltinFunc(_0(3418), _1("jsonb_contains"), _2(2), _3(true), _4(false), _5(jsonb_contains), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(2, 3802, 3802), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_contains"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) ), + AddFuncGroup( + "jsonb_delete", 3, + AddBuiltinFunc(_0(5612), _1("jsonb_delete"), _2(2), _3(true), _4(false), _5(jsonb_delete_idx), _6(3802), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(2, 3802, 23), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_delete_idx"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(5613), _1("jsonb_delete"), _2(2), _3(true), _4(false), _5(jsonb_delete), _6(3802), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(2, 3802, 25), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_delete"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(5614), _1("jsonb_delete"), _2(2), _3(true), _4(false), _5(jsonb_delete_array), _6(3802), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(2, 3802, 1009), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_delete_array"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), AddFuncGroup( "jsonb_each", 1, AddBuiltinFunc(_0(3419), _1("jsonb_each"), _2(1), _3(true), _4(true), _5(jsonb_each), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(100), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1, 3802), _21(3, 3802, 25, 3802), _22(3, 'i', 'o', 'o'), _23(3, "from_json", "key", "value"), _24(NULL), _25("jsonb_each"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) @@ -6381,6 +6387,10 @@ "jsonb_in", 1, AddBuiltinFunc(_0(3806), _1("jsonb_in"), _2(1), _3(true), _4(false), _5(jsonb_in), _6(3802), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1, 2275), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_in"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) ), + AddFuncGroup( + "jsonb_insert", 1, + AddBuiltinFunc(_0(5610), _1("jsonb_insert"), _2(4), _3(true), _4(false), _5(jsonb_insert), _6(3802), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(1), _20(4, 3802, 1009, 3802, 16), _21(NULL), _22(NULL), _23(NULL), _24("({CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :ismaxvalue false :location 72803 :constvalue 1 [ 0 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno 0 :is_open false :found false :not_found false :null_open false :null_fetch false})"), _25("jsonb_insert"), _26(NULL), _27(NULL), _28(NULL), _29(1, 3), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), AddFuncGroup( "jsonb_le", 1, AddBuiltinFunc(_0(3431), _1("jsonb_le"), _2(2), _3(true), _4(false), _5(jsonb_le), _6(16), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(2, 3802, 3802), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_le"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) @@ -6425,6 +6435,10 @@ "jsonb_send", 1, AddBuiltinFunc(_0(3444), _1("jsonb_send"), _2(1), _3(true), _4(false), _5(jsonb_send), _6(17), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1, 3802), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_send"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) ), + AddFuncGroup( + "jsonb_set", 1, + AddBuiltinFunc(_0(5611), _1("jsonb_set"), _2(4), _3(true), _4(false), _5(jsonb_set), _6(3802), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(1), _20(4, 3802, 1009, 3802, 16), _21(NULL), _22(NULL), _23(NULL), _24("({CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :ismaxvalue false :location 72803 :constvalue 1 [ 0 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno 0 :is_open false :found false :not_found false :null_open false :null_fetch false})"), _25("jsonb_set"), _26(NULL), _27(NULL), _28(NULL), _29(1, 3), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), AddFuncGroup( "jsonb_typeof", 1, AddBuiltinFunc(_0(3445), _1("jsonb_typeof"), _2(1), _3(true), _4(false), _5(jsonb_typeof), _6(25), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1, 3802), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("jsonb_typeof"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(false), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) diff --git a/src/common/backend/utils/adt/jsonfuncs.cpp b/src/common/backend/utils/adt/jsonfuncs.cpp index a15bb02096..5905aa862e 100644 --- a/src/common/backend/utils/adt/jsonfuncs.cpp +++ b/src/common/backend/utils/adt/jsonfuncs.cpp @@ -33,6 +33,18 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/typcache.h" +#include "utils/array.h" + +/* Operations available for setPath */ +#define JB_PATH_CREATE 0x0001 +#define JB_PATH_DELETE 0x0002 +#define JB_PATH_REPLACE 0x0004 +#define JB_PATH_INSERT_BEFORE 0x0008 +#define JB_PATH_INSERT_AFTER 0x0010 +#define JB_PATH_CREATE_OR_INSERT \ + (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE) +#define JB_PATH_FILL_GAPS 0x0020 +#define JB_PATH_CONSISTENT_POSITION 0x0040 /* semantic action functions for json_object_keys */ static void okeys_object_field_start(void *state, char *fname, bool isnull); @@ -105,6 +117,21 @@ static inline Datum populate_recordset_worker(FunctionCallInfo fcinfo, bool have /* Worker that takes care of common setup for us */ static JsonbValue *findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, uint32 flags, char *key, uint32 keylen); +/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */ +static void addJsonbToParseState(JsonbParseState **pstate, Jsonb *jb); +static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, + JsonbParseState **st, int level, Jsonb *newval, + int op_type); +static void setPathObject(JsonbIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, JsonbParseState **st, + int level, + Jsonb *newval, uint32 npairs, int op_type); +static void setPathArray(JsonbIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, JsonbParseState **st, + int level, + Jsonb *newval, uint32 nelems, int op_type); + /* search type classification for json_get* functions */ typedef enum { JSON_SEARCH_OBJECT = 1, @@ -2680,3 +2707,708 @@ static JsonbValue *findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, ui return findJsonbValueFromSuperHeader(sheader, flags, NULL, &k); } + +static void +push_null_elements(JsonbParseState **ps, int num) +{ + JsonbValue null; + + null.type = jbvNull; + + while (num-- > 0) + pushJsonbValue(ps, WJB_ELEM, &null); +} + +static void +addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb) +{ + JsonbIterator *it; + JsonbValue *o = &(*jbps)->contVal; + int type; + JsonbValue v; + + it = JsonbIteratorInit(VARDATA(jb)); + + Assert(o->type == jbvArray || o->type == jbvObject); + + if(JB_ROOT_IS_SCALAR(jb)) { + (void) JsonbIteratorNext(&it, &v, false); + (void) JsonbIteratorNext(&it, &v, false); + + switch(o->type) + { + case jbvArray: + (void)pushJsonbValue(jbps, WJB_ELEM, &v); + break; + case jbvObject: + (void)pushJsonbValue(jbps, WJB_VALUE, &v); + break; + default: + elog(ERROR, "unexpected parent oe nested structure."); + } + } else { + while((type == JsonbIteratorNext(&it, &v, false)) != WJB_DONE) { + if (type = WJB_ELEM || type == WJB_KEY || type == WJB_VALUE) { + (void)pushJsonbValue(jbps, type, &v); + } else { + (void)pushJsonbValue(jbps, type, NULL); + } + } + } +} + +/* + * Do most of the heavy work for jsonb_set/jsonb_insert + * + * If JB_PATH_DELETE bit is set in op_type, the element is to be removed. + * + * If any bit mentioned in JB_PATH_CREATE_OR_INSERT is set in op_type, + * we create the new value if the key or array index does not exist. + * + * Bits JB_PATH_INSERT_BEFORE and JB_PATH_INSERT_AFTER in op_type + * behave as JB_PATH_CREATE if new value is inserted in JsonbObject. + * + * If JB_PATH_FILL_GAPS bit is set, this will change an assignment logic in + * case if target is an array. The assignment index will not be restricted by + * number of elements in the array, and if there are any empty slots between + * last element of the array and a new one they will be filled with nulls. If + * the index is negative, it still will be considered an index from the end + * of the array. Of a part of the path is not present and this part is more + * than just one last element, this flag will instruct to create the whole + * chain of corresponding objects and insert the value. + * + * JB_PATH_CONSISTENT_POSITION for an array indicates that the caller wants to + * keep values with fixed indices. Indices for existing elements could be + * changed (shifted forward) in case if the array is prepended with a new value + * and a negative index out of the range, so this behavior will be prevented + * and return an error. + * + * All path elements before the last must already exist + * whatever bits in op_type are set, or nothing is done. + */ +static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, + bool *path_nulls, int path_len, + JsonbParseState **st, int level, Jsonb *newval, int op_type) +{ + JsonbValue v; + int r; + JsonbValue *res; + + check_stack_depth(); + + if (path_nulls[level]) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("path element at position %d is null", + level + 1))); + + r = JsonbIteratorNext(it, &v, false); + + switch (r) + { + case WJB_BEGIN_ARRAY: + + /* + * If instructed complain about attempts to replace within a raw + * scalar value. This happens even when current level is equal to + * path_len, because the last path key should also correspond to + * an object or an array, not raw scalar. + */ + if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1) && + v.array.rawScalar) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot replace existing key"), + errdetail("The path assumes key is a composite object, " + "but it is a scalar value."))); + + (void) pushJsonbValue(st, r, NULL); + setPathArray(it, path_elems, path_nulls, path_len, st, level, + newval, v.array.nElems, op_type); + r = JsonbIteratorNext(it, &v, false); + Assert(r == WJB_END_ARRAY); + res = pushJsonbValue(st, r, NULL); + break; + case WJB_BEGIN_OBJECT: + (void) pushJsonbValue(st, r, NULL); + setPathObject(it, path_elems, path_nulls, path_len, st, level, + newval, v.object.nPairs, op_type); + r = JsonbIteratorNext(it, &v, true); + Assert(r == WJB_END_OBJECT); + res = pushJsonbValue(st, r, NULL); + break; + case WJB_ELEM: + case WJB_VALUE: + + /* + * If instructed complain about attempts to replace within a + * scalar value. This happens even when current level is equal to + * path_len, because the last path key should also correspond to + * an object or an array, not an element or value. + */ + if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot replace existing key"), + errdetail("The path assumes key is a composite object, " + "but it is a scalar value."))); + + res = pushJsonbValue(st, r, &v); + break; + default: + elog(ERROR, "unrecognized iterator result: %d", (int) r); + res = NULL; /* keep compiler quiet */ + break; + } + + return res; +} + +/* + * Object walker for setPath + */ +static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, + int path_len, JsonbParseState **st, int level, + Jsonb *newval, uint32 npairs, int op_type) +{ + text *pathelem = NULL; + int i; + JsonbValue k, + v; + bool done = false; + + if (level >= path_len || path_nulls[level]) + done = true; + else + { + /* The path Datum could be toasted, in which case we must detoast it */ + pathelem = DatumGetTextPP(path_elems[level]); + } + + /* empty object is a special case for create */ + if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) && + (level == path_len - 1)) + { + JsonbValue newkey; + + newkey.type = jbvString; + newkey.string.val = VARDATA_ANY(pathelem); + newkey.string.len = VARSIZE_ANY_EXHDR(pathelem); + + (void) pushJsonbValue(st, WJB_KEY, &newkey); + addJsonbToParseState(st, newval); + } + + for (i = 0; i < npairs; i++) + { + int r = JsonbIteratorNext(it, &k, true); + + Assert(r == WJB_KEY); + + if (!done && + k.string.len == VARSIZE_ANY_EXHDR(pathelem) && + memcmp(k.string.val, VARDATA_ANY(pathelem), + k.string.len) == 0) + { + done = true; + + if (level == path_len - 1) + { + /* + * called from jsonb_insert(), it forbids redefining an + * existing value + */ + if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot replace existing key"), + errhint("Try using the function jsonb_set " + "to replace key value."))); + + r = JsonbIteratorNext(it, &v, true); /* skip value */ + if (!(op_type & JB_PATH_DELETE)) + { + (void) pushJsonbValue(st, WJB_KEY, &k); + addJsonbToParseState(st, newval); + } + } + else + { + (void) pushJsonbValue(st, r, &k); + setPath(it, path_elems, path_nulls, path_len, + st, level + 1, newval, op_type); + } + } + else + { + if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && + level == path_len - 1 && i == npairs - 1) + { + JsonbValue newkey; + + newkey.type = jbvString; + newkey.string.val = VARDATA_ANY(pathelem); + newkey.string.len = VARSIZE_ANY_EXHDR(pathelem); + + (void) pushJsonbValue(st, WJB_KEY, &newkey); + addJsonbToParseState(st, newval); + } + + (void) pushJsonbValue(st, r, &k); + r = JsonbIteratorNext(it, &v, false); + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) + { + int walking_level = 1; + + while (walking_level != 0) + { + r = JsonbIteratorNext(it, &v, false); + + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) + ++walking_level; + if (r == WJB_END_ARRAY || r == WJB_END_OBJECT) + --walking_level; + + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + } + } + } +} + +/* + * Array walker for setPath + */ +static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, + int path_len, JsonbParseState **st, int level, + Jsonb *newval, uint32 nelems, int op_type) +{ + JsonbValue v; + int idx, + i; + bool done = false; + + /* pick correct index */ + if (level < path_len && !path_nulls[level]) + { + char *c = TextDatumGetCString(path_elems[level]); + char *badp; + long val; + + errno = 0; + val = strtol(c, &badp, 10); + if (errno != 0 || badp == c || badp[0] != '\0' || val > INT_MAX || + val < INT_MIN) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("path element at position %d is not an integer: \"%s\"", + level + 1, c))); + idx = val; + } + else + idx = nelems; + + if (idx < 0) + { + if (-idx > nelems) + { + /* + * If asked to keep elements position consistent, it's not allowed + * to prepend the array. + */ + if (op_type & JB_PATH_CONSISTENT_POSITION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("path element at position %d is out of range: %d", + level + 1, idx))); + else + idx = INT_MIN; + } + else + idx = nelems + idx; + } + + /* + * Filling the gaps means there are no limits on the positive index are + * imposed, we can set any element. Otherwise limit the index by nelems. + */ + if (!(op_type & JB_PATH_FILL_GAPS)) + { + if (idx > 0 && idx > nelems) + idx = nelems; + } + + /* + * if we're creating, and idx == INT_MIN, we prepend the new value to the + * array also if the array is empty - in which case we don't really care + * what the idx value is + */ + if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) && + (op_type & JB_PATH_CREATE_OR_INSERT)) + { + Assert(newval != NULL); + addJsonbToParseState(st, newval); + done = true; + } + + /* iterate over the array elements */ + for (i = 0; i < nelems; i++) + { + int r; + + if (i == idx && level < path_len) + { + done = true; + + if (level == path_len - 1) + { + r = JsonbIteratorNext(it, &v, true); /* skip */ + + if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE)) + addJsonbToParseState(st, newval); + + /* + * We should keep current value only in case of + * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER because + * otherwise it should be deleted or replaced + */ + if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE)) + (void) pushJsonbValue(st, r, &v); + + if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE)) + addJsonbToParseState(st, newval); + } + else + (void) setPath(it, path_elems, path_nulls, path_len, + st, level + 1, newval, op_type); + } + else + { + r = JsonbIteratorNext(it, &v, false); + + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) + { + int walking_level = 1; + + while (walking_level != 0) + { + r = JsonbIteratorNext(it, &v, false); + + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) + ++walking_level; + if (r == WJB_END_ARRAY || r == WJB_END_OBJECT) + --walking_level; + + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + } + } + } + + if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && level == path_len - 1) + { + /* + * If asked to fill the gaps, idx could be bigger than nelems, so + * prepend the new element with nulls if that's the case. + */ + if (op_type & JB_PATH_FILL_GAPS && idx > nelems) + push_null_elements(st, idx - nelems); + + addJsonbToParseState(st, newval); + done = true; + } +} + +/* + * SQL function jsonb_insert(jsonb, text[], jsonb, boolean) + */ +Datum jsonb_insert(PG_FUNCTION_ARGS) +{ + Jsonb *in = PG_GETARG_JSONB(0); + ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *newjsonb = PG_GETARG_JSONB(2); + bool after = 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"))); + + deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); + + if (path_len == 0) + PG_RETURN_JSONB(in); + + it = JsonbIteratorInit(VARDATA(in)); + + res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newjsonb, + after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE); + + Assert(res != NULL); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} + +/* + * SQL function jsonb_delete (jsonb, text) + * + * return a copy of the jsonb with the indicated item + * 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; + JsonbValue v, + *res = NULL; + bool skipNested = false; + int r; + + 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(VARDATA(in)); + + while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) + { + skipNested = true; + + if ((r == WJB_ELEM || r == WJB_KEY) && + (v.type == jbvString && keylen == v.string.len && + memcmp(keyptr, v.string.val, keylen) == 0)) + { + /* skip corresponding value as well */ + if (r == WJB_KEY) + (void) JsonbIteratorNext(&it, &v, true); + + continue; + } + + res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + + Assert(res != NULL); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} + +/* + * SQL function jsonb_delete (jsonb, variadic text[]) + * + * return a copy of the jsonb with the indicated items + * removed. + */ +Datum jsonb_delete_array(PG_FUNCTION_ARGS) +{ + Jsonb *in = PG_GETARG_JSONB(0); + ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); + Datum *keys_elems; + bool *keys_nulls; + int keys_len; + JsonbParseState *state = NULL; + JsonbIterator *it; + JsonbValue v, + *res = NULL; + bool skipNested = false; + int r; + + if (ARR_NDIM(keys) > 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 delete from scalar"))); + + if (JB_ROOT_COUNT(in) == 0) + PG_RETURN_JSONB(in); + + deconstruct_array(keys, TEXTOID, -1, false, 'i', &keys_elems, &keys_nulls, &keys_len); + + if (keys_len == 0) + PG_RETURN_JSONB(in); + + it = JsonbIteratorInit(VARDATA(in)); + + while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) + { + skipNested = true; + + if ((r == WJB_ELEM || r == WJB_KEY) && v.type == jbvString) + { + int i; + bool found = false; + + for (i = 0; i < keys_len; i++) + { + char *keyptr; + int keylen; + + if (keys_nulls[i]) + continue; + + /* We rely on the array elements not being toasted */ + keyptr = VARDATA_ANY(keys_elems[i]); + keylen = VARSIZE_ANY_EXHDR(keys_elems[i]); + if (keylen == v.string.len && + memcmp(keyptr, v.string.val, keylen) == 0) + { + found = true; + break; + } + } + if (found) + { + /* skip corresponding value as well */ + if (r == WJB_KEY) + (void) JsonbIteratorNext(&it, &v, true); + + continue; + } + } + + res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + + Assert(res != NULL); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} + +/* + * SQL function jsonb_delete (jsonb, int) + * + * return a copy of the jsonb with the indicated item + * removed. Negative int means count back from the + * end of the items. + */ +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 i = 0, + n; + JsonbValue v, + *res = NULL; + int r; + + if (JB_ROOT_IS_SCALAR(in)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot delete from scalar"))); + + if (JB_ROOT_IS_OBJECT(in)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot delete from object using integer index"))); + + if (JB_ROOT_COUNT(in) == 0) + PG_RETURN_JSONB(in); + + it = JsonbIteratorInit(VARDATA(in)); + + r = JsonbIteratorNext(&it, &v, false); + Assert(r == WJB_BEGIN_ARRAY); + n = v.array.nElems; + + if (idx < 0) + { + if (-idx > n) + idx = n; + else + idx = n + idx; + } + + if (idx >= n) + PG_RETURN_JSONB(in); + + pushJsonbValue(&state, r, NULL); + + while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) + { + if (r == WJB_ELEM) + { + if (i++ == idx) + continue; + } + + res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + + Assert(res != NULL); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} + +/* + * SQL function jsonb_set(jsonb, text[], jsonb, boolean) + */ +Datum jsonb_set(PG_FUNCTION_ARGS) +{ + Jsonb *in = PG_GETARG_JSONB(0); + ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *newjsonb = 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(VARDATA(in)); + + res = setPath(&it, path_elems, path_nulls, path_len, &st, + 0, newjsonb, create ? JB_PATH_CREATE : JB_PATH_REPLACE); + + Assert(res != NULL); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index f8e2663fe2..5d018c1913 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -75,7 +75,7 @@ bool will_shutdown = false; * NEXT | 92899 | ? | ? * ********************************************/ -const uint32 GRAND_VERSION_NUM = 92917; +const uint32 GRAND_VERSION_NUM = 92918; /******************************************** * 2.VERSION NUM FOR EACH FEATURE diff --git a/src/include/catalog/pg_operator.data b/src/include/catalog/pg_operator.data index ce5638f1c9..67e260f310 100644 --- a/src/include/catalog/pg_operator.data +++ b/src/include/catalog/pg_operator.data @@ -1730,6 +1730,12 @@ DATA(insert OID = 3249 ( "?&" PGNSP PGUID b f f 3802 1009 16 0 0 jsonb_exist DESCR("exists all"); DATA(insert OID = 3250 ( "<@" PGNSP PGUID b f f 3802 3802 16 0 3246 jsonb_contained contsel contjoinsel )); DESCR("contained"); +DATA(insert OID = 3251 ( "-" PGNSP PGUID b f f 3802 23 3802 0 0 5612 - - )); +DESCR("delete index"); +DATA(insert OID = 3252 ( "-" PGNSP PGUID b f f 3802 25 3802 0 0 5613 - - )); +DESCR("delete text"); +DATA(insert OID = 3253 ( "-" PGNSP PGUID b f f 3802 1009 3802 0 0 5614 - - )); +DESCR("delete text[]"); DATA(insert OID = 5550 ("=" PGNSP PGUID b t t 9003 9003 16 5550 5551 smalldatetime_eq eqsel eqjoinsel)); DESCR("equal"); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 4eb0a3e03c..7e51d605d3 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -856,6 +856,11 @@ DESCR("undefined objects as PLSQL compilation time"); #define TYPCATEGORY_TABLEOF_INTEGER 'F' /* table of type, index by integer */ #define TYPCATEGORY_SET 'H' /* for set type */ +#define TYPALIGN_CHAR 'c' /* char alignment (i.e. unaligned) */ +#define TYPALIGN_SHORT 's' /* short alignment (typically 2 bytes) */ +#define TYPALIGN_INT 'i' /* int alignment (typically 4 bytes) */ +#define TYPALIGN_DOUBLE 'd' /* double alignment (often 8 bytes) */ + /* Is a type OID a polymorphic pseudotype? (Beware of multiple evaluation) */ #define IsPolymorphicType(typid) \ ((typid) == ANYELEMENTOID || \ diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_918.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_918.sql new file mode 100644 index 0000000000..ae65f6199d --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_918.sql @@ -0,0 +1,16 @@ +DO $$ +DECLARE +ans boolean; +BEGIN + select case when count(*)=1 then true else false end as ans from (select * from pg_type where typname = 'jsonb' limit 1) into ans; + if ans = true then + DROP FUNCTION IF EXISTS pg_catalog.jsonb_insert(jsonb, text[], jsonb, boolean) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_set(jsonb, text[], jsonb, boolean) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, int) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text[]) CASCADE; + DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, integer) CASCADE; + DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text) CASCADE; + DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text[]) CASCADE; + end if; +END$$; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_918.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_918.sql new file mode 100644 index 0000000000..ae65f6199d --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_otherdb_92_918.sql @@ -0,0 +1,16 @@ +DO $$ +DECLARE +ans boolean; +BEGIN + select case when count(*)=1 then true else false end as ans from (select * from pg_type where typname = 'jsonb' limit 1) into ans; + if ans = true then + DROP FUNCTION IF EXISTS pg_catalog.jsonb_insert(jsonb, text[], jsonb, boolean) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_set(jsonb, text[], jsonb, boolean) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, int) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text) CASCADE; + DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text[]) CASCADE; + DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, integer) CASCADE; + DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text) CASCADE; + DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text[]) CASCADE; + end if; +END$$; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_918.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_918.sql new file mode 100644 index 0000000000..af709e25c9 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_918.sql @@ -0,0 +1,41 @@ +DROP FUNCTION IF EXISTS pg_catalog.jsonb_insert(jsonb, text[], jsonb, boolean) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5610; +CREATE FUNCTION pg_catalog.jsonb_insert ( +jsonb, text[], jsonb, boolean DEFAULT false +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_insert'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_set(jsonb, text[], jsonb, boolean) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5611; +CREATE FUNCTION pg_catalog.jsonb_set( +jsonb, text[], jsonb, boolean DEFAULT false +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_set'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, int) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5612; +CREATE FUNCTION pg_catalog.jsonb_delete ( +jsonb, int +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_delete_idx'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5613; +CREATE FUNCTION pg_catalog.jsonb_delete ( +jsonb, text +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_delete'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text[]) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5614; +CREATE FUNCTION pg_catalog.jsonb_delete ( +jsonb, text[] +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_delete_array'; + +DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_GENERAL, 3252; +CREATE OPERATOR pg_catalog.-(LEFTARG = jsonb, RIGHTARG = text, PROCEDURE = jsonb_delete); + +DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, int) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_GENERAL, 3251; +CREATE OPERATOR pg_catalog.-(LEFTARG = jsonb, RIGHTARG = int, PROCEDURE = jsonb_delete); + +DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text[]) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_GENERAL, 3253; +CREATE OPERATOR pg_catalog.-(LEFTARG = jsonb, RIGHTARG = text[], PROCEDURE = jsonb_delete); \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_918.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_918.sql new file mode 100644 index 0000000000..af709e25c9 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_918.sql @@ -0,0 +1,41 @@ +DROP FUNCTION IF EXISTS pg_catalog.jsonb_insert(jsonb, text[], jsonb, boolean) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5610; +CREATE FUNCTION pg_catalog.jsonb_insert ( +jsonb, text[], jsonb, boolean DEFAULT false +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_insert'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_set(jsonb, text[], jsonb, boolean) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5611; +CREATE FUNCTION pg_catalog.jsonb_set( +jsonb, text[], jsonb, boolean DEFAULT false +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_set'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, int) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5612; +CREATE FUNCTION pg_catalog.jsonb_delete ( +jsonb, int +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_delete_idx'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5613; +CREATE FUNCTION pg_catalog.jsonb_delete ( +jsonb, text +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_delete'; + +DROP FUNCTION IF EXISTS pg_catalog.jsonb_delete(jsonb, text[]) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_PROC, 5614; +CREATE FUNCTION pg_catalog.jsonb_delete ( +jsonb, text[] +) RETURNS jsonb LANGUAGE INTERNAL IMMUTABLE STRICT as 'jsonb_delete_array'; + +DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_GENERAL, 3252; +CREATE OPERATOR pg_catalog.-(LEFTARG = jsonb, RIGHTARG = text, PROCEDURE = jsonb_delete); + +DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, int) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_GENERAL, 3251; +CREATE OPERATOR pg_catalog.-(LEFTARG = jsonb, RIGHTARG = int, PROCEDURE = jsonb_delete); + +DROP OPERATOR IF EXISTS pg_catalog.-(jsonb, text[]) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_GENERAL, 3253; +CREATE OPERATOR pg_catalog.-(LEFTARG = jsonb, RIGHTARG = text[], PROCEDURE = jsonb_delete); \ No newline at end of file diff --git a/src/include/utils/json.h b/src/include/utils/json.h index 8903494396..70a354f1c3 100644 --- a/src/include/utils/json.h +++ b/src/include/utils/json.h @@ -79,5 +79,10 @@ extern Datum jsonb_array_elements_text(PG_FUNCTION_ARGS); extern Datum jsonb_array_elements(PG_FUNCTION_ARGS); extern Datum jsonb_populate_record(PG_FUNCTION_ARGS); extern Datum jsonb_populate_recordset(PG_FUNCTION_ARGS); +extern Datum jsonb_insert(PG_FUNCTION_ARGS); +extern Datum jsonb_set(PG_FUNCTION_ARGS); +extern Datum jsonb_delete(PG_FUNCTION_ARGS); +extern Datum jsonb_delete_idx(PG_FUNCTION_ARGS); +extern Datum jsonb_delete_array(PG_FUNCTION_ARGS); #endif /* JSON_H */ diff --git a/src/test/regress/expected/json_and_jsonb.out b/src/test/regress/expected/json_and_jsonb.out index d5ebea1ac7..5a6411fbe7 100644 --- a/src/test/regress/expected/json_and_jsonb.out +++ b/src/test/regress/expected/json_and_jsonb.out @@ -92,8 +92,13 @@ select oid,* from pg_proc where proname like '%json%' order by oid; 3949 | json_array_element | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 2 | 0 | 114 | 114 23 | | | | | json_array_element | | | | | f | f | f | f | | 0 | f | | | 114 23 | 4039 | jsonb_lt | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 2 | 0 | 16 | 3802 3802 | | | | | jsonb_lt | | | | | f | f | f | f | | 0 | f | | | 3802 3802 | 4050 | jsonb_contained | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 2 | 0 | 16 | 3802 3802 | | | | | jsonb_contained | | | | | f | f | f | f | | 0 | f | | | 3802 3802 | + 5610 | jsonb_insert | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 4 | 1 | 3802 | 3802 1009 3802 16 | | | | ({CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :ismaxvalue false :location 72803 :constvalue 1 [ 0 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno 0 :is_open false :found false :not_found false :null_open false :null_fetch false}) | jsonb_insert | | | | 3 | f | f | f | f | | 0 | f | | | 3802 1009 3802 16 | + 5611 | jsonb_set | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 4 | 1 | 3802 | 3802 1009 3802 16 | | | | ({CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :ismaxvalue false :location 72803 :constvalue 1 [ 0 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno 0 :is_open false :found false :not_found false :null_open false :null_fetch false}) | jsonb_set | | | | 3 | f | f | f | f | | 0 | f | | | 3802 1009 3802 16 | + 5612 | jsonb_delete | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 2 | 0 | 3802 | 3802 23 | | | | | jsonb_delete_idx | | | | | f | f | f | f | | 0 | f | | | 3802 23 | + 5613 | jsonb_delete | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 2 | 0 | 3802 | 3802 25 | | | | | jsonb_delete | | | | | f | f | f | f | | 0 | f | | | 3802 25 | + 5614 | jsonb_delete | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | t | f | i | 2 | 0 | 3802 | 3802 1009 | | | | | jsonb_delete_array | | | | | f | f | f | f | | 0 | f | | | 3802 1009 | 5719 | capture_view_to_json | 11 | 10 | 12 | 1 | 0 | 0 | - | f | f | f | f | f | f | v | 2 | 0 | 23 | 25 23 | | {i,i} | {view_name,is_all_db} | | capture_view_to_json | | | | | f | f | f | f | | 0 | f | | | 25 23 | -(84 rows) +(89 rows) select * from pg_aggregate where aggfnoid in (3124, 3403) order by aggfnoid; aggfnoid | aggtransfn | aggcollectfn | aggfinalfn | aggsortop | aggtranstype | agginitval | agginitcollect | aggkind | aggnumdirectargs @@ -120,6 +125,9 @@ select oid, * from pg_operator where oprleft in (114, 3802) order by oid; 3248 | ?| | 11 | 10 | b | f | f | 3802 | 1009 | 16 | 0 | 0 | jsonb_exists_any | contsel | contjoinsel 3249 | ?& | 11 | 10 | b | f | f | 3802 | 1009 | 16 | 0 | 0 | jsonb_exists_all | contsel | contjoinsel 3250 | <@ | 11 | 10 | b | f | f | 3802 | 3802 | 16 | 0 | 3246 | jsonb_contained | contsel | contjoinsel + 3251 | - | 11 | 10 | b | f | f | 3802 | 23 | 3802 | 0 | 0 | pg_catalog.jsonb_delete | - | - + 3252 | - | 11 | 10 | b | f | f | 3802 | 25 | 3802 | 0 | 0 | pg_catalog.jsonb_delete | - | - + 3253 | - | 11 | 10 | b | f | f | 3802 | 1009 | 3802 | 0 | 0 | pg_catalog.jsonb_delete | - | - 3477 | ->> | 11 | 10 | b | f | f | 3802 | 25 | 25 | 0 | 0 | jsonb_object_field_text | - | - 3481 | ->> | 11 | 10 | b | f | f | 3802 | 23 | 25 | 0 | 0 | jsonb_array_element_text | - | - 3962 | -> | 11 | 10 | b | f | f | 114 | 25 | 114 | 0 | 0 | json_object_field | - | - @@ -128,7 +136,7 @@ select oid, * from pg_operator where oprleft in (114, 3802) order by oid; 3965 | ->> | 11 | 10 | b | f | f | 114 | 23 | 25 | 0 | 0 | json_array_element_text | - | - 3966 | #> | 11 | 10 | b | f | f | 114 | 1009 | 114 | 0 | 0 | json_extract_path_op | - | - 3967 | #>> | 11 | 10 | b | f | f | 114 | 1009 | 25 | 0 | 0 | json_extract_path_text_op | - | - -(23 rows) +(26 rows) select oid, * from pg_opfamily where oid in (4033, 4034, 4035, 4036, 4037) order by oid; oid | opfmethod | opfname | opfnamespace | opfowner diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 364f33b83e..8b0cedb7e2 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -2091,3 +2091,171 @@ SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'e'; f (1 row) +-- jsonb_insert +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"'); + jsonb_insert +------------------------------- + {"a": [0, "new_value", 1, 2]} +(1 row) + +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true); + jsonb_insert +------------------------------- + {"a": [0, 1, "new_value", 2]} +(1 row) + +select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"'); + jsonb_insert +------------------------------------------------------------ + {"a": {"b": {"c": [0, 1, "new_value", "test1", "test2"]}}} +(1 row) + +select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"', true); + jsonb_insert +------------------------------------------------------------ + {"a": {"b": {"c": [0, 1, "test1", "new_value", "test2"]}}} +(1 row) + +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '{"b": "value"}'); + jsonb_insert +------------------ + {"a": [0, 1, 2]} +(1 row) + +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '["value1", "value2"]'); + jsonb_insert +------------------ + {"a": [0, 1, 2]} +(1 row) + +-- jsonb_set +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]'); + jsonb_set +---------------------------------------------------------- + {"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]'); + jsonb_set +------------------------------------------------------------------ + {"a": 1, "b": [1], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]'); + jsonb_set +------------------------------------------------------------------ + {"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [3]}, "n": null} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]'); +ERROR: path element at position 2 is null +CONTEXT: referenced column: jsonb_set +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}'); + jsonb_set +---------------------------------------------------------- + {"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}'); + jsonb_set +------------------------------------------------------------------ + {"a": 1, "b": [1], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}'); + jsonb_set +------------------------------------------------------------------ + {"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [3]}, "n": null} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}'); +ERROR: path element at position 2 is null +CONTEXT: referenced column: jsonb_set +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"'); + jsonb_set +-------------------------------------------------------------------------- + {"a": 1, "b": [1, "test"], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null} +(1 row) + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}'); + jsonb_set +------------------------------------------------------------------ + {"a": 1, "b": [1], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null} +(1 row) + +-- prepend to array +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,-33}','{"foo":123}'); + jsonb_set +----------------------------------------- + {"a": 1, "b": [0, 1, 2], "c": {"d": 4}} +(1 row) + +-- append to array +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,33}','{"foo":123}'); + jsonb_set +----------------------------------------- + {"a": 1, "b": [0, 1, 2], "c": {"d": 4}} +(1 row) + +-- check nesting levels addition +select jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}','{b,2,33}','{"foo":123}'); + jsonb_set +------------------------------------------------------- + {"a": 1, "b": [4, 5, [0, 1, 2], 6, 7], "c": {"d": 4}} +(1 row) + +-- add new key +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{c,e}','{"foo":123}'); + jsonb_set +----------------------------------------- + {"a": 1, "b": [0, 1, 2], "c": {"d": 4}} +(1 row) + +-- adding doesn't do anything if elements before last aren't present +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,-33}','{"foo":123}'); + jsonb_set +----------------------------------------- + {"a": 1, "b": [0, 1, 2], "c": {"d": 4}} +(1 row) + +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,y}','{"foo":123}'); + jsonb_set +----------------------------------------- + {"a": 1, "b": [0, 1, 2], "c": {"d": 4}} +(1 row) + +-- add to empty object +select jsonb_set('{}','{x}','{"foo":123}'); + jsonb_set +----------- + {} +(1 row) + +--add to empty array +select jsonb_set('[]','{0}','{"foo":123}'); + jsonb_set +----------- + [] +(1 row) + +select jsonb_set('[]','{99}','{"foo":123}'); + jsonb_set +----------- + [] +(1 row) + +select jsonb_set('[]','{-99}','{"foo":123}'); + jsonb_set +----------- + [] +(1 row) + +select jsonb_set('{"a": [1, 2, 3]}', '{a, non_integer}', '"new_value"'); +ERROR: path element at position 2 is not an integer: "non_integer" +CONTEXT: referenced column: jsonb_set +select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"'); +ERROR: path element at position 3 is not an integer: "non_integer" +CONTEXT: referenced column: jsonb_set +select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"'); +ERROR: path element at position 3 is null +CONTEXT: referenced column: jsonb_set diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index b509410a70..3a64dc3981 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -478,3 +478,46 @@ SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'b'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'c'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'd'; SELECT '{"n":null,"a":1,"b":[1,2],"c":{"1":2},"d":{"1":[2,3]}}'::jsonb ? 'e'; + +-- jsonb_insert +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"'); +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true); +select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"'); +select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"', true); +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '{"b": "value"}'); +select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '["value1", "value2"]'); + +-- jsonb_set +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]'); + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}'); + +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"'); +select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}'); + +-- prepend to array +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,-33}','{"foo":123}'); +-- append to array +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,33}','{"foo":123}'); +-- check nesting levels addition +select jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}','{b,2,33}','{"foo":123}'); +-- add new key +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{c,e}','{"foo":123}'); +-- adding doesn't do anything if elements before last aren't present +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,-33}','{"foo":123}'); +select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,y}','{"foo":123}'); +-- add to empty object +select jsonb_set('{}','{x}','{"foo":123}'); +--add to empty array +select jsonb_set('[]','{0}','{"foo":123}'); +select jsonb_set('[]','{99}','{"foo":123}'); +select jsonb_set('[]','{-99}','{"foo":123}'); +select jsonb_set('{"a": [1, 2, 3]}', '{a, non_integer}', '"new_value"'); +select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"'); +select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"'); \ No newline at end of file -- Gitee From f1fb01070a30cf7fcabd162ed8dcc58d03a731bc Mon Sep 17 00:00:00 2001 From: nnuanyang Date: Mon, 20 Nov 2023 22:52:00 -0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/utils/adt/jsonfuncs.cpp | 1071 +++++++++----------- 1 file changed, 495 insertions(+), 576 deletions(-) diff --git a/src/common/backend/utils/adt/jsonfuncs.cpp b/src/common/backend/utils/adt/jsonfuncs.cpp index 5905aa862e..5fb854b344 100644 --- a/src/common/backend/utils/adt/jsonfuncs.cpp +++ b/src/common/backend/utils/adt/jsonfuncs.cpp @@ -36,15 +36,14 @@ #include "utils/array.h" /* Operations available for setPath */ -#define JB_PATH_CREATE 0x0001 -#define JB_PATH_DELETE 0x0002 -#define JB_PATH_REPLACE 0x0004 -#define JB_PATH_INSERT_BEFORE 0x0008 -#define JB_PATH_INSERT_AFTER 0x0010 -#define JB_PATH_CREATE_OR_INSERT \ - (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE) -#define JB_PATH_FILL_GAPS 0x0020 -#define JB_PATH_CONSISTENT_POSITION 0x0040 +#define JB_PATH_CREATE 0x0001 +#define JB_PATH_DELETE 0x0002 +#define JB_PATH_REPLACE 0x0004 +#define JB_PATH_INSERT_BEFORE 0x0008 +#define JB_PATH_INSERT_AFTER 0x0010 +#define JB_PATH_CREATE_OR_INSERT (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE) +#define JB_PATH_FILL_GAPS 0x0020 +#define JB_PATH_CONSISTENT_POSITION 0x0040 /* semantic action functions for json_object_keys */ static void okeys_object_field_start(void *state, char *fname, bool isnull); @@ -119,18 +118,12 @@ static JsonbValue *findJsonbValueFromSuperHeaderLen(JsonbSuperHeader sheader, ui /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */ static void addJsonbToParseState(JsonbParseState **pstate, Jsonb *jb); -static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, - bool *path_nulls, int path_len, - JsonbParseState **st, int level, Jsonb *newval, - int op_type); -static void setPathObject(JsonbIterator **it, Datum *path_elems, - bool *path_nulls, int path_len, JsonbParseState **st, - int level, - Jsonb *newval, uint32 npairs, int op_type); -static void setPathArray(JsonbIterator **it, Datum *path_elems, - bool *path_nulls, int path_len, JsonbParseState **st, - int level, - Jsonb *newval, uint32 nelems, int op_type); +static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, + JsonbParseState **st, int level, Jsonb *newval, int op_type); +static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, + int level, Jsonb *newval, uint32 npairs, int op_type); +static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, + int level, Jsonb *newval, uint32 nelems, int op_type); /* search type classification for json_get* functions */ typedef enum { @@ -2786,629 +2779,555 @@ addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb) * All path elements before the last must already exist * whatever bits in op_type are set, or nothing is done. */ -static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, - bool *path_nulls, int path_len, - JsonbParseState **st, int level, Jsonb *newval, int op_type) -{ - JsonbValue v; - int r; - JsonbValue *res; - - check_stack_depth(); - - if (path_nulls[level]) - ereport(ERROR, - (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("path element at position %d is null", - level + 1))); - - r = JsonbIteratorNext(it, &v, false); - - switch (r) - { - case WJB_BEGIN_ARRAY: - - /* - * If instructed complain about attempts to replace within a raw - * scalar value. This happens even when current level is equal to - * path_len, because the last path key should also correspond to - * an object or an array, not raw scalar. - */ - if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1) && - v.array.rawScalar) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot replace existing key"), - errdetail("The path assumes key is a composite object, " - "but it is a scalar value."))); - - (void) pushJsonbValue(st, r, NULL); - setPathArray(it, path_elems, path_nulls, path_len, st, level, - newval, v.array.nElems, op_type); - r = JsonbIteratorNext(it, &v, false); - Assert(r == WJB_END_ARRAY); - res = pushJsonbValue(st, r, NULL); - break; - case WJB_BEGIN_OBJECT: - (void) pushJsonbValue(st, r, NULL); - setPathObject(it, path_elems, path_nulls, path_len, st, level, - newval, v.object.nPairs, op_type); - r = JsonbIteratorNext(it, &v, true); - Assert(r == WJB_END_OBJECT); - res = pushJsonbValue(st, r, NULL); - break; - case WJB_ELEM: - case WJB_VALUE: - - /* - * If instructed complain about attempts to replace within a - * scalar value. This happens even when current level is equal to - * path_len, because the last path key should also correspond to - * an object or an array, not an element or value. - */ - if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot replace existing key"), - errdetail("The path assumes key is a composite object, " - "but it is a scalar value."))); - - res = pushJsonbValue(st, r, &v); - break; - default: - elog(ERROR, "unrecognized iterator result: %d", (int) r); - res = NULL; /* keep compiler quiet */ - break; - } - - return res; +static JsonbValue* setPath(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, + JsonbParseState **st, int level, Jsonb *newval, int op_type) +{ + JsonbValue v; + int r; + JsonbValue *res; + + check_stack_depth(); + + if (path_nulls[level]) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("path element at position %d is null", level + 1))); + + r = JsonbIteratorNext(it, &v, false); + + switch (r) { + case WJB_BEGIN_ARRAY: + + /* + * If instructed complain about attempts to replace within a raw + * scalar value. This happens even when current level is equal to + * path_len, because the last path key should also correspond to + * an object or an array, not raw scalar. + */ + if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1) && v.array.rawScalar) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot replace existing key"), + errdetail("The path assumes key is a composite object, " + "but it is a scalar value."))); + + (void) pushJsonbValue(st, r, NULL); + setPathArray(it, path_elems, path_nulls, path_len, st, level, newval, v.array.nElems, op_type); + r = JsonbIteratorNext(it, &v, false); + Assert(r == WJB_END_ARRAY); + res = pushJsonbValue(st, r, NULL); + break; + case WJB_BEGIN_OBJECT: + (void) pushJsonbValue(st, r, NULL); + setPathObject(it, path_elems, path_nulls, path_len, st, level, newval, v.object.nPairs, op_type); + r = JsonbIteratorNext(it, &v, true); + Assert(r == WJB_END_OBJECT); + res = pushJsonbValue(st, r, NULL); + break; + case WJB_ELEM: + case WJB_VALUE: + + /* + * If instructed complain about attempts to replace within a + * scalar value. This happens even when current level is equal to + * path_len, because the last path key should also correspond to + * an object or an array, not an element or value. + */ + if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot replace existing key"), + errdetail("The path assumes key is a composite object, but it is a scalar value."))); + + res = pushJsonbValue(st, r, &v); + break; + default: + elog(ERROR, "unrecognized iterator result: %d", (int) r); + res = NULL; /* keep compiler quiet */ + break; + } + + return res; } /* * Object walker for setPath */ static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, - int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 npairs, int op_type) -{ - text *pathelem = NULL; - int i; - JsonbValue k, - v; - bool done = false; - - if (level >= path_len || path_nulls[level]) - done = true; - else - { - /* The path Datum could be toasted, in which case we must detoast it */ - pathelem = DatumGetTextPP(path_elems[level]); - } - - /* empty object is a special case for create */ - if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) && - (level == path_len - 1)) - { - JsonbValue newkey; - - newkey.type = jbvString; - newkey.string.val = VARDATA_ANY(pathelem); - newkey.string.len = VARSIZE_ANY_EXHDR(pathelem); - - (void) pushJsonbValue(st, WJB_KEY, &newkey); - addJsonbToParseState(st, newval); - } - - for (i = 0; i < npairs; i++) - { - int r = JsonbIteratorNext(it, &k, true); - - Assert(r == WJB_KEY); - - if (!done && - k.string.len == VARSIZE_ANY_EXHDR(pathelem) && - memcmp(k.string.val, VARDATA_ANY(pathelem), - k.string.len) == 0) - { - done = true; - - if (level == path_len - 1) - { - /* - * called from jsonb_insert(), it forbids redefining an - * existing value - */ - if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot replace existing key"), - errhint("Try using the function jsonb_set " - "to replace key value."))); - - r = JsonbIteratorNext(it, &v, true); /* skip value */ - if (!(op_type & JB_PATH_DELETE)) - { - (void) pushJsonbValue(st, WJB_KEY, &k); - addJsonbToParseState(st, newval); - } - } - else - { - (void) pushJsonbValue(st, r, &k); - setPath(it, path_elems, path_nulls, path_len, - st, level + 1, newval, op_type); - } - } - else - { - if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && - level == path_len - 1 && i == npairs - 1) - { - JsonbValue newkey; - - newkey.type = jbvString; - newkey.string.val = VARDATA_ANY(pathelem); - newkey.string.len = VARSIZE_ANY_EXHDR(pathelem); - - (void) pushJsonbValue(st, WJB_KEY, &newkey); - addJsonbToParseState(st, newval); - } - - (void) pushJsonbValue(st, r, &k); - r = JsonbIteratorNext(it, &v, false); - (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) - { - int walking_level = 1; - - while (walking_level != 0) - { - r = JsonbIteratorNext(it, &v, false); - - if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) - ++walking_level; - if (r == WJB_END_ARRAY || r == WJB_END_OBJECT) - --walking_level; - - (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - } - } - } - } + int path_len, JsonbParseState **st, int level, Jsonb *newval, uint32 npairs, int op_type) +{ + text *pathelem = NULL; + int i; + JsonbValue k, v; + bool done = false; + + if (level >= path_len || path_nulls[level]) { + done = true; + } else { + /* The path Datum could be toasted, in which case we must detoast it */ + pathelem = DatumGetTextPP(path_elems[level]); + } + + /* empty object is a special case for create */ + if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) && (level == path_len - 1)) { + JsonbValue newkey; + + newkey.type = jbvString; + newkey.string.val = VARDATA_ANY(pathelem); + newkey.string.len = VARSIZE_ANY_EXHDR(pathelem); + + (void) pushJsonbValue(st, WJB_KEY, &newkey); + addJsonbToParseState(st, newval); + } + + for (i = 0; i < npairs; i++) { + int r = JsonbIteratorNext(it, &k, true); + + Assert(r == WJB_KEY); + + if (!done && k.string.len == VARSIZE_ANY_EXHDR(pathelem) && + memcmp(k.string.val, VARDATA_ANY(pathelem), k.string.len) == 0) { + done = true; + + if (level == path_len - 1) { + /* + * called from jsonb_insert(), it forbids redefining an + * existing value + */ + if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot replace existing key"), + errhint("Try using the function jsonb_set " + "to replace key value."))); + + r = JsonbIteratorNext(it, &v, true); /* skip value */ + if (!(op_type & JB_PATH_DELETE)) { + (void) pushJsonbValue(st, WJB_KEY, &k); + addJsonbToParseState(st, newval); + } + } else { + (void) pushJsonbValue(st, r, &k); + setPath(it, path_elems, path_nulls, path_len, + st, level + 1, newval, op_type); + } + } else { + if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && level == path_len - 1 && i == npairs - 1) { + JsonbValue newkey; + + newkey.type = jbvString; + newkey.string.val = VARDATA_ANY(pathelem); + newkey.string.len = VARSIZE_ANY_EXHDR(pathelem); + + (void) pushJsonbValue(st, WJB_KEY, &newkey); + addJsonbToParseState(st, newval); + } + + (void) pushJsonbValue(st, r, &k); + r = JsonbIteratorNext(it, &v, false); + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) { + int walking_level = 1; + + while (walking_level != 0) { + r = JsonbIteratorNext(it, &v, false); + + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) + ++walking_level; + if (r == WJB_END_ARRAY || r == WJB_END_OBJECT) + --walking_level; + + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + } + } + } } /* - * Array walker for setPath - */ +* Array walker for setPath +*/ static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, - int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 nelems, int op_type) -{ - JsonbValue v; - int idx, - i; - bool done = false; - - /* pick correct index */ - if (level < path_len && !path_nulls[level]) - { - char *c = TextDatumGetCString(path_elems[level]); - char *badp; - long val; - - errno = 0; - val = strtol(c, &badp, 10); - if (errno != 0 || badp == c || badp[0] != '\0' || val > INT_MAX || - val < INT_MIN) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), - errmsg("path element at position %d is not an integer: \"%s\"", - level + 1, c))); - idx = val; - } - else - idx = nelems; - - if (idx < 0) - { - if (-idx > nelems) - { - /* - * If asked to keep elements position consistent, it's not allowed - * to prepend the array. - */ - if (op_type & JB_PATH_CONSISTENT_POSITION) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("path element at position %d is out of range: %d", - level + 1, idx))); - else - idx = INT_MIN; - } - else - idx = nelems + idx; - } - - /* - * Filling the gaps means there are no limits on the positive index are - * imposed, we can set any element. Otherwise limit the index by nelems. - */ - if (!(op_type & JB_PATH_FILL_GAPS)) - { - if (idx > 0 && idx > nelems) - idx = nelems; - } - - /* - * if we're creating, and idx == INT_MIN, we prepend the new value to the - * array also if the array is empty - in which case we don't really care - * what the idx value is - */ - if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) && - (op_type & JB_PATH_CREATE_OR_INSERT)) - { - Assert(newval != NULL); + int path_len, JsonbParseState **st, int level, Jsonb *newval, uint32 nelems, int op_type) +{ + JsonbValue v; + int idx, + i; + bool done = false; + + /* pick correct index */ + if (level < path_len && !path_nulls[level]) { + char *c = TextDatumGetCString(path_elems[level]); + char *badp; + long val; + + errno = 0; + val = strtol(c, &badp, 10); + if (errno != 0 || badp == c || badp[0] != '\0' || val > INT_MAX || + val < INT_MIN) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("path element at position %d is not an integer: \"%s\"", + level + 1, c))); + idx = val; + } else + idx = nelems; + + if (idx < 0) { + if (-idx > nelems) { + /* + * If asked to keep elements position consistent, it's not allowed + * to prepend the array. + */ + if (op_type & JB_PATH_CONSISTENT_POSITION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("path element at position %d is out of range: %d", + level + 1, idx))); + else + idx = INT_MIN; + } else + idx = nelems + idx; + } + + /* + * Filling the gaps means there are no limits on the positive index are + * imposed, we can set any element. Otherwise limit the index by nelems. + */ + if (!(op_type & JB_PATH_FILL_GAPS)) { + if (idx > 0 && idx > nelems) + idx = nelems; + } + + /* + * if we're creating, and idx == INT_MIN, we prepend the new value to the + * array also if the array is empty - in which case we don't really care + * what the idx value is + */ + if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) && (op_type & JB_PATH_CREATE_OR_INSERT)) { + Assert(newval != NULL); addJsonbToParseState(st, newval); - done = true; - } - - /* iterate over the array elements */ - for (i = 0; i < nelems; i++) - { - int r; - - if (i == idx && level < path_len) - { - done = true; - - if (level == path_len - 1) - { - r = JsonbIteratorNext(it, &v, true); /* skip */ - - if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE)) - addJsonbToParseState(st, newval); - - /* - * We should keep current value only in case of - * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER because - * otherwise it should be deleted or replaced - */ - if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE)) - (void) pushJsonbValue(st, r, &v); - - if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE)) - addJsonbToParseState(st, newval); - } - else - (void) setPath(it, path_elems, path_nulls, path_len, - st, level + 1, newval, op_type); - } - else - { - r = JsonbIteratorNext(it, &v, false); - - (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - - if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) - { - int walking_level = 1; - - while (walking_level != 0) - { - r = JsonbIteratorNext(it, &v, false); - - if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) - ++walking_level; - if (r == WJB_END_ARRAY || r == WJB_END_OBJECT) - --walking_level; - - (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - } - } - } - } - - if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && level == path_len - 1) - { - /* - * If asked to fill the gaps, idx could be bigger than nelems, so - * prepend the new element with nulls if that's the case. - */ - if (op_type & JB_PATH_FILL_GAPS && idx > nelems) - push_null_elements(st, idx - nelems); - - addJsonbToParseState(st, newval); - done = true; - } + done = true; + } + + /* iterate over the array elements */ + for (i = 0; i < nelems; i++) { + int r; + + if (i == idx && level < path_len) { + done = true; + + if (level == path_len - 1) { + r = JsonbIteratorNext(it, &v, true); /* skip */ + + if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE)) + addJsonbToParseState(st, newval); + + /* + * We should keep current value only in case of + * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER because + * otherwise it should be deleted or replaced + */ + if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE)) + (void) pushJsonbValue(st, r, &v); + + if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE)) + addJsonbToParseState(st, newval); + } else + (void) setPath(it, path_elems, path_nulls, path_len, st, level + 1, newval, op_type); + } else { + r = JsonbIteratorNext(it, &v, false); + + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) { + int walking_level = 1; + + while (walking_level != 0) { + r = JsonbIteratorNext(it, &v, false); + + if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT) + ++walking_level; + if (r == WJB_END_ARRAY || r == WJB_END_OBJECT) + --walking_level; + + (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + } + } + } + + if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && level == path_len - 1) { + /* + * If asked to fill the gaps, idx could be bigger than nelems, so + * prepend the new element with nulls if that's the case. + */ + if (op_type & JB_PATH_FILL_GAPS && idx > nelems) + push_null_elements(st, idx - nelems); + + addJsonbToParseState(st, newval); + done = true; + } } /* - * SQL function jsonb_insert(jsonb, text[], jsonb, boolean) - */ +* SQL function jsonb_insert(jsonb, text[], jsonb, boolean) +*/ Datum jsonb_insert(PG_FUNCTION_ARGS) { - Jsonb *in = PG_GETARG_JSONB(0); - ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); - Jsonb *newjsonb = PG_GETARG_JSONB(2); - bool after = PG_GETARG_BOOL(3); - JsonbValue *res = NULL; - Datum *path_elems; - bool *path_nulls; - int path_len; - JsonbIterator *it; - JsonbParseState *st = NULL; + Jsonb *in = PG_GETARG_JSONB(0); + ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *newjsonb = PG_GETARG_JSONB(2); + bool after = 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 (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_IS_SCALAR(in)) + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot set path in scalar"))); - deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); + deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); - if (path_len == 0) - PG_RETURN_JSONB(in); + if (path_len == 0) + PG_RETURN_JSONB(in); - it = JsonbIteratorInit(VARDATA(in)); + it = JsonbIteratorInit(VARDATA(in)); - res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newjsonb, - after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE); + res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newjsonb, + after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE); - Assert(res != NULL); + Assert(res != NULL); - PG_RETURN_JSONB(JsonbValueToJsonb(res)); + PG_RETURN_JSONB(JsonbValueToJsonb(res)); } /* - * SQL function jsonb_delete (jsonb, text) - * - * return a copy of the jsonb with the indicated item - * 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; - JsonbValue v, - *res = NULL; - bool skipNested = false; - int r; +* SQL function jsonb_delete (jsonb, text) +* +* return a copy of the jsonb with the indicated item +* 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; + JsonbValue v, *res = NULL; + bool skipNested = false; + int r; - if (JB_ROOT_IS_SCALAR(in)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot delete from scalar"))); + 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); + if (JB_ROOT_COUNT(in) == 0) + PG_RETURN_JSONB(in); - it = JsonbIteratorInit(VARDATA(in)); + it = JsonbIteratorInit(VARDATA(in)); - while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) - { - skipNested = true; + while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) { + skipNested = true; - if ((r == WJB_ELEM || r == WJB_KEY) && - (v.type == jbvString && keylen == v.string.len && - memcmp(keyptr, v.string.val, keylen) == 0)) - { - /* skip corresponding value as well */ - if (r == WJB_KEY) - (void) JsonbIteratorNext(&it, &v, true); + if ((r == WJB_ELEM || r == WJB_KEY) && (v.type == jbvString && keylen == v.string.len && + memcmp(keyptr, v.string.val, keylen) == 0)) { + /* skip corresponding value as well */ + if (r == WJB_KEY) + (void) JsonbIteratorNext(&it, &v, true); - continue; - } + continue; + } - res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - } + res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } - Assert(res != NULL); + Assert(res != NULL); - PG_RETURN_JSONB(JsonbValueToJsonb(res)); + PG_RETURN_JSONB(JsonbValueToJsonb(res)); } /* - * SQL function jsonb_delete (jsonb, variadic text[]) - * - * return a copy of the jsonb with the indicated items - * removed. - */ +* SQL function jsonb_delete (jsonb, variadic text[]) +* +* return a copy of the jsonb with the indicated items +* removed. +*/ Datum jsonb_delete_array(PG_FUNCTION_ARGS) { - Jsonb *in = PG_GETARG_JSONB(0); - ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); - Datum *keys_elems; - bool *keys_nulls; - int keys_len; - JsonbParseState *state = NULL; - JsonbIterator *it; - JsonbValue v, - *res = NULL; - bool skipNested = false; - int r; - - if (ARR_NDIM(keys) > 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 delete from scalar"))); - - if (JB_ROOT_COUNT(in) == 0) - PG_RETURN_JSONB(in); - - deconstruct_array(keys, TEXTOID, -1, false, 'i', &keys_elems, &keys_nulls, &keys_len); - - if (keys_len == 0) - PG_RETURN_JSONB(in); - - it = JsonbIteratorInit(VARDATA(in)); - - while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) - { - skipNested = true; - - if ((r == WJB_ELEM || r == WJB_KEY) && v.type == jbvString) - { - int i; - bool found = false; - - for (i = 0; i < keys_len; i++) - { - char *keyptr; - int keylen; - - if (keys_nulls[i]) - continue; - - /* We rely on the array elements not being toasted */ - keyptr = VARDATA_ANY(keys_elems[i]); - keylen = VARSIZE_ANY_EXHDR(keys_elems[i]); - if (keylen == v.string.len && - memcmp(keyptr, v.string.val, keylen) == 0) - { - found = true; - break; - } - } - if (found) - { - /* skip corresponding value as well */ - if (r == WJB_KEY) - (void) JsonbIteratorNext(&it, &v, true); - - continue; - } - } - - res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - } - - Assert(res != NULL); - - PG_RETURN_JSONB(JsonbValueToJsonb(res)); + Jsonb *in = PG_GETARG_JSONB(0); + ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); + Datum *keys_elems; + bool *keys_nulls; + int keys_len; + JsonbParseState *state = NULL; + JsonbIterator *it; + JsonbValue v, *res = NULL; + bool skipNested = false; + int r; + + if (ARR_NDIM(keys) > 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 delete from scalar"))); + + if (JB_ROOT_COUNT(in) == 0) + PG_RETURN_JSONB(in); + + deconstruct_array(keys, TEXTOID, -1, false, 'i', &keys_elems, &keys_nulls, &keys_len); + + if (keys_len == 0) + PG_RETURN_JSONB(in); + + it = JsonbIteratorInit(VARDATA(in)); + + while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) { + skipNested = true; + + if ((r == WJB_ELEM || r == WJB_KEY) && v.type == jbvString) { + int i; + bool found = false; + + for (i = 0; i < keys_len; i++) { + char *keyptr; + int keylen; + + if (keys_nulls[i]) + continue; + + /* We rely on the array elements not being toasted */ + keyptr = VARDATA_ANY(keys_elems[i]); + keylen = VARSIZE_ANY_EXHDR(keys_elems[i]); + if (keylen == v.string.len && + memcmp(keyptr, v.string.val, keylen) == 0) + { + found = true; + break; + } + } + if (found) { + /* skip corresponding value as well */ + if (r == WJB_KEY) + (void) JsonbIteratorNext(&it, &v, true); + + continue; + } + } + + res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } + + Assert(res != NULL); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); } /* - * SQL function jsonb_delete (jsonb, int) - * - * return a copy of the jsonb with the indicated item - * removed. Negative int means count back from the - * end of the items. - */ -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 i = 0, - n; - JsonbValue v, - *res = NULL; - int r; +* SQL function jsonb_delete (jsonb, int) +* +* return a copy of the jsonb with the indicated item +* removed. Negative int means count back from the +* end of the items. +*/ +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 i = 0, + n; + JsonbValue v, + *res = NULL; + int r; - if (JB_ROOT_IS_SCALAR(in)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot delete from scalar"))); + if (JB_ROOT_IS_SCALAR(in)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot delete from scalar"))); - if (JB_ROOT_IS_OBJECT(in)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot delete from object using integer index"))); + if (JB_ROOT_IS_OBJECT(in)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot delete from object using integer index"))); - if (JB_ROOT_COUNT(in) == 0) - PG_RETURN_JSONB(in); + if (JB_ROOT_COUNT(in) == 0) + PG_RETURN_JSONB(in); - it = JsonbIteratorInit(VARDATA(in)); + it = JsonbIteratorInit(VARDATA(in)); - r = JsonbIteratorNext(&it, &v, false); - Assert(r == WJB_BEGIN_ARRAY); - n = v.array.nElems; + r = JsonbIteratorNext(&it, &v, false); + Assert(r == WJB_BEGIN_ARRAY); + n = v.array.nElems; - if (idx < 0) - { - if (-idx > n) - idx = n; - else - idx = n + idx; - } + if (idx < 0) { + if (-idx > n) + idx = n; + else + idx = n + idx; + } - if (idx >= n) - PG_RETURN_JSONB(in); + if (idx >= n) + PG_RETURN_JSONB(in); - pushJsonbValue(&state, r, NULL); + pushJsonbValue(&state, r, NULL); - while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) - { - if (r == WJB_ELEM) - { - if (i++ == idx) - continue; - } + while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE) + { + if (r == WJB_ELEM) { + if (i++ == idx) + continue; + } - res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); - } + res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL); + } - Assert(res != NULL); + Assert(res != NULL); - PG_RETURN_JSONB(JsonbValueToJsonb(res)); + PG_RETURN_JSONB(JsonbValueToJsonb(res)); } /* - * SQL function jsonb_set(jsonb, text[], jsonb, boolean) - */ +* SQL function jsonb_set(jsonb, text[], jsonb, boolean) +*/ Datum jsonb_set(PG_FUNCTION_ARGS) { - Jsonb *in = PG_GETARG_JSONB(0); - ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); - Jsonb *newjsonb = 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; + Jsonb *in = PG_GETARG_JSONB(0); + ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); + Jsonb *newjsonb = 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 (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_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); + if (JB_ROOT_COUNT(in) == 0 && !create) + PG_RETURN_JSONB(in); - deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); + deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls, &path_len); - if (path_len == 0) - PG_RETURN_JSONB(in); + if (path_len == 0) + PG_RETURN_JSONB(in); - it = JsonbIteratorInit(VARDATA(in)); + it = JsonbIteratorInit(VARDATA(in)); - res = setPath(&it, path_elems, path_nulls, path_len, &st, - 0, newjsonb, create ? JB_PATH_CREATE : JB_PATH_REPLACE); + res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newjsonb, create ? JB_PATH_CREATE : JB_PATH_REPLACE); - Assert(res != NULL); + Assert(res != NULL); - PG_RETURN_JSONB(JsonbValueToJsonb(res)); + PG_RETURN_JSONB(JsonbValueToJsonb(res)); } -- Gitee