3 * Test jsonb delete and concatenate operator functions for 9.4
5 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994, Regents of the University of California
7 * Author: Glyn Astill <glyn@8kb.co.uk>
9 * This is purely experimentation and will contain many errors and bad form
10 * DO NOT USE ON PRODUCTION SYSTEMS.
15 #include "utils/jsonb.h"
16 #include "catalog/pg_type.h"
17 #include "utils/builtins.h"
18 #include "jsonb_opx.h"
21 pushJsonbBinary(JsonbParseState **pstate, JsonbContainer *jsonb_container)
23 JsonbIterator *jsonb_iterator;
24 JsonbValue jsonb_iterator_value;
25 int32 jsonb_iterator_token;
26 JsonbValue *return_jsonb_value = NULL;
28 jsonb_iterator = JsonbIteratorInit((void *)jsonb_container);
29 while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
31 return_jsonb_value = pushJsonbValueBlind(pstate, jsonb_iterator_token, &jsonb_iterator_value);
33 return return_jsonb_value;
37 pushJsonbValueBlind(JsonbParseState **pstate, JsonbIteratorToken jsonb_iterator_token, JsonbValue *jsonb_iterator_value)
39 JsonbValue *return_jsonb_value = NULL;
41 if ((jsonb_iterator_token == WJB_KEY) ||
42 (jsonb_iterator_token == WJB_VALUE) ||
43 (jsonb_iterator_token == WJB_ELEM))
45 return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, jsonb_iterator_value);
48 return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, NULL);
50 return return_jsonb_value;
54 jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
56 /* pointers to return jsonb value data and state to be converted to jsonb on return */
57 JsonbParseState *state = NULL;
58 JsonbValue *return_jsonb_value = NULL;
60 /* pointer to iterator for input jsonb */
61 JsonbIterator *jsonb_iterator;
62 JsonbValue jsonb_iterator_value;
63 int32 jsonb_iterator_token;
65 JsonbIterator *jsonb_replacement_iterator;
66 JsonbValue jsonb_replacement_iterator_value;
67 int32 jsonb_replacement_iterator_token;
70 * array element variables for use during deconstruction
71 * count is the depth we will be looking from the first matching key
77 /* the current key we are looking for, starting with the first key */
81 int32 array_level = 0;
83 int32 push_nest_level = 0;
86 /* assert input_array is a text array type */
87 Assert(ARR_ELEMTYPE(array_path) == TEXTOID);
89 /* check input_array is one-dimensional */
90 if (ARR_NDIM(array_path) > 1)
92 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
93 errmsg("1 dimensional text array expected")));
95 /* deconstruct array elements */
96 deconstruct_array(array_path, TEXTOID, -1, false, 'i',
97 &datums, &nulls, &count);
99 /* can't follow key based paths on non objects */
100 if (!JB_ROOT_IS_OBJECT(jsonb_a) && (count > 1))
102 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103 errmsg("cannot call with path deeper than 1 on a non-object")));
105 /* if the array is empty there's no work to do so return the input value */
106 if ((count == 0) || (JB_ROOT_COUNT(jsonb_a) == 0))
109 if (!nulls[index_on])
110 key_on = DatumGetTextP(datums[index_on]);
112 jsonb_iterator = JsonbIteratorInit(&jsonb_a->root);
114 while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
118 switch (jsonb_iterator_token)
120 case WJB_BEGIN_ARRAY:
123 case WJB_BEGIN_OBJECT:
129 * If we are not skipping the current nest level check that the nesting level follows the array index
130 * and if it does check the current key. For array elements only check the root level (array_level==1).
131 * If there is no match we just keep on pushing
133 if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level-1 == index_on && array_level == 0) ||
134 (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1)))
136 if ((jsonb_iterator_value.type == jbvNull && key_on == NULL) ||
137 (key_on != NULL && (jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(key_on)) &&
138 (memcmp(jsonb_iterator_value.val.string.val, VARDATA_ANY(key_on), jsonb_iterator_value.val.string.len) == 0)))
140 /* if we have not yet reached the last index in the array / key chain move on and check the next */
141 if (index_on < count-1)
144 if (!nulls[index_on])
145 key_on = DatumGetTextP(datums[index_on]);
149 /* if we have reached the last index, the we can modify this level */
152 /* if jsonb_b is not null unwrap it with iterator into replacement_jsonb_value */
153 if (jsonb_b != NULL) {
154 jsonb_replacement_iterator = JsonbIteratorInit(&jsonb_b->root);
156 /* if the replacement value is a scalar then it will replace the current element or value */
157 if (JB_ROOT_IS_SCALAR(jsonb_b))
159 jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false);
160 jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false);
162 if (jsonb_iterator_token == WJB_ELEM)
164 return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_replacement_iterator_value);
168 return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
169 return_jsonb_value = pushJsonbValue(&state, WJB_VALUE, &jsonb_replacement_iterator_value);
172 /* otherwise assume this is the replacement for the whole element /key-value pair */
174 while ((jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false)) != WJB_DONE)
176 if (push_nest_level == 0 && jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)
178 return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
181 if ((jsonb_replacement_iterator_token == WJB_BEGIN_OBJECT) || (jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY))
184 if ((push_nest_level > 1) ||
185 (jsonb_iterator_token == WJB_ELEM && jsonb_replacement_iterator_token != WJB_BEGIN_ARRAY && jsonb_replacement_iterator_token != WJB_END_ARRAY) ||
186 (jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token != WJB_BEGIN_OBJECT && jsonb_replacement_iterator_token != WJB_END_OBJECT))
188 return_jsonb_value = pushJsonbValueBlind(&state, jsonb_replacement_iterator_token, &jsonb_replacement_iterator_value);
191 if ((jsonb_replacement_iterator_token == WJB_END_OBJECT || jsonb_replacement_iterator_token == WJB_END_ARRAY))
194 Assert(push_nest_level >= 0);
198 if (jsonb_iterator_token == WJB_ELEM)
201 skip_level = nest_level;
207 if (push && (skip_level == 0 || nest_level < skip_level))
209 return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
212 switch (jsonb_iterator_token)
216 if (skip_level == nest_level && array_level == 0)
221 if (skip_level == nest_level && array_level == 0)
225 if (skip_level == nest_level)
230 if (return_jsonb_value->type == jbvArray)
232 if (return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 0)
233 return_jsonb_value->val.array.rawScalar = false;
234 else if (JB_ROOT_IS_SCALAR(jsonb_a) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
235 return_jsonb_value->val.array.rawScalar = true;
238 return JsonbValueToJsonb(return_jsonb_value);