]> git.8kb.co.uk Git - postgresql/pg_jsonb_opx/blob - jsonb_utilsx.c
Raise an error if un implemented append function called.
[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 ((!nulls[index_on] && (jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(key_on)) &&
137                         (memcmp(jsonb_iterator_value.val.string.val, VARDATA_ANY(key_on), jsonb_iterator_value.val.string.len) == 0)))
138                     {   
139                         /* if we have not yet reached the last index in the array / key chain move on and check the next */
140                         if (index_on < count-1) 
141                         {
142                             index_on++;
143                             if (!nulls[index_on])
144                                 key_on = DatumGetTextP(datums[index_on]);
145                         }
146                         /* if we have reached the last index, the we can modify this level */ 
147                         else 
148                         {
149                             /* if jsonb_b is not null unwrap it with iterator into replacement_jsonb_value */
150                             if (jsonb_b != NULL) {
151                                 jsonb_replacement_iterator = JsonbIteratorInit(&jsonb_b->root);
152
153                                 /* if the replacement value is a scalar then it will replace the current element or value */
154                                 if (JB_ROOT_IS_SCALAR(jsonb_b)) 
155                                 {
156                                     jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false);
157                                     jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false);
158                                     
159                                     if (jsonb_iterator_token == WJB_ELEM) 
160                                     {
161                                         return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_replacement_iterator_value);
162                                     }
163                                     else
164                                     {
165                                         return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
166                                         return_jsonb_value = pushJsonbValue(&state, WJB_VALUE, &jsonb_replacement_iterator_value);
167                                     }
168                                 }
169                                 /* otherwise assume this is the replacement for the whole element or key-value pair */
170                                 else {
171                                     while ((jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false)) != WJB_DONE)
172                                     {
173                                         if (push_nest_level == 0 && jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)
174                                         {
175                                             return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
176                                         }
177
178                                         if ((jsonb_replacement_iterator_token == WJB_BEGIN_OBJECT) || (jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)) 
179                                             push_nest_level++;
180
181                                         if ((push_nest_level > 1) ||
182                                             (jsonb_iterator_token == WJB_ELEM && jsonb_replacement_iterator_token != WJB_BEGIN_ARRAY && jsonb_replacement_iterator_token != WJB_END_ARRAY) ||
183                                             (jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token != WJB_BEGIN_OBJECT && jsonb_replacement_iterator_token != WJB_END_OBJECT)) 
184                                         {
185                                             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_replacement_iterator_token, &jsonb_replacement_iterator_value);
186                                         }
187
188                                         if ((jsonb_replacement_iterator_token == WJB_END_OBJECT || jsonb_replacement_iterator_token == WJB_END_ARRAY)) 
189                                             push_nest_level--;
190
191                                         Assert(push_nest_level >= 0);
192                                     }
193                                 }
194                             }
195                             if (jsonb_iterator_token == WJB_ELEM) 
196                                 push = false;
197                             else
198                                 skip_level = nest_level;
199                         }
200                     } 
201                 }
202             /* switch end */
203         }
204
205         if (push && (skip_level == 0 || nest_level < skip_level)) 
206         {
207             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
208         }
209
210         switch (jsonb_iterator_token) 
211         {
212             case WJB_END_OBJECT:
213                 nest_level--;
214                 if (skip_level == nest_level && array_level == 0)
215                     skip_level = 0;
216                 break;
217             case WJB_END_ARRAY:
218                 array_level--;
219                 if (skip_level == nest_level && array_level == 0)
220                     skip_level = 0;
221                 break;
222             case WJB_VALUE:
223                 if (skip_level == nest_level)
224                     skip_level = 0;
225             /* switch end */
226         }
227     }
228
229     if (return_jsonb_value->type == jbvArray)
230     {
231         if (return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 0)
232                 return_jsonb_value->val.array.rawScalar = false;
233         else if (JB_ROOT_IS_SCALAR(jsonb_a) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
234                 return_jsonb_value->val.array.rawScalar = true;
235     }
236
237     return JsonbValueToJsonb(return_jsonb_value);
238     
239 }