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