]> git.8kb.co.uk Git - postgresql/pg_jsonb_opx/blob - jsonb_utilsx.c
Fix some logic when modifying scalars and simplify simple text deletion.
[postgresql/pg_jsonb_opx] / jsonb_utilsx.c
1 /*
2  * jsonb_utilsx.c
3  *     Test jsonb delete and concatenate operator functions for 9.4
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>
8  *
9  * This is purely experimentation and will contain many errors and bad form
10  * DO NOT USE ON PRODUCTION SYSTEMS.
11  *
12  */
13
14 #include "postgres.h"
15 #include "utils/jsonb.h"
16 #include "catalog/pg_type.h"
17 #include "utils/builtins.h"
18 #include "jsonb_opx.h"
19
20 JsonbValue * 
21 pushJsonbBinary(JsonbParseState **pstate, JsonbContainer *jsonb_container)
22 {
23     JsonbIterator *jsonb_iterator;
24     JsonbValue jsonb_iterator_value;
25     int32 jsonb_iterator_token;
26     JsonbValue *return_jsonb_value = NULL;
27
28     jsonb_iterator = JsonbIteratorInit((void *)jsonb_container);
29     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
30     {
31         return_jsonb_value = pushJsonbValueBlind(pstate, jsonb_iterator_token, &jsonb_iterator_value);
32     }
33     return return_jsonb_value;
34 }
35
36 JsonbValue *
37 pushJsonbValueBlind(JsonbParseState **pstate, JsonbIteratorToken jsonb_iterator_token, JsonbValue *jsonb_iterator_value)
38 {
39     JsonbValue *return_jsonb_value = NULL;
40
41     if ((jsonb_iterator_token == WJB_KEY) ||
42             (jsonb_iterator_token == WJB_VALUE) ||
43             (jsonb_iterator_token == WJB_ELEM))
44             {
45             return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, jsonb_iterator_value);
46             }
47         else
48             return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, NULL);
49
50     return return_jsonb_value;
51 }
52
53 Jsonb * 
54 jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
55 {
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;
59
60     /* pointer to iterator for input jsonb */
61     JsonbIterator *jsonb_iterator;
62     JsonbValue jsonb_iterator_value;
63     int32 jsonb_iterator_token;
64
65     JsonbIterator *jsonb_replacement_iterator;
66     JsonbValue jsonb_replacement_iterator_value;
67     int32 jsonb_replacement_iterator_token;
68     
69     /* 
70      * array element variables for use during deconstruction 
71      * count is the depth we will be looking from the first matching key
72      */
73     Datum *datums;
74     bool *nulls;
75     int32 count; 
76
77     /* the current key we are looking for, starting with the first key */
78     text *key_on = NULL;
79     int32 index_on = 0; 
80     int32 nest_level = 0;
81     int32 array_level = 0;
82     int32 skip_level = 0;
83     int32 push_nest_level = 0;
84     bool push = true;
85
86     /* assert input_array is a text array type */
87     Assert(ARR_ELEMTYPE(array_path) == TEXTOID);
88
89     /* check input_array is one-dimensional */
90     if (ARR_NDIM(array_path) > 1)
91         ereport(ERROR,
92             (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
93                 errmsg("1 dimensional text array expected")));
94
95     /* deconstruct array elements */
96     deconstruct_array(array_path, TEXTOID, -1, false, 'i',
97                         &datums, &nulls, &count);
98
99     /* can't follow key based paths on non objects */ 
100     if (!JB_ROOT_IS_OBJECT(jsonb_a) && (count > 1))
101         ereport(ERROR,
102             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
103                 errmsg("cannot call with path deeper than 1 on a non-object")));
104
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))
107         return jsonb_a;
108
109     if (!nulls[index_on])
110         key_on = DatumGetTextP(datums[index_on]);
111
112     jsonb_iterator = JsonbIteratorInit(&jsonb_a->root);
113
114     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
115     {    
116         push = true;
117
118         switch (jsonb_iterator_token)
119         {
120             case WJB_BEGIN_ARRAY:
121                 array_level++;
122             break;
123             case WJB_BEGIN_OBJECT:
124                nest_level++;
125             break;
126             case WJB_ELEM:
127             case WJB_KEY:
128                 /*
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
132                  */
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)))
135                 {
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)))
139                     {   
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) 
142                         {
143                             index_on++;
144                             if (!nulls[index_on])
145                                 key_on = DatumGetTextP(datums[index_on]);
146                             else
147                                 key_on = NULL;
148                         }
149                         /* if we have reached the last index, the we can modify this level */ 
150                         else 
151                         {
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);
155
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)) 
158                                 {
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);
161                                     
162                                     if (jsonb_iterator_token == WJB_ELEM) 
163                                     {
164                                         return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_replacement_iterator_value);
165                                     }
166                                     else
167                                     {
168                                         return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
169                                         return_jsonb_value = pushJsonbValue(&state, WJB_VALUE, &jsonb_replacement_iterator_value);
170                                     }
171                                 }
172                                 /* otherwise assume this is the replacement for the whole element /key-value pair */
173                                 else {
174                                     while ((jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false)) != WJB_DONE)
175                                     {
176                                         if (push_nest_level == 0 && jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)
177                                         {
178                                             return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
179                                         }
180
181                                         if ((jsonb_replacement_iterator_token == WJB_BEGIN_OBJECT) || (jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)) 
182                                             push_nest_level++;
183
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)) 
187                                         {
188                                             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_replacement_iterator_token, &jsonb_replacement_iterator_value);
189                                         }
190
191                                         if ((jsonb_replacement_iterator_token == WJB_END_OBJECT || jsonb_replacement_iterator_token == WJB_END_ARRAY)) 
192                                             push_nest_level--;
193
194                                         Assert(push_nest_level >= 0);
195                                     }
196                                 }
197                             }
198                             if (jsonb_iterator_token == WJB_ELEM) 
199                                 push = false;
200                             else
201                                 skip_level = nest_level;
202                         }
203                     } 
204                 }
205         }
206
207         if (push && (skip_level == 0 || nest_level < skip_level)) 
208         {
209             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
210         }
211
212         switch (jsonb_iterator_token) 
213         {
214             case WJB_END_OBJECT:
215                 nest_level--;
216                 if (skip_level == nest_level && array_level == 0)
217                     skip_level = 0;
218             break;
219             case WJB_END_ARRAY:
220                 array_level--;
221                 if (skip_level == nest_level && array_level == 0)
222                     skip_level = 0;
223             break;
224             case WJB_VALUE:
225                 if (skip_level == nest_level)
226                     skip_level = 0;
227         }
228     }
229
230     if (return_jsonb_value->type == jbvArray)
231     {
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;
236     }
237
238     return JsonbValueToJsonb(return_jsonb_value);
239     
240 }