Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * jsonpath.c
4 : : * Input/output and supporting routines for jsonpath
5 : : *
6 : : * jsonpath expression is a chain of path items. First path item is $, $var,
7 : : * literal or arithmetic expression. Subsequent path items are accessors
8 : : * (.key, .*, [subscripts], [*]), filters (? (predicate)) and methods (.type(),
9 : : * .size() etc).
10 : : *
11 : : * For instance, structure of path items for simple expression:
12 : : *
13 : : * $.a[*].type()
14 : : *
15 : : * is pretty evident:
16 : : *
17 : : * $ => .a => [*] => .type()
18 : : *
19 : : * Some path items such as arithmetic operations, predicates or array
20 : : * subscripts may comprise subtrees. For instance, more complex expression
21 : : *
22 : : * ($.a + $[1 to 5, 7] ? (@ > 3).double()).type()
23 : : *
24 : : * have following structure of path items:
25 : : *
26 : : * + => .type()
27 : : * ___/ \___
28 : : * / \
29 : : * $ => .a $ => [] => ? => .double()
30 : : * _||_ |
31 : : * / \ >
32 : : * to to / \
33 : : * / \ / @ 3
34 : : * 1 5 7
35 : : *
36 : : * Binary encoding of jsonpath constitutes a sequence of 4-bytes aligned
37 : : * variable-length path items connected by links. Every item has a header
38 : : * consisting of item type (enum JsonPathItemType) and offset of next item
39 : : * (zero means no next item). After the header, item may have payload
40 : : * depending on item type. For instance, payload of '.key' accessor item is
41 : : * length of key name and key name itself. Payload of '>' arithmetic operator
42 : : * item is offsets of right and left operands.
43 : : *
44 : : * So, binary representation of sample expression above is:
45 : : * (bottom arrows are next links, top lines are argument links)
46 : : *
47 : : * _____
48 : : * _____ ___/____ \ __
49 : : * _ /_ \ _____/__/____ \ \ __ _ /_ \
50 : : * / / \ \ / / / \ \ \ / \ / / \ \
51 : : * +(LR) $ .a $ [](* to *, * to *) 1 5 7 ?(A) >(LR) @ 3 .double() .type()
52 : : * | | ^ | ^| ^| ^ ^
53 : : * | |__| |__||________________________||___________________| |
54 : : * |_______________________________________________________________________|
55 : : *
56 : : * Copyright (c) 2019-2026, PostgreSQL Global Development Group
57 : : *
58 : : * IDENTIFICATION
59 : : * src/backend/utils/adt/jsonpath.c
60 : : *
61 : : *-------------------------------------------------------------------------
62 : : */
63 : :
64 : : #include "postgres.h"
65 : :
66 : : #include "catalog/pg_type.h"
67 : : #include "lib/stringinfo.h"
68 : : #include "libpq/pqformat.h"
69 : : #include "miscadmin.h"
70 : : #include "nodes/miscnodes.h"
71 : : #include "nodes/nodeFuncs.h"
72 : : #include "utils/fmgrprotos.h"
73 : : #include "utils/formatting.h"
74 : : #include "utils/json.h"
75 : : #include "utils/jsonpath.h"
76 : :
77 : :
78 : : static Datum jsonPathFromCstring(char *in, int len, struct Node *escontext);
79 : : static char *jsonPathToCstring(StringInfo out, JsonPath *in,
80 : : int estimated_len);
81 : : static bool flattenJsonPathParseItem(StringInfo buf, int *result,
82 : : struct Node *escontext,
83 : : JsonPathParseItem *item,
84 : : int nestingLevel, bool insideArraySubscript);
85 : : static void alignStringInfoInt(StringInfo buf);
86 : : static int32 reserveSpaceForItemPointer(StringInfo buf);
87 : : static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
88 : : bool printBracketes);
89 : : static int operationPriority(JsonPathItemType op);
90 : :
91 : :
92 : : /**************************** INPUT/OUTPUT ********************************/
93 : :
94 : : /*
95 : : * jsonpath type input function
96 : : */
97 : : Datum
2607 akorotkov@postgresql 98 :CBC 7698 : jsonpath_in(PG_FUNCTION_ARGS)
99 : : {
100 : 7698 : char *in = PG_GETARG_CSTRING(0);
101 : 7698 : int len = strlen(in);
102 : :
1228 andrew@dunslane.net 103 : 7698 : return jsonPathFromCstring(in, len, fcinfo->context);
104 : : }
105 : :
106 : : /*
107 : : * jsonpath type recv function
108 : : *
109 : : * The type is sent as text in binary mode, so this is almost the same
110 : : * as the input function, but it's prefixed with a version number so we
111 : : * can change the binary format sent in future if necessary. For now,
112 : : * only version 1 is supported.
113 : : */
114 : : Datum
2607 akorotkov@postgresql 115 :UBC 0 : jsonpath_recv(PG_FUNCTION_ARGS)
116 : : {
117 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
118 : 0 : int version = pq_getmsgint(buf, 1);
119 : : char *str;
120 : : int nbytes;
121 : :
122 [ # # ]: 0 : if (version == JSONPATH_VERSION)
123 : 0 : str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
124 : : else
125 [ # # ]: 0 : elog(ERROR, "unsupported jsonpath version number: %d", version);
126 : :
1228 andrew@dunslane.net 127 : 0 : return jsonPathFromCstring(str, nbytes, NULL);
128 : : }
129 : :
130 : : /*
131 : : * jsonpath type output function
132 : : */
133 : : Datum
2607 akorotkov@postgresql 134 :CBC 1336 : jsonpath_out(PG_FUNCTION_ARGS)
135 : : {
136 : 1336 : JsonPath *in = PG_GETARG_JSONPATH_P(0);
137 : :
138 : 1336 : PG_RETURN_CSTRING(jsonPathToCstring(NULL, in, VARSIZE(in)));
139 : : }
140 : :
141 : : /*
142 : : * jsonpath type send function
143 : : *
144 : : * Just send jsonpath as a version number, then a string of text
145 : : */
146 : : Datum
2607 akorotkov@postgresql 147 :UBC 0 : jsonpath_send(PG_FUNCTION_ARGS)
148 : : {
149 : 0 : JsonPath *in = PG_GETARG_JSONPATH_P(0);
150 : : StringInfoData buf;
151 : : StringInfoData jtext;
152 : 0 : int version = JSONPATH_VERSION;
153 : :
154 : 0 : initStringInfo(&jtext);
155 : 0 : (void) jsonPathToCstring(&jtext, in, VARSIZE(in));
156 : :
157 : 0 : pq_begintypsend(&buf);
158 : 0 : pq_sendint8(&buf, version);
159 : 0 : pq_sendtext(&buf, jtext.data, jtext.len);
160 : 0 : pfree(jtext.data);
161 : :
162 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
163 : : }
164 : :
165 : : /*
166 : : * Converts C-string to a jsonpath value.
167 : : *
168 : : * Uses jsonpath parser to turn string into an AST, then
169 : : * flattenJsonPathParseItem() does second pass turning AST into binary
170 : : * representation of jsonpath.
171 : : */
172 : : static Datum
1228 andrew@dunslane.net 173 :CBC 7698 : jsonPathFromCstring(char *in, int len, struct Node *escontext)
174 : : {
175 : 7698 : JsonPathParseResult *jsonpath = parsejsonpath(in, len, escontext);
176 : : JsonPath *res;
177 : : StringInfoData buf;
178 : :
179 [ + + + - : 7390 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
180 : 28 : return (Datum) 0;
181 : :
2607 akorotkov@postgresql 182 [ + + ]: 7362 : if (!jsonpath)
1228 andrew@dunslane.net 183 [ + - ]: 4 : ereturn(escontext, (Datum) 0,
184 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
185 : : errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
186 : : in)));
187 : :
188 : 7358 : initStringInfo(&buf);
189 : 7358 : enlargeStringInfo(&buf, 4 * len /* estimation */ );
190 : :
191 : 7358 : appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
192 : :
193 [ + + ]: 7358 : if (!flattenJsonPathParseItem(&buf, NULL, escontext,
194 : : jsonpath->expr, 0, false))
195 : 8 : return (Datum) 0;
196 : :
2607 akorotkov@postgresql 197 : 7338 : res = (JsonPath *) buf.data;
198 : 7338 : SET_VARSIZE(res, buf.len);
199 : 7338 : res->header = JSONPATH_VERSION;
200 [ + + ]: 7338 : if (jsonpath->lax)
201 : 6870 : res->header |= JSONPATH_LAX;
202 : :
203 : 7338 : PG_RETURN_JSONPATH_P(res);
204 : : }
205 : :
206 : : /*
207 : : * Converts jsonpath value to a C-string.
208 : : *
209 : : * If 'out' argument is non-null, the resulting C-string is stored inside the
210 : : * StringBuffer. The resulting string is always returned.
211 : : */
212 : : static char *
213 : 1336 : jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
214 : : {
215 : : StringInfoData buf;
216 : : JsonPathItem v;
217 : :
218 [ + - ]: 1336 : if (!out)
219 : : {
220 : 1336 : out = &buf;
221 : 1336 : initStringInfo(out);
222 : : }
223 : 1336 : enlargeStringInfo(out, estimated_len);
224 : :
225 [ + + ]: 1336 : if (!(in->header & JSONPATH_LAX))
1222 peter@eisentraut.org 226 : 12 : appendStringInfoString(out, "strict ");
227 : :
2607 akorotkov@postgresql 228 : 1336 : jspInit(&v, in);
229 : 1336 : printJsonPathItem(out, &v, false, true);
230 : :
231 : 1336 : return out->data;
232 : : }
233 : :
234 : : /*
235 : : * Recursive function converting given jsonpath parse item and all its
236 : : * children into a binary representation.
237 : : */
238 : : static bool
1082 tgl@sss.pgh.pa.us 239 : 24730 : flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
240 : : JsonPathParseItem *item, int nestingLevel,
241 : : bool insideArraySubscript)
242 : : {
243 : : /* position from beginning of jsonpath data */
2607 akorotkov@postgresql 244 : 24730 : int32 pos = buf->len - JSONPATH_HDRSZ;
245 : : int32 chld;
246 : : int32 next;
247 : 24730 : int argNestingLevel = 0;
248 : :
249 : 24730 : check_stack_depth();
250 [ - + ]: 24730 : CHECK_FOR_INTERRUPTS();
251 : :
252 : 24730 : appendStringInfoChar(buf, (char) (item->type));
253 : :
254 : : /*
255 : : * We align buffer to int32 because a series of int32 values often goes
256 : : * after the header, and we want to read them directly by dereferencing
257 : : * int32 pointer (see jspInitByBuffer()).
258 : : */
259 : 24730 : alignStringInfoInt(buf);
260 : :
261 : : /*
262 : : * Reserve space for next item pointer. Actual value will be recorded
263 : : * later, after next and children items processing.
264 : : */
265 : 24730 : next = reserveSpaceForItemPointer(buf);
266 : :
267 [ + + + + : 24730 : switch (item->type)
+ + + + +
+ + + + +
+ - ]
268 : : {
269 : 4236 : case jpiString:
270 : : case jpiVariable:
271 : : case jpiKey:
1222 peter@eisentraut.org 272 : 4236 : appendBinaryStringInfo(buf, &item->value.string.len,
273 : : sizeof(item->value.string.len));
2607 akorotkov@postgresql 274 : 4236 : appendBinaryStringInfo(buf, item->value.string.val,
275 : 4236 : item->value.string.len);
276 : 4236 : appendStringInfoChar(buf, '\0');
277 : 4236 : break;
278 : 1708 : case jpiNumeric:
1222 peter@eisentraut.org 279 : 1708 : appendBinaryStringInfo(buf, item->value.numeric,
2607 akorotkov@postgresql 280 : 1708 : VARSIZE(item->value.numeric));
281 : 1708 : break;
282 : 120 : case jpiBool:
1222 peter@eisentraut.org 283 : 120 : appendBinaryStringInfo(buf, &item->value.boolean,
284 : : sizeof(item->value.boolean));
2607 akorotkov@postgresql 285 : 120 : break;
286 : 2536 : case jpiAnd:
287 : : case jpiOr:
288 : : case jpiEqual:
289 : : case jpiNotEqual:
290 : : case jpiLess:
291 : : case jpiGreater:
292 : : case jpiLessOrEqual:
293 : : case jpiGreaterOrEqual:
294 : : case jpiAdd:
295 : : case jpiSub:
296 : : case jpiMul:
297 : : case jpiDiv:
298 : : case jpiMod:
299 : : case jpiStartsWith:
300 : : case jpiDecimal:
301 : : case jpiStrReplace:
302 : : case jpiStrSplitPart:
303 : : {
304 : : /*
305 : : * First, reserve place for left/right arg's positions, then
306 : : * record both args and sets actual position in reserved
307 : : * places.
308 : : */
309 : 2536 : int32 left = reserveSpaceForItemPointer(buf);
310 : 2536 : int32 right = reserveSpaceForItemPointer(buf);
311 : :
1228 andrew@dunslane.net 312 [ + + ]: 2536 : if (!item->value.args.left)
313 : 112 : chld = pos;
1082 tgl@sss.pgh.pa.us 314 [ + + ]: 2424 : else if (!flattenJsonPathParseItem(buf, &chld, escontext,
315 : : item->value.args.left,
316 : : nestingLevel + argNestingLevel,
317 : : insideArraySubscript))
1228 andrew@dunslane.net 318 : 8 : return false;
2607 akorotkov@postgresql 319 : 2520 : *(int32 *) (buf->data + left) = chld - pos;
320 : :
1228 andrew@dunslane.net 321 [ + + ]: 2520 : if (!item->value.args.right)
322 : 112 : chld = pos;
1082 tgl@sss.pgh.pa.us 323 [ - + ]: 2408 : else if (!flattenJsonPathParseItem(buf, &chld, escontext,
324 : : item->value.args.right,
325 : : nestingLevel + argNestingLevel,
326 : : insideArraySubscript))
1228 andrew@dunslane.net 327 :UBC 0 : return false;
2607 akorotkov@postgresql 328 :CBC 2520 : *(int32 *) (buf->data + right) = chld - pos;
329 : : }
330 : 2520 : break;
331 : 80 : case jpiLikeRegex:
332 : : {
333 : : int32 offs;
334 : :
335 : 80 : appendBinaryStringInfo(buf,
1222 peter@eisentraut.org 336 : 80 : &item->value.like_regex.flags,
337 : : sizeof(item->value.like_regex.flags));
2607 akorotkov@postgresql 338 : 80 : offs = reserveSpaceForItemPointer(buf);
339 : 80 : appendBinaryStringInfo(buf,
1222 peter@eisentraut.org 340 : 80 : &item->value.like_regex.patternlen,
341 : : sizeof(item->value.like_regex.patternlen));
2607 akorotkov@postgresql 342 : 80 : appendBinaryStringInfo(buf, item->value.like_regex.pattern,
343 : 80 : item->value.like_regex.patternlen);
344 : 80 : appendStringInfoChar(buf, '\0');
345 : :
1082 tgl@sss.pgh.pa.us 346 [ - + ]: 80 : if (!flattenJsonPathParseItem(buf, &chld, escontext,
347 : : item->value.like_regex.expr,
348 : : nestingLevel,
349 : : insideArraySubscript))
1228 andrew@dunslane.net 350 :UBC 0 : return false;
2607 akorotkov@postgresql 351 :CBC 80 : *(int32 *) (buf->data + offs) = chld - pos;
352 : : }
353 : 80 : break;
354 : 1520 : case jpiFilter:
355 : 1520 : argNestingLevel++;
356 : : pg_fallthrough;
357 : 3560 : case jpiIsUnknown:
358 : : case jpiNot:
359 : : case jpiPlus:
360 : : case jpiMinus:
361 : : case jpiExists:
362 : : case jpiDatetime:
363 : : case jpiTime:
364 : : case jpiTimeTz:
365 : : case jpiTimestamp:
366 : : case jpiTimestampTz:
367 : : case jpiStrLtrim:
368 : : case jpiStrRtrim:
369 : : case jpiStrBtrim:
370 : : {
371 : 3560 : int32 arg = reserveSpaceForItemPointer(buf);
372 : :
1228 andrew@dunslane.net 373 [ + + ]: 3560 : if (!item->value.arg)
374 : 1128 : chld = pos;
1082 tgl@sss.pgh.pa.us 375 [ - + ]: 2432 : else if (!flattenJsonPathParseItem(buf, &chld, escontext,
376 : : item->value.arg,
377 : : nestingLevel + argNestingLevel,
378 : : insideArraySubscript))
1228 andrew@dunslane.net 379 :UBC 0 : return false;
2607 akorotkov@postgresql 380 :CBC 3556 : *(int32 *) (buf->data + arg) = chld - pos;
381 : : }
382 : 3556 : break;
383 : 76 : case jpiNull:
384 : 76 : break;
385 : 7122 : case jpiRoot:
386 : 7122 : break;
387 : 1472 : case jpiAnyArray:
388 : : case jpiAnyKey:
389 : 1472 : break;
390 : 1712 : case jpiCurrent:
391 [ + + ]: 1712 : if (nestingLevel <= 0)
1228 andrew@dunslane.net 392 [ + + ]: 12 : ereturn(escontext, false,
393 : : (errcode(ERRCODE_SYNTAX_ERROR),
394 : : errmsg("@ is not allowed in root expressions")));
2607 akorotkov@postgresql 395 : 1700 : break;
396 : 60 : case jpiLast:
397 [ + + ]: 60 : if (!insideArraySubscript)
1228 andrew@dunslane.net 398 [ + - ]: 8 : ereturn(escontext, false,
399 : : (errcode(ERRCODE_SYNTAX_ERROR),
400 : : errmsg("LAST is allowed only in array subscripts")));
2607 akorotkov@postgresql 401 : 52 : break;
402 : 340 : case jpiIndexArray:
403 : : {
404 : 340 : int32 nelems = item->value.array.nelems;
405 : : int offset;
406 : : int i;
407 : :
1222 peter@eisentraut.org 408 : 340 : appendBinaryStringInfo(buf, &nelems, sizeof(nelems));
409 : :
2607 akorotkov@postgresql 410 : 340 : offset = buf->len;
411 : :
412 : 340 : appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
413 : :
414 [ + + ]: 712 : for (i = 0; i < nelems; i++)
415 : : {
416 : : int32 *ppos;
417 : : int32 topos;
418 : : int32 frompos;
419 : :
1082 tgl@sss.pgh.pa.us 420 [ - + ]: 372 : if (!flattenJsonPathParseItem(buf, &frompos, escontext,
421 : 372 : item->value.array.elems[i].from,
422 : : nestingLevel, true))
1228 andrew@dunslane.net 423 :UBC 0 : return false;
1228 andrew@dunslane.net 424 :CBC 372 : frompos -= pos;
425 : :
2607 akorotkov@postgresql 426 [ + + ]: 372 : if (item->value.array.elems[i].to)
427 : : {
1082 tgl@sss.pgh.pa.us 428 [ - + ]: 32 : if (!flattenJsonPathParseItem(buf, &topos, escontext,
429 : 32 : item->value.array.elems[i].to,
430 : : nestingLevel, true))
1228 andrew@dunslane.net 431 :UBC 0 : return false;
1228 andrew@dunslane.net 432 :CBC 32 : topos -= pos;
433 : : }
434 : : else
2607 akorotkov@postgresql 435 : 340 : topos = 0;
436 : :
437 : 372 : ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
438 : :
439 : 372 : ppos[0] = frompos;
440 : 372 : ppos[1] = topos;
441 : : }
442 : : }
443 : 340 : break;
444 : 236 : case jpiAny:
445 : 236 : appendBinaryStringInfo(buf,
1222 peter@eisentraut.org 446 : 236 : &item->value.anybounds.first,
447 : : sizeof(item->value.anybounds.first));
2607 akorotkov@postgresql 448 : 236 : appendBinaryStringInfo(buf,
1222 peter@eisentraut.org 449 : 236 : &item->value.anybounds.last,
450 : : sizeof(item->value.anybounds.last));
2607 akorotkov@postgresql 451 : 236 : break;
452 : 1472 : case jpiType:
453 : : case jpiSize:
454 : : case jpiAbs:
455 : : case jpiFloor:
456 : : case jpiCeiling:
457 : : case jpiDouble:
458 : : case jpiKeyValue:
459 : : case jpiBigint:
460 : : case jpiBoolean:
461 : : case jpiDate:
462 : : case jpiInteger:
463 : : case jpiNumber:
464 : : case jpiStringFunc:
465 : : case jpiStrLower:
466 : : case jpiStrUpper:
467 : : case jpiStrInitcap:
468 : 1472 : break;
2607 akorotkov@postgresql 469 :UBC 0 : default:
470 [ # # ]: 0 : elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
471 : : }
472 : :
2607 akorotkov@postgresql 473 [ + + ]:CBC 24690 : if (item->next)
474 : : {
1082 tgl@sss.pgh.pa.us 475 [ - + ]: 9624 : if (!flattenJsonPathParseItem(buf, &chld, escontext,
476 : : item->next, nestingLevel,
477 : : insideArraySubscript))
1228 andrew@dunslane.net 478 :UBC 0 : return false;
1228 andrew@dunslane.net 479 :CBC 9620 : chld -= pos;
2607 akorotkov@postgresql 480 : 9620 : *(int32 *) (buf->data + next) = chld;
481 : : }
482 : :
1228 andrew@dunslane.net 483 [ + + ]: 24686 : if (result)
484 : 17348 : *result = pos;
485 : 24686 : return true;
486 : : }
487 : :
488 : : /*
489 : : * Align StringInfo to int by adding zero padding bytes
490 : : */
491 : : static void
2607 akorotkov@postgresql 492 : 24730 : alignStringInfoInt(StringInfo buf)
493 : : {
494 [ + + + + ]: 24730 : switch (INTALIGN(buf->len) - buf->len)
495 : : {
496 : 22542 : case 3:
497 [ - + ]: 22542 : appendStringInfoCharMacro(buf, 0);
498 : : pg_fallthrough;
499 : : case 2:
500 [ - + ]: 22814 : appendStringInfoCharMacro(buf, 0);
501 : : pg_fallthrough;
502 : : case 1:
503 [ - + ]: 24430 : appendStringInfoCharMacro(buf, 0);
504 : : pg_fallthrough;
505 : : default:
506 : 24730 : break;
507 : : }
508 : 24730 : }
509 : :
510 : : /*
511 : : * Reserve space for int32 JsonPathItem pointer. Now zero pointer is written,
512 : : * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
513 : : */
514 : : static int32
515 : 33442 : reserveSpaceForItemPointer(StringInfo buf)
516 : : {
517 : 33442 : int32 pos = buf->len;
518 : 33442 : int32 ptr = 0;
519 : :
1222 peter@eisentraut.org 520 : 33442 : appendBinaryStringInfo(buf, &ptr, sizeof(ptr));
521 : :
2607 akorotkov@postgresql 522 : 33442 : return pos;
523 : : }
524 : :
525 : : /*
526 : : * Prints text representation of given jsonpath item and all its children.
527 : : */
528 : : static void
529 : 4636 : printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
530 : : bool printBracketes)
531 : : {
532 : : JsonPathItem elem;
533 : : int i;
534 : : int32 len;
535 : : char *str;
536 : :
537 : 4636 : check_stack_depth();
538 [ - + ]: 4636 : CHECK_FOR_INTERRUPTS();
539 : :
540 [ + + + + : 4636 : switch (v->type)
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + - ]
541 : : {
542 : 28 : case jpiNull:
543 : 28 : appendStringInfoString(buf, "null");
544 : 28 : break;
545 : 88 : case jpiString:
647 drowley@postgresql.o 546 : 88 : str = jspGetString(v, &len);
547 : 88 : escape_json_with_len(buf, str, len);
2607 akorotkov@postgresql 548 : 88 : break;
549 : 656 : case jpiNumeric:
1499 peter@eisentraut.org 550 [ + + ]: 656 : if (jspHasNext(v))
551 : 56 : appendStringInfoChar(buf, '(');
2607 akorotkov@postgresql 552 : 656 : appendStringInfoString(buf,
553 : 656 : DatumGetCString(DirectFunctionCall1(numeric_out,
554 : : NumericGetDatum(jspGetNumeric(v)))));
1499 peter@eisentraut.org 555 [ + + ]: 656 : if (jspHasNext(v))
556 : 56 : appendStringInfoChar(buf, ')');
2607 akorotkov@postgresql 557 : 656 : break;
558 : 8 : case jpiBool:
559 [ + + ]: 8 : if (jspGetBool(v))
1222 peter@eisentraut.org 560 : 4 : appendStringInfoString(buf, "true");
561 : : else
562 : 4 : appendStringInfoString(buf, "false");
2607 akorotkov@postgresql 563 : 8 : break;
564 : 524 : case jpiAnd:
565 : : case jpiOr:
566 : : case jpiEqual:
567 : : case jpiNotEqual:
568 : : case jpiLess:
569 : : case jpiGreater:
570 : : case jpiLessOrEqual:
571 : : case jpiGreaterOrEqual:
572 : : case jpiAdd:
573 : : case jpiSub:
574 : : case jpiMul:
575 : : case jpiDiv:
576 : : case jpiMod:
577 : : case jpiStartsWith:
578 [ + + ]: 524 : if (printBracketes)
579 : 76 : appendStringInfoChar(buf, '(');
580 : 524 : jspGetLeftArg(v, &elem);
581 : 524 : printJsonPathItem(buf, &elem, false,
582 : 524 : operationPriority(elem.type) <=
583 : 524 : operationPriority(v->type));
584 : 524 : appendStringInfoChar(buf, ' ');
585 : 524 : appendStringInfoString(buf, jspOperationName(v->type));
586 : 524 : appendStringInfoChar(buf, ' ');
587 : 524 : jspGetRightArg(v, &elem);
588 : 524 : printJsonPathItem(buf, &elem, false,
589 : 524 : operationPriority(elem.type) <=
590 : 524 : operationPriority(v->type));
591 [ + + ]: 524 : if (printBracketes)
592 : 76 : appendStringInfoChar(buf, ')');
593 : 524 : break;
594 : 8 : case jpiNot:
1222 peter@eisentraut.org 595 : 8 : appendStringInfoString(buf, "!(");
2607 akorotkov@postgresql 596 : 8 : jspGetArg(v, &elem);
597 : 8 : printJsonPathItem(buf, &elem, false, false);
598 : 8 : appendStringInfoChar(buf, ')');
599 : 8 : break;
600 : 4 : case jpiIsUnknown:
601 : 4 : appendStringInfoChar(buf, '(');
602 : 4 : jspGetArg(v, &elem);
603 : 4 : printJsonPathItem(buf, &elem, false, false);
1222 peter@eisentraut.org 604 : 4 : appendStringInfoString(buf, ") is unknown");
2607 akorotkov@postgresql 605 : 4 : break;
853 peter@eisentraut.org 606 : 32 : case jpiPlus:
607 : : case jpiMinus:
608 [ + + ]: 32 : if (printBracketes)
609 : 12 : appendStringInfoChar(buf, '(');
610 [ + + ]: 32 : appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
2607 akorotkov@postgresql 611 : 32 : jspGetArg(v, &elem);
853 peter@eisentraut.org 612 : 32 : printJsonPathItem(buf, &elem, false,
613 : 32 : operationPriority(elem.type) <=
614 : 32 : operationPriority(v->type));
615 [ + + ]: 32 : if (printBracketes)
616 : 12 : appendStringInfoChar(buf, ')');
2607 akorotkov@postgresql 617 : 32 : break;
618 : 144 : case jpiAnyArray:
1222 peter@eisentraut.org 619 : 144 : appendStringInfoString(buf, "[*]");
2607 akorotkov@postgresql 620 : 144 : break;
621 : 8 : case jpiAnyKey:
622 [ + - ]: 8 : if (inKey)
623 : 8 : appendStringInfoChar(buf, '.');
624 : 8 : appendStringInfoChar(buf, '*');
625 : 8 : break;
626 : 64 : case jpiIndexArray:
627 : 64 : appendStringInfoChar(buf, '[');
628 [ + + ]: 140 : for (i = 0; i < v->content.array.nelems; i++)
629 : : {
630 : : JsonPathItem from;
631 : : JsonPathItem to;
632 : 76 : bool range = jspGetArraySubscript(v, &from, &to, i);
633 : :
634 [ + + ]: 76 : if (i)
635 : 12 : appendStringInfoChar(buf, ',');
636 : :
637 : 76 : printJsonPathItem(buf, &from, false, false);
638 : :
639 [ + + ]: 76 : if (range)
640 : : {
1222 peter@eisentraut.org 641 : 8 : appendStringInfoString(buf, " to ");
2607 akorotkov@postgresql 642 : 8 : printJsonPathItem(buf, &to, false, false);
643 : : }
644 : : }
645 : 64 : appendStringInfoChar(buf, ']');
646 : 64 : break;
647 : 32 : case jpiAny:
648 [ + - ]: 32 : if (inKey)
649 : 32 : appendStringInfoChar(buf, '.');
650 : :
651 [ + + ]: 32 : if (v->content.anybounds.first == 0 &&
652 [ + + ]: 8 : v->content.anybounds.last == PG_UINT32_MAX)
1222 peter@eisentraut.org 653 : 4 : appendStringInfoString(buf, "**");
2607 akorotkov@postgresql 654 [ + + ]: 28 : else if (v->content.anybounds.first == v->content.anybounds.last)
655 : : {
656 [ + + ]: 12 : if (v->content.anybounds.first == PG_UINT32_MAX)
2028 drowley@postgresql.o 657 : 4 : appendStringInfoString(buf, "**{last}");
658 : : else
2607 akorotkov@postgresql 659 : 8 : appendStringInfo(buf, "**{%u}",
660 : : v->content.anybounds.first);
661 : : }
662 [ + + ]: 16 : else if (v->content.anybounds.first == PG_UINT32_MAX)
663 : 4 : appendStringInfo(buf, "**{last to %u}",
664 : : v->content.anybounds.last);
665 [ + + ]: 12 : else if (v->content.anybounds.last == PG_UINT32_MAX)
666 : 4 : appendStringInfo(buf, "**{%u to last}",
667 : : v->content.anybounds.first);
668 : : else
669 : 8 : appendStringInfo(buf, "**{%u to %u}",
670 : : v->content.anybounds.first,
671 : : v->content.anybounds.last);
672 : 32 : break;
853 peter@eisentraut.org 673 : 884 : case jpiKey:
674 [ + - ]: 884 : if (inKey)
675 : 884 : appendStringInfoChar(buf, '.');
647 drowley@postgresql.o 676 : 884 : str = jspGetString(v, &len);
677 : 884 : escape_json_with_len(buf, str, len);
853 peter@eisentraut.org 678 : 884 : break;
679 : 400 : case jpiCurrent:
680 [ - + ]: 400 : Assert(!inKey);
681 : 400 : appendStringInfoChar(buf, '@');
682 : 400 : break;
683 : 1124 : case jpiRoot:
684 [ - + ]: 1124 : Assert(!inKey);
685 : 1124 : appendStringInfoChar(buf, '$');
686 : 1124 : break;
687 : 48 : case jpiVariable:
688 : 48 : appendStringInfoChar(buf, '$');
647 drowley@postgresql.o 689 : 48 : str = jspGetString(v, &len);
690 : 48 : escape_json_with_len(buf, str, len);
853 peter@eisentraut.org 691 : 48 : break;
692 : 352 : case jpiFilter:
693 : 352 : appendStringInfoString(buf, "?(");
694 : 352 : jspGetArg(v, &elem);
695 : 352 : printJsonPathItem(buf, &elem, false, false);
696 : 352 : appendStringInfoChar(buf, ')');
697 : 352 : break;
698 : 16 : case jpiExists:
699 : 16 : appendStringInfoString(buf, "exists (");
700 : 16 : jspGetArg(v, &elem);
701 : 16 : printJsonPathItem(buf, &elem, false, false);
702 : 16 : appendStringInfoChar(buf, ')');
703 : 16 : break;
2607 akorotkov@postgresql 704 : 20 : case jpiType:
1222 peter@eisentraut.org 705 : 20 : appendStringInfoString(buf, ".type()");
2607 akorotkov@postgresql 706 : 20 : break;
707 : 4 : case jpiSize:
1222 peter@eisentraut.org 708 : 4 : appendStringInfoString(buf, ".size()");
2607 akorotkov@postgresql 709 : 4 : break;
710 : 4 : case jpiAbs:
1222 peter@eisentraut.org 711 : 4 : appendStringInfoString(buf, ".abs()");
2607 akorotkov@postgresql 712 : 4 : break;
853 peter@eisentraut.org 713 : 4 : case jpiFloor:
714 : 4 : appendStringInfoString(buf, ".floor()");
715 : 4 : break;
2607 akorotkov@postgresql 716 : 4 : case jpiCeiling:
1222 peter@eisentraut.org 717 : 4 : appendStringInfoString(buf, ".ceiling()");
2607 akorotkov@postgresql 718 : 4 : break;
853 peter@eisentraut.org 719 : 4 : case jpiDouble:
720 : 4 : appendStringInfoString(buf, ".double()");
2607 akorotkov@postgresql 721 : 4 : break;
2414 722 : 8 : case jpiDatetime:
1222 peter@eisentraut.org 723 : 8 : appendStringInfoString(buf, ".datetime(");
2414 akorotkov@postgresql 724 [ + + ]: 8 : if (v->content.arg)
725 : : {
726 : 4 : jspGetArg(v, &elem);
727 : 4 : printJsonPathItem(buf, &elem, false, false);
728 : : }
729 : 8 : appendStringInfoChar(buf, ')');
730 : 8 : break;
2607 731 : 4 : case jpiKeyValue:
1222 peter@eisentraut.org 732 : 4 : appendStringInfoString(buf, ".keyvalue()");
2607 akorotkov@postgresql 733 : 4 : break;
853 peter@eisentraut.org 734 : 8 : case jpiLast:
735 : 8 : appendStringInfoString(buf, "last");
736 : 8 : break;
737 : 32 : case jpiLikeRegex:
738 [ - + ]: 32 : if (printBracketes)
853 peter@eisentraut.org 739 :UBC 0 : appendStringInfoChar(buf, '(');
740 : :
853 peter@eisentraut.org 741 :CBC 32 : jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
742 : 32 : printJsonPathItem(buf, &elem, false,
743 : 32 : operationPriority(elem.type) <=
744 : 32 : operationPriority(v->type));
745 : :
746 : 32 : appendStringInfoString(buf, " like_regex ");
747 : :
647 drowley@postgresql.o 748 : 32 : escape_json_with_len(buf,
749 : 32 : v->content.like_regex.pattern,
750 : : v->content.like_regex.patternlen);
751 : :
853 peter@eisentraut.org 752 [ + + ]: 32 : if (v->content.like_regex.flags)
753 : : {
754 : 24 : appendStringInfoString(buf, " flag \"");
755 : :
756 [ + + ]: 24 : if (v->content.like_regex.flags & JSP_REGEX_ICASE)
757 : 20 : appendStringInfoChar(buf, 'i');
758 [ + + ]: 24 : if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
759 : 12 : appendStringInfoChar(buf, 's');
760 [ + + ]: 24 : if (v->content.like_regex.flags & JSP_REGEX_MLINE)
761 : 8 : appendStringInfoChar(buf, 'm');
762 [ + + ]: 24 : if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
763 : 4 : appendStringInfoChar(buf, 'x');
764 [ + + ]: 24 : if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
765 : 12 : appendStringInfoChar(buf, 'q');
766 : :
767 : 24 : appendStringInfoChar(buf, '"');
768 : : }
769 : :
770 [ - + ]: 32 : if (printBracketes)
853 peter@eisentraut.org 771 :UBC 0 : appendStringInfoChar(buf, ')');
853 peter@eisentraut.org 772 :CBC 32 : break;
831 andrew@dunslane.net 773 : 4 : case jpiBigint:
774 : 4 : appendStringInfoString(buf, ".bigint()");
775 : 4 : break;
776 : 4 : case jpiBoolean:
777 : 4 : appendStringInfoString(buf, ".boolean()");
778 : 4 : break;
779 : 4 : case jpiDate:
780 : 4 : appendStringInfoString(buf, ".date()");
781 : 4 : break;
782 : 8 : case jpiDecimal:
783 : 8 : appendStringInfoString(buf, ".decimal(");
784 [ + + ]: 8 : if (v->content.args.left)
785 : : {
786 : 4 : jspGetLeftArg(v, &elem);
787 : 4 : printJsonPathItem(buf, &elem, false, false);
788 : : }
789 [ + + ]: 8 : if (v->content.args.right)
790 : : {
791 : 4 : appendStringInfoChar(buf, ',');
792 : 4 : jspGetRightArg(v, &elem);
793 : 4 : printJsonPathItem(buf, &elem, false, false);
794 : : }
795 : 8 : appendStringInfoChar(buf, ')');
796 : 8 : break;
797 : 4 : case jpiInteger:
798 : 4 : appendStringInfoString(buf, ".integer()");
799 : 4 : break;
800 : 4 : case jpiNumber:
801 : 4 : appendStringInfoString(buf, ".number()");
802 : 4 : break;
803 : 4 : case jpiStringFunc:
804 : 4 : appendStringInfoString(buf, ".string()");
805 : 4 : break;
806 : 8 : case jpiTime:
807 : 8 : appendStringInfoString(buf, ".time(");
808 [ + + ]: 8 : if (v->content.arg)
809 : : {
810 : 4 : jspGetArg(v, &elem);
811 : 4 : printJsonPathItem(buf, &elem, false, false);
812 : : }
813 : 8 : appendStringInfoChar(buf, ')');
814 : 8 : break;
815 : 8 : case jpiTimeTz:
816 : 8 : appendStringInfoString(buf, ".time_tz(");
817 [ + + ]: 8 : if (v->content.arg)
818 : : {
819 : 4 : jspGetArg(v, &elem);
820 : 4 : printJsonPathItem(buf, &elem, false, false);
821 : : }
822 : 8 : appendStringInfoChar(buf, ')');
823 : 8 : break;
824 : 8 : case jpiTimestamp:
825 : 8 : appendStringInfoString(buf, ".timestamp(");
826 [ + + ]: 8 : if (v->content.arg)
827 : : {
828 : 4 : jspGetArg(v, &elem);
829 : 4 : printJsonPathItem(buf, &elem, false, false);
830 : : }
831 : 8 : appendStringInfoChar(buf, ')');
832 : 8 : break;
833 : 8 : case jpiTimestampTz:
834 : 8 : appendStringInfoString(buf, ".timestamp_tz(");
835 [ + + ]: 8 : if (v->content.arg)
836 : : {
837 : 4 : jspGetArg(v, &elem);
838 : 4 : printJsonPathItem(buf, &elem, false, false);
839 : : }
840 : 8 : appendStringInfoChar(buf, ')');
841 : 8 : break;
33 andrew@dunslane.net 842 :GNC 8 : case jpiStrReplace:
843 : 8 : appendStringInfoString(buf, ".replace(");
844 : 8 : jspGetLeftArg(v, &elem);
845 : 8 : printJsonPathItem(buf, &elem, false, false);
846 : 8 : appendStringInfoChar(buf, ',');
847 : 8 : jspGetRightArg(v, &elem);
848 : 8 : printJsonPathItem(buf, &elem, false, false);
849 : 8 : appendStringInfoChar(buf, ')');
850 : 8 : break;
851 : 12 : case jpiStrLower:
852 : 12 : appendStringInfoString(buf, ".lower()");
853 : 12 : break;
854 : 8 : case jpiStrUpper:
855 : 8 : appendStringInfoString(buf, ".upper()");
856 : 8 : break;
857 : 4 : case jpiStrSplitPart:
858 : 4 : appendStringInfoString(buf, ".split_part(");
859 : 4 : jspGetLeftArg(v, &elem);
860 : 4 : printJsonPathItem(buf, &elem, false, false);
861 : 4 : appendStringInfoChar(buf, ',');
862 : 4 : jspGetRightArg(v, &elem);
863 : 4 : printJsonPathItem(buf, &elem, false, false);
864 : 4 : appendStringInfoChar(buf, ')');
865 : 4 : break;
866 : 8 : case jpiStrLtrim:
867 : 8 : appendStringInfoString(buf, ".ltrim(");
868 [ + + ]: 8 : if (v->content.arg)
869 : : {
870 : 4 : jspGetArg(v, &elem);
871 : 4 : printJsonPathItem(buf, &elem, false, false);
872 : : }
873 : 8 : appendStringInfoChar(buf, ')');
874 : 8 : break;
875 : 8 : case jpiStrRtrim:
876 : 8 : appendStringInfoString(buf, ".rtrim(");
877 [ + + ]: 8 : if (v->content.arg)
878 : : {
879 : 4 : jspGetArg(v, &elem);
880 : 4 : printJsonPathItem(buf, &elem, false, false);
881 : : }
882 : 8 : appendStringInfoChar(buf, ')');
883 : 8 : break;
884 : 8 : case jpiStrBtrim:
885 : 8 : appendStringInfoString(buf, ".btrim(");
886 [ + + ]: 8 : if (v->content.arg)
887 : : {
888 : 4 : jspGetArg(v, &elem);
889 : 4 : printJsonPathItem(buf, &elem, false, false);
890 : : }
891 : 8 : appendStringInfoChar(buf, ')');
892 : 8 : break;
893 : 4 : case jpiStrInitcap:
894 : 4 : appendStringInfoString(buf, ".initcap()");
895 : 4 : break;
2607 akorotkov@postgresql 896 :UBC 0 : default:
897 [ # # ]: 0 : elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
898 : : }
899 : :
2607 akorotkov@postgresql 900 [ + + ]:CBC 4636 : if (jspGetNext(v, &elem))
901 : 1660 : printJsonPathItem(buf, &elem, true, true);
902 : 4636 : }
903 : :
904 : : const char *
905 : 1160 : jspOperationName(JsonPathItemType type)
906 : : {
907 [ + + + + : 1160 : switch (type)
+ + + + +
+ + + + -
+ + + + +
+ + + - +
+ + + + +
+ + + + +
+ + + + -
- + - - ]
908 : : {
909 : 20 : case jpiAnd:
910 : 20 : return "&&";
911 : 36 : case jpiOr:
912 : 36 : return "||";
913 : 112 : case jpiEqual:
914 : 112 : return "==";
915 : 4 : case jpiNotEqual:
916 : 4 : return "!=";
917 : 200 : case jpiLess:
918 : 200 : return "<";
919 : 28 : case jpiGreater:
920 : 28 : return ">";
921 : 4 : case jpiLessOrEqual:
922 : 4 : return "<=";
923 : 28 : case jpiGreaterOrEqual:
924 : 28 : return ">=";
853 peter@eisentraut.org 925 : 56 : case jpiAdd:
926 : : case jpiPlus:
2607 akorotkov@postgresql 927 : 56 : return "+";
853 peter@eisentraut.org 928 : 24 : case jpiSub:
929 : : case jpiMinus:
2607 akorotkov@postgresql 930 : 24 : return "-";
931 : 16 : case jpiMul:
932 : 16 : return "*";
933 : 4 : case jpiDiv:
934 : 4 : return "/";
935 : 4 : case jpiMod:
936 : 4 : return "%";
2607 akorotkov@postgresql 937 :UBC 0 : case jpiType:
938 : 0 : return "type";
2607 akorotkov@postgresql 939 :CBC 4 : case jpiSize:
940 : 4 : return "size";
941 : 4 : case jpiAbs:
942 : 4 : return "abs";
853 peter@eisentraut.org 943 : 4 : case jpiFloor:
944 : 4 : return "floor";
945 : 4 : case jpiCeiling:
946 : 4 : return "ceiling";
947 : 40 : case jpiDouble:
948 : 40 : return "double";
2414 akorotkov@postgresql 949 : 20 : case jpiDatetime:
950 : 20 : return "datetime";
853 peter@eisentraut.org 951 : 12 : case jpiKeyValue:
952 : 12 : return "keyvalue";
953 : 8 : case jpiStartsWith:
954 : 8 : return "starts with";
853 peter@eisentraut.org 955 :UBC 0 : case jpiLikeRegex:
956 : 0 : return "like_regex";
831 andrew@dunslane.net 957 :CBC 52 : case jpiBigint:
958 : 52 : return "bigint";
959 : 48 : case jpiBoolean:
960 : 48 : return "boolean";
961 : 24 : case jpiDate:
962 : 24 : return "date";
963 : 52 : case jpiDecimal:
964 : 52 : return "decimal";
965 : 52 : case jpiInteger:
966 : 52 : return "integer";
967 : 36 : case jpiNumber:
968 : 36 : return "number";
969 : 12 : case jpiStringFunc:
970 : 12 : return "string";
971 : 28 : case jpiTime:
972 : 28 : return "time";
973 : 28 : case jpiTimeTz:
974 : 28 : return "time_tz";
975 : 28 : case jpiTimestamp:
976 : 28 : return "timestamp";
977 : 28 : case jpiTimestampTz:
978 : 28 : return "timestamp_tz";
33 andrew@dunslane.net 979 :GNC 12 : case jpiStrReplace:
980 : 12 : return "replace";
981 : 32 : case jpiStrLower:
982 : 32 : return "lower";
983 : 32 : case jpiStrUpper:
984 : 32 : return "upper";
985 : 32 : case jpiStrLtrim:
986 : 32 : return "ltrim";
33 andrew@dunslane.net 987 :UNC 0 : case jpiStrRtrim:
988 : 0 : return "rtrim";
989 : 0 : case jpiStrBtrim:
990 : 0 : return "btrim";
33 andrew@dunslane.net 991 :GNC 32 : case jpiStrInitcap:
992 : 32 : return "initcap";
33 andrew@dunslane.net 993 :UNC 0 : case jpiStrSplitPart:
994 : 0 : return "split_part";
2607 akorotkov@postgresql 995 :UBC 0 : default:
996 [ # # ]: 0 : elog(ERROR, "unrecognized jsonpath item type: %d", type);
997 : : return NULL;
998 : : }
999 : : }
1000 : :
1001 : : static int
2607 akorotkov@postgresql 1002 :CBC 2224 : operationPriority(JsonPathItemType op)
1003 : : {
1004 [ + + + + : 2224 : switch (op)
+ + + ]
1005 : : {
1006 : 76 : case jpiOr:
1007 : 76 : return 0;
1008 : 52 : case jpiAnd:
1009 : 52 : return 1;
1010 : 852 : case jpiEqual:
1011 : : case jpiNotEqual:
1012 : : case jpiLess:
1013 : : case jpiGreater:
1014 : : case jpiLessOrEqual:
1015 : : case jpiGreaterOrEqual:
1016 : : case jpiStartsWith:
1017 : 852 : return 2;
1018 : 168 : case jpiAdd:
1019 : : case jpiSub:
1020 : 168 : return 3;
1021 : 44 : case jpiMul:
1022 : : case jpiDiv:
1023 : : case jpiMod:
1024 : 44 : return 4;
1025 : 56 : case jpiPlus:
1026 : : case jpiMinus:
1027 : 56 : return 5;
1028 : 976 : default:
1029 : 976 : return 6;
1030 : : }
1031 : : }
1032 : :
1033 : : /******************* Support functions for JsonPath *************************/
1034 : :
1035 : : /*
1036 : : * Support macros to read stored values
1037 : : */
1038 : :
1039 : : #define read_byte(v, b, p) do { \
1040 : : (v) = *(uint8*)((b) + (p)); \
1041 : : (p) += 1; \
1042 : : } while(0) \
1043 : :
1044 : : #define read_int32(v, b, p) do { \
1045 : : (v) = *(uint32*)((b) + (p)); \
1046 : : (p) += sizeof(int32); \
1047 : : } while(0) \
1048 : :
1049 : : #define read_int32_n(v, b, p, n) do { \
1050 : : (v) = (void *)((b) + (p)); \
1051 : : (p) += sizeof(int32) * (n); \
1052 : : } while(0) \
1053 : :
1054 : : /*
1055 : : * Read root node and fill root node representation
1056 : : */
1057 : : void
1058 : 135019 : jspInit(JsonPathItem *v, JsonPath *js)
1059 : : {
1060 [ - + ]: 135019 : Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
1061 : 135019 : jspInitByBuffer(v, js->data, 0);
1062 : 135019 : }
1063 : :
1064 : : /*
1065 : : * Read node from buffer and fill its representation
1066 : : */
1067 : : void
1068 : 460838 : jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
1069 : : {
1070 : 460838 : v->base = base + pos;
1071 : :
1072 : 460838 : read_byte(v->type, base, pos);
1073 : 460838 : pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
1074 : 460838 : read_int32(v->nextPos, base, pos);
1075 : :
1076 [ + + + + : 460838 : switch (v->type)
+ + + +
- ]
1077 : : {
1078 : 169324 : case jpiNull:
1079 : : case jpiRoot:
1080 : : case jpiCurrent:
1081 : : case jpiAnyArray:
1082 : : case jpiAnyKey:
1083 : : case jpiType:
1084 : : case jpiSize:
1085 : : case jpiAbs:
1086 : : case jpiFloor:
1087 : : case jpiCeiling:
1088 : : case jpiDouble:
1089 : : case jpiKeyValue:
1090 : : case jpiLast:
1091 : : case jpiBigint:
1092 : : case jpiBoolean:
1093 : : case jpiDate:
1094 : : case jpiInteger:
1095 : : case jpiNumber:
1096 : : case jpiStringFunc:
1097 : : case jpiStrLower:
1098 : : case jpiStrUpper:
1099 : : case jpiStrInitcap:
1100 : 169324 : break;
1101 : 135909 : case jpiString:
1102 : : case jpiKey:
1103 : : case jpiVariable:
1104 : 135909 : read_int32(v->content.value.datalen, base, pos);
1105 : : pg_fallthrough;
1106 : 151661 : case jpiNumeric:
1107 : : case jpiBool:
1108 : 151661 : v->content.value.data = base + pos;
1109 : 151661 : break;
1110 : 67734 : case jpiAnd:
1111 : : case jpiOr:
1112 : : case jpiEqual:
1113 : : case jpiNotEqual:
1114 : : case jpiLess:
1115 : : case jpiGreater:
1116 : : case jpiLessOrEqual:
1117 : : case jpiGreaterOrEqual:
1118 : : case jpiAdd:
1119 : : case jpiSub:
1120 : : case jpiMul:
1121 : : case jpiDiv:
1122 : : case jpiMod:
1123 : : case jpiStartsWith:
1124 : : case jpiDecimal:
1125 : : case jpiStrReplace:
1126 : : case jpiStrSplitPart:
1127 : 67734 : read_int32(v->content.args.left, base, pos);
1128 : 67734 : read_int32(v->content.args.right, base, pos);
1129 : 67734 : break;
1130 : 71141 : case jpiNot:
1131 : : case jpiIsUnknown:
1132 : : case jpiExists:
1133 : : case jpiPlus:
1134 : : case jpiMinus:
1135 : : case jpiFilter:
1136 : : case jpiDatetime:
1137 : : case jpiTime:
1138 : : case jpiTimeTz:
1139 : : case jpiTimestamp:
1140 : : case jpiTimestampTz:
1141 : : case jpiStrLtrim:
1142 : : case jpiStrRtrim:
1143 : : case jpiStrBtrim:
1144 : 71141 : read_int32(v->content.arg, base, pos);
1145 : 71141 : break;
1146 : 421 : case jpiIndexArray:
1147 : 421 : read_int32(v->content.array.nelems, base, pos);
1148 : 421 : read_int32_n(v->content.array.elems, base, pos,
1149 : : v->content.array.nelems * 2);
1150 : 421 : break;
1151 : 261 : case jpiAny:
1152 : 261 : read_int32(v->content.anybounds.first, base, pos);
1153 : 261 : read_int32(v->content.anybounds.last, base, pos);
1154 : 261 : break;
853 peter@eisentraut.org 1155 : 296 : case jpiLikeRegex:
1156 : 296 : read_int32(v->content.like_regex.flags, base, pos);
1157 : 296 : read_int32(v->content.like_regex.expr, base, pos);
1158 : 296 : read_int32(v->content.like_regex.patternlen, base, pos);
1159 : 296 : v->content.like_regex.pattern = base + pos;
1160 : 296 : break;
2607 akorotkov@postgresql 1161 :UBC 0 : default:
1162 [ # # ]: 0 : elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
1163 : : }
2607 akorotkov@postgresql 1164 :CBC 460838 : }
1165 : :
1166 : : void
1167 : 70919 : jspGetArg(JsonPathItem *v, JsonPathItem *a)
1168 : : {
853 peter@eisentraut.org 1169 [ + + + + : 70919 : Assert(v->type == jpiNot ||
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + - + ]
1170 : : v->type == jpiIsUnknown ||
1171 : : v->type == jpiPlus ||
1172 : : v->type == jpiMinus ||
1173 : : v->type == jpiFilter ||
1174 : : v->type == jpiExists ||
1175 : : v->type == jpiDatetime ||
1176 : : v->type == jpiTime ||
1177 : : v->type == jpiTimeTz ||
1178 : : v->type == jpiTimestamp ||
1179 : : v->type == jpiTimestampTz ||
1180 : : v->type == jpiStrLtrim ||
1181 : : v->type == jpiStrRtrim ||
1182 : : v->type == jpiStrBtrim);
1183 : :
2607 akorotkov@postgresql 1184 : 70919 : jspInitByBuffer(a, v->base, v->content.arg);
1185 : 70919 : }
1186 : :
1187 : : bool
1188 : 303685 : jspGetNext(JsonPathItem *v, JsonPathItem *a)
1189 : : {
1190 [ + + ]: 303685 : if (jspHasNext(v))
1191 : : {
853 peter@eisentraut.org 1192 [ + + + + : 135810 : Assert(v->type == jpiNull ||
+ + + + +
- + - + -
+ - + - +
- + - + +
+ - + - +
+ + + + +
+ - + - +
- + + + +
+ + + + +
+ + + + +
+ + + + +
+ + - + -
+ - + + +
+ + + + +
+ + + + +
+ + - + -
+ + + + +
+ + - + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ - - + -
- - - ]
1193 : : v->type == jpiString ||
1194 : : v->type == jpiNumeric ||
1195 : : v->type == jpiBool ||
1196 : : v->type == jpiAnd ||
1197 : : v->type == jpiOr ||
1198 : : v->type == jpiNot ||
1199 : : v->type == jpiIsUnknown ||
1200 : : v->type == jpiEqual ||
1201 : : v->type == jpiNotEqual ||
1202 : : v->type == jpiLess ||
1203 : : v->type == jpiGreater ||
1204 : : v->type == jpiLessOrEqual ||
1205 : : v->type == jpiGreaterOrEqual ||
1206 : : v->type == jpiAdd ||
1207 : : v->type == jpiSub ||
1208 : : v->type == jpiMul ||
1209 : : v->type == jpiDiv ||
1210 : : v->type == jpiMod ||
1211 : : v->type == jpiPlus ||
1212 : : v->type == jpiMinus ||
1213 : : v->type == jpiAnyArray ||
1214 : : v->type == jpiAnyKey ||
1215 : : v->type == jpiIndexArray ||
1216 : : v->type == jpiAny ||
1217 : : v->type == jpiKey ||
1218 : : v->type == jpiCurrent ||
1219 : : v->type == jpiRoot ||
1220 : : v->type == jpiVariable ||
1221 : : v->type == jpiFilter ||
1222 : : v->type == jpiExists ||
1223 : : v->type == jpiType ||
1224 : : v->type == jpiSize ||
1225 : : v->type == jpiAbs ||
1226 : : v->type == jpiFloor ||
1227 : : v->type == jpiCeiling ||
1228 : : v->type == jpiDouble ||
1229 : : v->type == jpiDatetime ||
1230 : : v->type == jpiKeyValue ||
1231 : : v->type == jpiLast ||
1232 : : v->type == jpiStartsWith ||
1233 : : v->type == jpiLikeRegex ||
1234 : : v->type == jpiBigint ||
1235 : : v->type == jpiBoolean ||
1236 : : v->type == jpiDate ||
1237 : : v->type == jpiDecimal ||
1238 : : v->type == jpiInteger ||
1239 : : v->type == jpiNumber ||
1240 : : v->type == jpiStringFunc ||
1241 : : v->type == jpiTime ||
1242 : : v->type == jpiTimeTz ||
1243 : : v->type == jpiTimestamp ||
1244 : : v->type == jpiTimestampTz ||
1245 : : v->type == jpiStrReplace ||
1246 : : v->type == jpiStrLower ||
1247 : : v->type == jpiStrUpper ||
1248 : : v->type == jpiStrLtrim ||
1249 : : v->type == jpiStrRtrim ||
1250 : : v->type == jpiStrBtrim ||
1251 : : v->type == jpiStrInitcap ||
1252 : : v->type == jpiStrSplitPart);
1253 : :
2607 akorotkov@postgresql 1254 [ + - ]: 135810 : if (a)
1255 : 135810 : jspInitByBuffer(a, v->base, v->nextPos);
1256 : 135810 : return true;
1257 : : }
1258 : :
1259 : 167875 : return false;
1260 : : }
1261 : :
1262 : : void
1263 : 67598 : jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
1264 : : {
1265 [ + + + + : 67598 : Assert(v->type == jpiAnd ||
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + -
+ ]
1266 : : v->type == jpiOr ||
1267 : : v->type == jpiEqual ||
1268 : : v->type == jpiNotEqual ||
1269 : : v->type == jpiLess ||
1270 : : v->type == jpiGreater ||
1271 : : v->type == jpiLessOrEqual ||
1272 : : v->type == jpiGreaterOrEqual ||
1273 : : v->type == jpiAdd ||
1274 : : v->type == jpiSub ||
1275 : : v->type == jpiMul ||
1276 : : v->type == jpiDiv ||
1277 : : v->type == jpiMod ||
1278 : : v->type == jpiStartsWith ||
1279 : : v->type == jpiDecimal ||
1280 : : v->type == jpiStrReplace ||
1281 : : v->type == jpiStrSplitPart);
1282 : :
1283 : 67598 : jspInitByBuffer(a, v->base, v->content.args.left);
1284 : 67598 : }
1285 : :
1286 : : void
1287 : 50718 : jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
1288 : : {
1289 [ + + + + : 50718 : Assert(v->type == jpiAnd ||
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + -
+ ]
1290 : : v->type == jpiOr ||
1291 : : v->type == jpiEqual ||
1292 : : v->type == jpiNotEqual ||
1293 : : v->type == jpiLess ||
1294 : : v->type == jpiGreater ||
1295 : : v->type == jpiLessOrEqual ||
1296 : : v->type == jpiGreaterOrEqual ||
1297 : : v->type == jpiAdd ||
1298 : : v->type == jpiSub ||
1299 : : v->type == jpiMul ||
1300 : : v->type == jpiDiv ||
1301 : : v->type == jpiMod ||
1302 : : v->type == jpiStartsWith ||
1303 : : v->type == jpiDecimal ||
1304 : : v->type == jpiStrReplace ||
1305 : : v->type == jpiStrSplitPart);
1306 : :
1307 : 50718 : jspInitByBuffer(a, v->base, v->content.args.right);
1308 : 50718 : }
1309 : :
1310 : : bool
1311 : 958 : jspGetBool(JsonPathItem *v)
1312 : : {
1313 [ - + ]: 958 : Assert(v->type == jpiBool);
1314 : :
1315 : 958 : return (bool) *v->content.value.data;
1316 : : }
1317 : :
1318 : : Numeric
1319 : 14644 : jspGetNumeric(JsonPathItem *v)
1320 : : {
1321 [ - + ]: 14644 : Assert(v->type == jpiNumeric);
1322 : :
1323 : 14644 : return (Numeric) v->content.value.data;
1324 : : }
1325 : :
1326 : : char *
1327 : 135296 : jspGetString(JsonPathItem *v, int32 *len)
1328 : : {
1329 [ + + + + : 135296 : Assert(v->type == jpiKey ||
- + ]
1330 : : v->type == jpiString ||
1331 : : v->type == jpiVariable);
1332 : :
1333 [ + + ]: 135296 : if (len)
1334 : 135140 : *len = v->content.value.datalen;
1335 : 135296 : return v->content.value.data;
1336 : : }
1337 : :
1338 : : bool
1339 : 445 : jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
1340 : : int i)
1341 : : {
1342 [ - + ]: 445 : Assert(v->type == jpiIndexArray);
1343 : :
1344 : 445 : jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
1345 : :
1346 [ + + ]: 445 : if (!v->content.array.elems[i].to)
1347 : 412 : return false;
1348 : :
1349 : 33 : jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
1350 : :
1351 : 33 : return true;
1352 : : }
1353 : :
1354 : : /* SQL/JSON datatype status: */
1355 : : enum JsonPathDatatypeStatus
1356 : : {
1357 : : jpdsNonDateTime, /* null, bool, numeric, string, array, object */
1358 : : jpdsUnknownDateTime, /* unknown datetime type */
1359 : : jpdsDateTimeZoned, /* timetz, timestamptz */
1360 : : jpdsDateTimeNonZoned, /* time, timestamp, date */
1361 : : };
1362 : :
1363 : : /* Context for jspIsMutableWalker() */
1364 : : struct JsonPathMutableContext
1365 : : {
1366 : : List *varnames; /* list of variable names */
1367 : : List *varexprs; /* list of variable expressions */
1368 : : enum JsonPathDatatypeStatus current; /* status of @ item */
1369 : : bool lax; /* jsonpath is lax or strict */
1370 : : bool mutable; /* resulting mutability status */
1371 : : };
1372 : :
1373 : : static enum JsonPathDatatypeStatus jspIsMutableWalker(JsonPathItem *jpi,
1374 : : struct JsonPathMutableContext *cxt);
1375 : :
1376 : : /*
1377 : : * Function to check whether jsonpath expression is mutable to be used in the
1378 : : * planner function contain_mutable_functions().
1379 : : */
1380 : : bool
775 amitlan@postgresql.o 1381 : 188 : jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
1382 : : {
1383 : : struct JsonPathMutableContext cxt;
1384 : : JsonPathItem jpi;
1385 : :
1386 : 188 : cxt.varnames = varnames;
1387 : 188 : cxt.varexprs = varexprs;
1388 : 188 : cxt.current = jpdsNonDateTime;
1389 : 188 : cxt.lax = (path->header & JSONPATH_LAX) != 0;
1390 : 188 : cxt.mutable = false;
1391 : :
1392 : 188 : jspInit(&jpi, path);
1393 : 188 : (void) jspIsMutableWalker(&jpi, &cxt);
1394 : :
1395 : 188 : return cxt.mutable;
1396 : : }
1397 : :
1398 : : /*
1399 : : * Recursive walker for jspIsMutable()
1400 : : */
1401 : : static enum JsonPathDatatypeStatus
1402 : 556 : jspIsMutableWalker(JsonPathItem *jpi, struct JsonPathMutableContext *cxt)
1403 : : {
1404 : : JsonPathItem next;
1405 : 556 : enum JsonPathDatatypeStatus status = jpdsNonDateTime;
1406 : :
1407 [ + + ]: 956 : while (!cxt->mutable)
1408 : : {
1409 : : JsonPathItem arg;
1410 : : enum JsonPathDatatypeStatus leftStatus;
1411 : : enum JsonPathDatatypeStatus rightStatus;
1412 : :
1413 [ + + + + : 892 : switch (jpi->type)
+ - - + -
- + - + +
+ + - ]
1414 : : {
1415 : 224 : case jpiRoot:
1416 [ - + ]: 224 : Assert(status == jpdsNonDateTime);
1417 : 224 : break;
1418 : :
1419 : 96 : case jpiCurrent:
1420 [ - + ]: 96 : Assert(status == jpdsNonDateTime);
1421 : 96 : status = cxt->current;
1422 : 96 : break;
1423 : :
1424 : 96 : case jpiFilter:
1425 : : {
1426 : 96 : enum JsonPathDatatypeStatus prevStatus = cxt->current;
1427 : :
1428 : 96 : cxt->current = status;
1429 : 96 : jspGetArg(jpi, &arg);
1430 : 96 : jspIsMutableWalker(&arg, cxt);
1431 : :
1432 : 96 : cxt->current = prevStatus;
1433 : 96 : break;
1434 : : }
1435 : :
1436 : 36 : case jpiVariable:
1437 : : {
1438 : : int32 len;
1439 : 36 : const char *name = jspGetString(jpi, &len);
1440 : : ListCell *lc1;
1441 : : ListCell *lc2;
1442 : :
1443 [ - + ]: 36 : Assert(status == jpdsNonDateTime);
1444 : :
1445 [ + - + + : 40 : forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
+ - + + +
+ + - +
+ ]
1446 : : {
1447 : 36 : String *varname = lfirst_node(String, lc1);
1448 : 36 : Node *varexpr = lfirst(lc2);
1449 : :
1450 [ + + ]: 36 : if (strncmp(varname->sval, name, len))
1451 : 4 : continue;
1452 : :
1453 [ + + + ]: 32 : switch (exprType(varexpr))
1454 : : {
1455 : 20 : case DATEOID:
1456 : : case TIMEOID:
1457 : : case TIMESTAMPOID:
1458 : 20 : status = jpdsDateTimeNonZoned;
1459 : 20 : break;
1460 : :
1461 : 8 : case TIMETZOID:
1462 : : case TIMESTAMPTZOID:
1463 : 8 : status = jpdsDateTimeZoned;
1464 : 8 : break;
1465 : :
1466 : 4 : default:
1467 : 4 : status = jpdsNonDateTime;
1468 : 4 : break;
1469 : : }
1470 : :
1471 : 32 : break;
1472 : : }
1473 : 36 : break;
1474 : : }
1475 : :
1476 : 120 : case jpiEqual:
1477 : : case jpiNotEqual:
1478 : : case jpiLess:
1479 : : case jpiGreater:
1480 : : case jpiLessOrEqual:
1481 : : case jpiGreaterOrEqual:
1482 [ - + ]: 120 : Assert(status == jpdsNonDateTime);
1483 : 120 : jspGetLeftArg(jpi, &arg);
1484 : 120 : leftStatus = jspIsMutableWalker(&arg, cxt);
1485 : :
1486 : 120 : jspGetRightArg(jpi, &arg);
1487 : 120 : rightStatus = jspIsMutableWalker(&arg, cxt);
1488 : :
1489 : : /*
1490 : : * Comparison of datetime type with different timezone status
1491 : : * is mutable.
1492 : : */
1493 [ + + + + ]: 120 : if (leftStatus != jpdsNonDateTime &&
1494 [ + + ]: 48 : rightStatus != jpdsNonDateTime &&
1495 [ + - ]: 24 : (leftStatus == jpdsUnknownDateTime ||
1496 [ + + ]: 24 : rightStatus == jpdsUnknownDateTime ||
1497 : : leftStatus != rightStatus))
1498 : 28 : cxt->mutable = true;
1499 : 120 : break;
1500 : :
775 amitlan@postgresql.o 1501 :UBC 0 : case jpiNot:
1502 : : case jpiIsUnknown:
1503 : : case jpiExists:
1504 : : case jpiPlus:
1505 : : case jpiMinus:
1506 [ # # ]: 0 : Assert(status == jpdsNonDateTime);
1507 : 0 : jspGetArg(jpi, &arg);
1508 : 0 : jspIsMutableWalker(&arg, cxt);
1509 : 0 : break;
1510 : :
1511 : 0 : case jpiAnd:
1512 : : case jpiOr:
1513 : : case jpiAdd:
1514 : : case jpiSub:
1515 : : case jpiMul:
1516 : : case jpiDiv:
1517 : : case jpiMod:
1518 : : case jpiStartsWith:
1519 [ # # ]: 0 : Assert(status == jpdsNonDateTime);
1520 : 0 : jspGetLeftArg(jpi, &arg);
1521 : 0 : jspIsMutableWalker(&arg, cxt);
1522 : 0 : jspGetRightArg(jpi, &arg);
1523 : 0 : jspIsMutableWalker(&arg, cxt);
1524 : 0 : break;
1525 : :
775 amitlan@postgresql.o 1526 :CBC 16 : case jpiIndexArray:
1527 [ + + ]: 44 : for (int i = 0; i < jpi->content.array.nelems; i++)
1528 : : {
1529 : : JsonPathItem from;
1530 : : JsonPathItem to;
1531 : :
1532 [ + + ]: 28 : if (jspGetArraySubscript(jpi, &from, &to, i))
1533 : 4 : jspIsMutableWalker(&to, cxt);
1534 : :
1535 : 28 : jspIsMutableWalker(&from, cxt);
1536 : : }
1537 : : pg_fallthrough;
1538 : :
1539 : : case jpiAnyArray:
1540 [ - + ]: 16 : if (!cxt->lax)
775 amitlan@postgresql.o 1541 :UBC 0 : status = jpdsNonDateTime;
775 amitlan@postgresql.o 1542 :CBC 16 : break;
1543 : :
775 amitlan@postgresql.o 1544 :UBC 0 : case jpiAny:
1545 [ # # ]: 0 : if (jpi->content.anybounds.first > 0)
1546 : 0 : status = jpdsNonDateTime;
1547 : 0 : break;
1548 : :
775 amitlan@postgresql.o 1549 :CBC 84 : case jpiDatetime:
1550 [ + + ]: 84 : if (jpi->content.arg)
1551 : : {
1552 : : char *template;
1553 : :
1554 : 44 : jspGetArg(jpi, &arg);
1555 [ - + ]: 44 : if (arg.type != jpiString)
1556 : : {
775 amitlan@postgresql.o 1557 :UBC 0 : status = jpdsNonDateTime;
1558 : 0 : break; /* there will be runtime error */
1559 : : }
1560 : :
775 amitlan@postgresql.o 1561 :CBC 44 : template = jspGetString(&arg, NULL);
1562 [ + + ]: 44 : if (datetime_format_has_tz(template))
1563 : 24 : status = jpdsDateTimeZoned;
1564 : : else
1565 : 20 : status = jpdsDateTimeNonZoned;
1566 : : }
1567 : : else
1568 : : {
1569 : 40 : status = jpdsUnknownDateTime;
1570 : : }
1571 : 84 : break;
1572 : :
775 amitlan@postgresql.o 1573 :UBC 0 : case jpiLikeRegex:
1574 [ # # ]: 0 : Assert(status == jpdsNonDateTime);
1575 : 0 : jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
1576 : 0 : jspIsMutableWalker(&arg, cxt);
1577 : 0 : break;
1578 : :
1579 : : /* literals */
775 amitlan@postgresql.o 1580 :CBC 16 : case jpiNull:
1581 : : case jpiString:
1582 : : case jpiNumeric:
1583 : : case jpiBool:
1584 : 16 : break;
1585 : : /* accessors */
1586 : 124 : case jpiKey:
1587 : : case jpiAnyKey:
1588 : : /* special items */
1589 : : case jpiSubscript:
1590 : : case jpiLast:
1591 : : /* item methods */
1592 : : case jpiType:
1593 : : case jpiSize:
1594 : : case jpiAbs:
1595 : : case jpiFloor:
1596 : : case jpiCeiling:
1597 : : case jpiDouble:
1598 : : case jpiKeyValue:
1599 : : case jpiBigint:
1600 : : case jpiBoolean:
1601 : : case jpiDecimal:
1602 : : case jpiInteger:
1603 : : case jpiNumber:
1604 : : case jpiStringFunc:
1605 : : case jpiStrReplace:
1606 : : case jpiStrLower:
1607 : : case jpiStrUpper:
1608 : : case jpiStrLtrim:
1609 : : case jpiStrRtrim:
1610 : : case jpiStrBtrim:
1611 : : case jpiStrInitcap:
1612 : : case jpiStrSplitPart:
1613 : 124 : status = jpdsNonDateTime;
1614 : 124 : break;
1615 : :
1616 : 60 : case jpiTime:
1617 : : case jpiDate:
1618 : : case jpiTimestamp:
1619 : 60 : status = jpdsDateTimeNonZoned;
1620 : 60 : cxt->mutable = true;
1621 : 60 : break;
1622 : :
1623 : 20 : case jpiTimeTz:
1624 : : case jpiTimestampTz:
1625 : 20 : status = jpdsDateTimeNonZoned;
1626 : 20 : cxt->mutable = true;
1627 : 20 : break;
1628 : :
1629 : : }
1630 : :
1631 [ + + ]: 892 : if (!jspGetNext(jpi, &next))
1632 : 492 : break;
1633 : :
1634 : 400 : jpi = &next;
1635 : : }
1636 : :
1637 : 556 : return status;
1638 : : }
|