1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * jsonb_op.c |
4 | * Special operators for jsonb only, used by various index access methods |
5 | * |
6 | * Copyright (c) 2014-2019, PostgreSQL Global Development Group |
7 | * |
8 | * |
9 | * IDENTIFICATION |
10 | * src/backend/utils/adt/jsonb_op.c |
11 | * |
12 | *------------------------------------------------------------------------- |
13 | */ |
14 | #include "postgres.h" |
15 | |
16 | #include "catalog/pg_type.h" |
17 | #include "miscadmin.h" |
18 | #include "utils/builtins.h" |
19 | #include "utils/jsonb.h" |
20 | |
21 | Datum |
22 | jsonb_exists(PG_FUNCTION_ARGS) |
23 | { |
24 | Jsonb *jb = PG_GETARG_JSONB_P(0); |
25 | text *key = PG_GETARG_TEXT_PP(1); |
26 | JsonbValue kval; |
27 | JsonbValue *v = NULL; |
28 | |
29 | /* |
30 | * We only match Object keys (which are naturally always Strings), or |
31 | * string elements in arrays. In particular, we do not match non-string |
32 | * scalar elements. Existence of a key/element is only considered at the |
33 | * top level. No recursion occurs. |
34 | */ |
35 | kval.type = jbvString; |
36 | kval.val.string.val = VARDATA_ANY(key); |
37 | kval.val.string.len = VARSIZE_ANY_EXHDR(key); |
38 | |
39 | v = findJsonbValueFromContainer(&jb->root, |
40 | JB_FOBJECT | JB_FARRAY, |
41 | &kval); |
42 | |
43 | PG_RETURN_BOOL(v != NULL); |
44 | } |
45 | |
46 | Datum |
47 | jsonb_exists_any(PG_FUNCTION_ARGS) |
48 | { |
49 | Jsonb *jb = PG_GETARG_JSONB_P(0); |
50 | ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); |
51 | int i; |
52 | Datum *key_datums; |
53 | bool *key_nulls; |
54 | int elem_count; |
55 | |
56 | deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, |
57 | &elem_count); |
58 | |
59 | for (i = 0; i < elem_count; i++) |
60 | { |
61 | JsonbValue strVal; |
62 | |
63 | if (key_nulls[i]) |
64 | continue; |
65 | |
66 | strVal.type = jbvString; |
67 | strVal.val.string.val = VARDATA(key_datums[i]); |
68 | strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ; |
69 | |
70 | if (findJsonbValueFromContainer(&jb->root, |
71 | JB_FOBJECT | JB_FARRAY, |
72 | &strVal) != NULL) |
73 | PG_RETURN_BOOL(true); |
74 | } |
75 | |
76 | PG_RETURN_BOOL(false); |
77 | } |
78 | |
79 | Datum |
80 | jsonb_exists_all(PG_FUNCTION_ARGS) |
81 | { |
82 | Jsonb *jb = PG_GETARG_JSONB_P(0); |
83 | ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); |
84 | int i; |
85 | Datum *key_datums; |
86 | bool *key_nulls; |
87 | int elem_count; |
88 | |
89 | deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls, |
90 | &elem_count); |
91 | |
92 | for (i = 0; i < elem_count; i++) |
93 | { |
94 | JsonbValue strVal; |
95 | |
96 | if (key_nulls[i]) |
97 | continue; |
98 | |
99 | strVal.type = jbvString; |
100 | strVal.val.string.val = VARDATA(key_datums[i]); |
101 | strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ; |
102 | |
103 | if (findJsonbValueFromContainer(&jb->root, |
104 | JB_FOBJECT | JB_FARRAY, |
105 | &strVal) == NULL) |
106 | PG_RETURN_BOOL(false); |
107 | } |
108 | |
109 | PG_RETURN_BOOL(true); |
110 | } |
111 | |
112 | Datum |
113 | jsonb_contains(PG_FUNCTION_ARGS) |
114 | { |
115 | Jsonb *val = PG_GETARG_JSONB_P(0); |
116 | Jsonb *tmpl = PG_GETARG_JSONB_P(1); |
117 | |
118 | JsonbIterator *it1, |
119 | *it2; |
120 | |
121 | if (JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl)) |
122 | PG_RETURN_BOOL(false); |
123 | |
124 | it1 = JsonbIteratorInit(&val->root); |
125 | it2 = JsonbIteratorInit(&tmpl->root); |
126 | |
127 | PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2)); |
128 | } |
129 | |
130 | Datum |
131 | jsonb_contained(PG_FUNCTION_ARGS) |
132 | { |
133 | /* Commutator of "contains" */ |
134 | Jsonb *tmpl = PG_GETARG_JSONB_P(0); |
135 | Jsonb *val = PG_GETARG_JSONB_P(1); |
136 | |
137 | JsonbIterator *it1, |
138 | *it2; |
139 | |
140 | if (JB_ROOT_IS_OBJECT(val) != JB_ROOT_IS_OBJECT(tmpl)) |
141 | PG_RETURN_BOOL(false); |
142 | |
143 | it1 = JsonbIteratorInit(&val->root); |
144 | it2 = JsonbIteratorInit(&tmpl->root); |
145 | |
146 | PG_RETURN_BOOL(JsonbDeepContains(&it1, &it2)); |
147 | } |
148 | |
149 | Datum |
150 | jsonb_ne(PG_FUNCTION_ARGS) |
151 | { |
152 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
153 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
154 | bool res; |
155 | |
156 | res = (compareJsonbContainers(&jba->root, &jbb->root) != 0); |
157 | |
158 | PG_FREE_IF_COPY(jba, 0); |
159 | PG_FREE_IF_COPY(jbb, 1); |
160 | PG_RETURN_BOOL(res); |
161 | } |
162 | |
163 | /* |
164 | * B-Tree operator class operators, support function |
165 | */ |
166 | Datum |
167 | jsonb_lt(PG_FUNCTION_ARGS) |
168 | { |
169 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
170 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
171 | bool res; |
172 | |
173 | res = (compareJsonbContainers(&jba->root, &jbb->root) < 0); |
174 | |
175 | PG_FREE_IF_COPY(jba, 0); |
176 | PG_FREE_IF_COPY(jbb, 1); |
177 | PG_RETURN_BOOL(res); |
178 | } |
179 | |
180 | Datum |
181 | jsonb_gt(PG_FUNCTION_ARGS) |
182 | { |
183 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
184 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
185 | bool res; |
186 | |
187 | res = (compareJsonbContainers(&jba->root, &jbb->root) > 0); |
188 | |
189 | PG_FREE_IF_COPY(jba, 0); |
190 | PG_FREE_IF_COPY(jbb, 1); |
191 | PG_RETURN_BOOL(res); |
192 | } |
193 | |
194 | Datum |
195 | jsonb_le(PG_FUNCTION_ARGS) |
196 | { |
197 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
198 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
199 | bool res; |
200 | |
201 | res = (compareJsonbContainers(&jba->root, &jbb->root) <= 0); |
202 | |
203 | PG_FREE_IF_COPY(jba, 0); |
204 | PG_FREE_IF_COPY(jbb, 1); |
205 | PG_RETURN_BOOL(res); |
206 | } |
207 | |
208 | Datum |
209 | jsonb_ge(PG_FUNCTION_ARGS) |
210 | { |
211 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
212 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
213 | bool res; |
214 | |
215 | res = (compareJsonbContainers(&jba->root, &jbb->root) >= 0); |
216 | |
217 | PG_FREE_IF_COPY(jba, 0); |
218 | PG_FREE_IF_COPY(jbb, 1); |
219 | PG_RETURN_BOOL(res); |
220 | } |
221 | |
222 | Datum |
223 | jsonb_eq(PG_FUNCTION_ARGS) |
224 | { |
225 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
226 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
227 | bool res; |
228 | |
229 | res = (compareJsonbContainers(&jba->root, &jbb->root) == 0); |
230 | |
231 | PG_FREE_IF_COPY(jba, 0); |
232 | PG_FREE_IF_COPY(jbb, 1); |
233 | PG_RETURN_BOOL(res); |
234 | } |
235 | |
236 | Datum |
237 | jsonb_cmp(PG_FUNCTION_ARGS) |
238 | { |
239 | Jsonb *jba = PG_GETARG_JSONB_P(0); |
240 | Jsonb *jbb = PG_GETARG_JSONB_P(1); |
241 | int res; |
242 | |
243 | res = compareJsonbContainers(&jba->root, &jbb->root); |
244 | |
245 | PG_FREE_IF_COPY(jba, 0); |
246 | PG_FREE_IF_COPY(jbb, 1); |
247 | PG_RETURN_INT32(res); |
248 | } |
249 | |
250 | /* |
251 | * Hash operator class jsonb hashing function |
252 | */ |
253 | Datum |
254 | jsonb_hash(PG_FUNCTION_ARGS) |
255 | { |
256 | Jsonb *jb = PG_GETARG_JSONB_P(0); |
257 | JsonbIterator *it; |
258 | JsonbValue v; |
259 | JsonbIteratorToken r; |
260 | uint32 hash = 0; |
261 | |
262 | if (JB_ROOT_COUNT(jb) == 0) |
263 | PG_RETURN_INT32(0); |
264 | |
265 | it = JsonbIteratorInit(&jb->root); |
266 | |
267 | while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) |
268 | { |
269 | switch (r) |
270 | { |
271 | /* Rotation is left to JsonbHashScalarValue() */ |
272 | case WJB_BEGIN_ARRAY: |
273 | hash ^= JB_FARRAY; |
274 | break; |
275 | case WJB_BEGIN_OBJECT: |
276 | hash ^= JB_FOBJECT; |
277 | break; |
278 | case WJB_KEY: |
279 | case WJB_VALUE: |
280 | case WJB_ELEM: |
281 | JsonbHashScalarValue(&v, &hash); |
282 | break; |
283 | case WJB_END_ARRAY: |
284 | case WJB_END_OBJECT: |
285 | break; |
286 | default: |
287 | elog(ERROR, "invalid JsonbIteratorNext rc: %d" , (int) r); |
288 | } |
289 | } |
290 | |
291 | PG_FREE_IF_COPY(jb, 0); |
292 | PG_RETURN_INT32(hash); |
293 | } |
294 | |
295 | Datum |
296 | jsonb_hash_extended(PG_FUNCTION_ARGS) |
297 | { |
298 | Jsonb *jb = PG_GETARG_JSONB_P(0); |
299 | uint64 seed = PG_GETARG_INT64(1); |
300 | JsonbIterator *it; |
301 | JsonbValue v; |
302 | JsonbIteratorToken r; |
303 | uint64 hash = 0; |
304 | |
305 | if (JB_ROOT_COUNT(jb) == 0) |
306 | PG_RETURN_UINT64(seed); |
307 | |
308 | it = JsonbIteratorInit(&jb->root); |
309 | |
310 | while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) |
311 | { |
312 | switch (r) |
313 | { |
314 | /* Rotation is left to JsonbHashScalarValueExtended() */ |
315 | case WJB_BEGIN_ARRAY: |
316 | hash ^= ((uint64) JB_FARRAY) << 32 | JB_FARRAY; |
317 | break; |
318 | case WJB_BEGIN_OBJECT: |
319 | hash ^= ((uint64) JB_FOBJECT) << 32 | JB_FOBJECT; |
320 | break; |
321 | case WJB_KEY: |
322 | case WJB_VALUE: |
323 | case WJB_ELEM: |
324 | JsonbHashScalarValueExtended(&v, &hash, seed); |
325 | break; |
326 | case WJB_END_ARRAY: |
327 | case WJB_END_OBJECT: |
328 | break; |
329 | default: |
330 | elog(ERROR, "invalid JsonbIteratorNext rc: %d" , (int) r); |
331 | } |
332 | } |
333 | |
334 | PG_FREE_IF_COPY(jb, 0); |
335 | PG_RETURN_UINT64(hash); |
336 | } |
337 | |