]> git.8kb.co.uk Git - postgresql/pg_jsonb_opx/blob - sql/jsonb_utilsx.c
Amend operations on scalar arrays
[postgresql/pg_jsonb_opx] / sql / 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             (jsonb_iterator_token == WJB_BEGIN_ARRAY && jsonb_iterator_value->val.array.rawScalar))
45             {
46             return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, jsonb_iterator_value);
47             }
48         else
49             return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, NULL);
50
51     return return_jsonb_value;
52 }
53
54 Jsonb * 
55 jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
56 {
57     /* pointers to return jsonb value data and state to be converted to jsonb on return */
58     JsonbParseState *state = NULL;
59     JsonbValue *return_jsonb_value = NULL;
60
61     /* pointer to iterator for input jsonb */
62     JsonbIterator *jsonb_iterator;
63     JsonbValue jsonb_iterator_value;
64     int32 jsonb_iterator_token;
65     int32 jsonb_last_token = 0;
66
67     JsonbIterator *jsonb_replacement_iterator;
68     JsonbValue jsonb_replacement_iterator_value;
69     int32 jsonb_replacement_iterator_token;
70     
71     /* 
72      * array element variables for use during deconstruction 
73      * count is the depth we will be looking from the first matching key
74      */
75     Datum *datums;
76     bool *nulls;
77     int32 count; 
78
79     /* the current key we are looking for, starting with the first key */
80     text *key_on = NULL;
81     int32 index_on = 0; 
82     int32 nest_level = 0;
83     int32 array_level = 0;
84     int32 skip_level = 0;
85     int32 push_nest_level = 0;
86     bool push = true;
87
88     /* assert input_array is a text array type */
89     Assert(ARR_ELEMTYPE(array_path) == TEXTOID);
90
91     /* check input_array is one-dimensional */
92     if (ARR_NDIM(array_path) > 1)
93         ereport(ERROR,
94             (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
95                 errmsg("1 dimensional text array expected")));
96
97     /* deconstruct array elements */
98     deconstruct_array(array_path, TEXTOID, -1, false, 'i',
99                         &datums, &nulls, &count);
100
101     /* can't follow key based paths on non objects */ 
102     if (!JB_ROOT_IS_OBJECT(jsonb_a) && (count > 1))
103         ereport(ERROR,
104             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
105                 errmsg("cannot call with path deeper than 1 on a non-object")));
106
107      /* if the array is empty there's no work to do so return the input value */
108     if ((count == 0) || (JB_ROOT_COUNT(jsonb_a) == 0))
109         return jsonb_a;
110
111     if (!nulls[index_on])
112         key_on = DatumGetTextP(datums[index_on]);
113
114     jsonb_iterator = JsonbIteratorInit(&jsonb_a->root);
115
116     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
117     {    
118         push = true;
119
120         switch (jsonb_iterator_token)
121         {
122             case WJB_BEGIN_ARRAY:
123                 array_level++;
124             break;
125             case WJB_BEGIN_OBJECT:
126                nest_level++;
127             break;
128             case WJB_ELEM:
129             case WJB_KEY:
130                 /*
131                  * If we are not skipping the current nest level check that the nesting level follows the array index
132                  * and if it does check the current key. For array elements only check the root level (array_level==1).
133                  * If there is no match we just keep on pushing
134                  */
135                 if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level-1 == index_on && array_level == 0) ||
136                         (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1)))
137                 {
138                     if ((jsonb_iterator_value.type == jbvNull && key_on == NULL) ||
139                         (key_on != NULL && (jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(key_on)) &&
140                         (memcmp(jsonb_iterator_value.val.string.val, VARDATA_ANY(key_on), jsonb_iterator_value.val.string.len) == 0)))
141                     {   
142                         /* if we have not yet reached the last index in the array / key chain move on and check the next */
143                         if (index_on < count-1) 
144                         {
145                             index_on++;
146                             if (!nulls[index_on])
147                                 key_on = DatumGetTextP(datums[index_on]);
148                             else
149                                 key_on = NULL;
150                         }
151                         /* if we have reached the last index, the we can modify this level */ 
152                         else 
153                         {
154                             /* if jsonb_b is not null unwrap it with iterator into replacement_jsonb_value */
155                             if (jsonb_b != NULL) {
156                                 jsonb_replacement_iterator = JsonbIteratorInit(&jsonb_b->root);
157
158                                 while ((jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false)) != WJB_DONE)
159                                 {
160                                     if (((jsonb_last_token == jsonb_replacement_iterator_token) && 
161                                         (jsonb_last_token != WJB_VALUE)) || 
162                                         ((jsonb_last_token == WJB_VALUE) && 
163                                         ((jsonb_replacement_iterator_token == WJB_BEGIN_OBJECT) || 
164                                         (jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)))) 
165                                     {
166                                         push_nest_level++;
167                                     }
168
169                                     if ((jsonb_replacement_iterator_token == WJB_KEY) || 
170                                         (jsonb_replacement_iterator_token == WJB_VALUE) || 
171                                         (jsonb_replacement_iterator_token == WJB_ELEM) || (push_nest_level != 1))
172                                     {
173                                         return_jsonb_value = pushJsonbValueBlind(&state, jsonb_replacement_iterator_token, &jsonb_replacement_iterator_value);
174                                     }
175
176                                     if (((jsonb_last_token == WJB_BEGIN_ARRAY) || 
177                                         (jsonb_last_token == WJB_VALUE)) && 
178                                         (jsonb_replacement_iterator_token == WJB_END_ARRAY))
179                                     {
180                                         push_nest_level--;
181                                     }
182                                     else if (((jsonb_last_token == WJB_BEGIN_OBJECT) || 
183                                         (jsonb_last_token == WJB_VALUE)) && 
184                                         (jsonb_replacement_iterator_token == WJB_END_OBJECT))
185                                     {
186                                         push_nest_level--;
187                                     }
188                                 }
189                             }
190                             if (jsonb_iterator_token == WJB_ELEM) 
191                                 push = false;
192                             else
193                                 skip_level = nest_level;
194                         }
195                     } 
196                 }
197         }
198
199         if (push && (skip_level == 0 || nest_level < skip_level)) 
200         {
201             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
202             jsonb_last_token = jsonb_iterator_token;
203         }
204
205         switch (jsonb_iterator_token) 
206         {
207             case WJB_END_OBJECT:
208                 nest_level--;
209                 if (skip_level == nest_level && array_level == 0)
210                     skip_level = 0;
211             break;
212             case WJB_END_ARRAY:
213                 array_level--;
214                 if (skip_level == nest_level && array_level == 0)
215                     skip_level = 0;
216             break;
217             case WJB_VALUE:
218                 if (skip_level == nest_level)
219                     skip_level = 0;
220         }
221     }
222
223     if (return_jsonb_value->type == jbvArray && return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 0)
224     {
225         return_jsonb_value->val.array.rawScalar = false;
226     }
227
228     return JsonbValueToJsonb(return_jsonb_value);
229     
230 }