Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * txtquery io
3 : : * Teodor Sigaev <teodor@stack.net>
4 : : * contrib/ltree/ltxtquery_io.c
5 : : */
6 : : #include "postgres.h"
7 : :
8 : : #include <ctype.h>
9 : :
10 : : #include "crc32.h"
11 : : #include "libpq/pqformat.h"
12 : : #include "ltree.h"
13 : : #include "miscadmin.h"
14 : : #include "nodes/miscnodes.h"
15 : : #include "varatt.h"
16 : :
17 : :
18 : : /* parser's states */
19 : : #define WAITOPERAND 1
20 : : #define INOPERAND 2
21 : : #define WAITOPERATOR 3
22 : :
23 : : /*
24 : : * node of query tree, also used
25 : : * for storing polish notation in parser
26 : : */
27 : : typedef struct NODE
28 : : {
29 : : int32 type;
30 : : int32 val;
31 : : int16 distance;
32 : : int16 length;
33 : : uint16 flag;
34 : : struct NODE *next;
35 : : } NODE;
36 : :
37 : : typedef struct
38 : : {
39 : : char *buf;
40 : : int32 state;
41 : : int32 count;
42 : : struct Node *escontext;
43 : : /* reverse polish notation in list (for temporary usage) */
44 : : NODE *str;
45 : : /* number in str */
46 : : int32 num;
47 : :
48 : : /* user-friendly operand */
49 : : int32 lenop;
50 : : int32 sumlen;
51 : : char *op;
52 : : char *curop;
53 : : } QPRS_STATE;
54 : :
55 : : /*
56 : : * get token from query string
57 : : *
58 : : * caller needs to check if a soft-error was set if the result is ERR.
59 : : */
60 : : static int32
5087 peter_e@gmx.net 61 :CBC 65644 : gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint16 *flag)
62 : : {
63 : : int charlen;
64 : :
65 : : for (;;)
66 : : {
143 tmunro@postgresql.or 67 : 82286 : charlen = pg_mblen_cstr(state->buf);
68 : :
8705 bruce@momjian.us 69 [ + + + - ]: 82286 : switch (state->state)
70 : : {
71 : 32851 : case WAITOPERAND:
1332 tgl@sss.pgh.pa.us 72 [ + + ]: 32851 : if (t_iseq(state->buf, '!'))
73 : : {
8705 bruce@momjian.us 74 : 6 : (state->buf)++;
5087 peter_e@gmx.net 75 : 6 : *val = (int32) '!';
8705 bruce@momjian.us 76 : 6 : return OPR;
77 : : }
1332 tgl@sss.pgh.pa.us 78 [ + + ]: 32845 : else if (t_iseq(state->buf, '('))
79 : : {
8705 bruce@momjian.us 80 : 16383 : state->count++;
81 : 16383 : (state->buf)++;
82 : 16383 : return OPEN;
83 : : }
1240 andrew@dunslane.net 84 [ + + + - : 16462 : else if (ISLABEL(state->buf))
- + ]
85 : : {
8705 bruce@momjian.us 86 : 16435 : state->state = INOPERAND;
87 : 16435 : *strval = state->buf;
6543 teodor@sigaev.ru 88 : 16435 : *lenval = charlen;
8705 bruce@momjian.us 89 : 16435 : *flag = 0;
90 : : }
529 peter@eisentraut.org 91 [ + + ]: 27 : else if (!isspace((unsigned char) *state->buf))
1249 andrew@dunslane.net 92 [ + + ]: 2 : ereturn(state->escontext, ERR,
93 : : (errcode(ERRCODE_SYNTAX_ERROR),
94 : : errmsg("operand syntax error")));
8705 bruce@momjian.us 95 : 16460 : break;
96 : 16592 : case INOPERAND:
1240 andrew@dunslane.net 97 [ + + + + : 16592 : if (ISLABEL(state->buf))
+ + ]
98 : : {
8669 bruce@momjian.us 99 [ - + ]: 127 : if (*flag)
1249 andrew@dunslane.net 100 [ # # ]:UBC 0 : ereturn(state->escontext, ERR,
101 : : (errcode(ERRCODE_SYNTAX_ERROR),
102 : : errmsg("modifiers syntax error")));
6543 teodor@sigaev.ru 103 :CBC 127 : *lenval += charlen;
104 : : }
1332 tgl@sss.pgh.pa.us 105 [ + + ]: 16465 : else if (t_iseq(state->buf, '%'))
7395 neilc@samurai.com 106 : 4 : *flag |= LVAR_SUBLEXEME;
1332 tgl@sss.pgh.pa.us 107 [ + + ]: 16461 : else if (t_iseq(state->buf, '@'))
8705 bruce@momjian.us 108 : 12 : *flag |= LVAR_INCASE;
1332 tgl@sss.pgh.pa.us 109 [ + + ]: 16449 : else if (t_iseq(state->buf, '*'))
8705 bruce@momjian.us 110 : 14 : *flag |= LVAR_ANYEND;
111 : : else
112 : : {
113 : 16435 : state->state = WAITOPERATOR;
114 : 16435 : return VAL;
115 : : }
116 : 157 : break;
117 : 32843 : case WAITOPERATOR:
1332 tgl@sss.pgh.pa.us 118 [ + + + + ]: 32843 : if (t_iseq(state->buf, '&') || t_iseq(state->buf, '|'))
119 : : {
8705 bruce@momjian.us 120 : 16409 : state->state = WAITOPERAND;
5087 peter_e@gmx.net 121 : 16409 : *val = (int32) *(state->buf);
8705 bruce@momjian.us 122 : 16409 : (state->buf)++;
123 : 16409 : return OPR;
124 : : }
1332 tgl@sss.pgh.pa.us 125 [ + + ]: 16434 : else if (t_iseq(state->buf, ')'))
126 : : {
8705 bruce@momjian.us 127 : 16383 : (state->buf)++;
128 : 16383 : state->count--;
129 [ - + ]: 16383 : return (state->count < 0) ? ERR : CLOSE;
130 : : }
131 [ + + ]: 51 : else if (*(state->buf) == '\0')
132 : : {
133 : 26 : return (state->count) ? ERR : END;
134 : : }
1332 tgl@sss.pgh.pa.us 135 [ - + ]: 25 : else if (!t_iseq(state->buf, ' '))
136 : : {
8705 bruce@momjian.us 137 :UBC 0 : return ERR;
138 : : }
8705 bruce@momjian.us 139 :CBC 25 : break;
8705 bruce@momjian.us 140 :UBC 0 : default:
141 : 0 : return ERR;
142 : : break;
143 : : }
144 : :
6543 teodor@sigaev.ru 145 :CBC 16642 : state->buf += charlen;
146 : : }
147 : :
148 : : /* should not get here */
149 : : }
150 : :
151 : : /*
152 : : * push new one in polish notation reverse view
153 : : */
154 : : static bool
5087 peter_e@gmx.net 155 : 32850 : pushquery(QPRS_STATE *state, int32 type, int32 val, int32 distance, int32 lenval, uint16 flag)
156 : : {
176 michael@paquier.xyz 157 :GNC 32850 : NODE *tmp = palloc_object(NODE);
158 : :
8705 bruce@momjian.us 159 :CBC 32850 : tmp->type = type;
160 : 32850 : tmp->val = val;
161 : 32850 : tmp->flag = flag;
162 [ - + ]: 32850 : if (distance > 0xffff)
1249 andrew@dunslane.net 163 [ # # ]:UBC 0 : ereturn(state->escontext, false,
164 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
165 : : errmsg("value is too big")));
8705 bruce@momjian.us 166 [ - + ]:CBC 32850 : if (lenval > 0xff)
1249 andrew@dunslane.net 167 [ # # ]:UBC 0 : ereturn(state->escontext, false,
168 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
169 : : errmsg("operand is too long")));
8705 bruce@momjian.us 170 :CBC 32850 : tmp->distance = distance;
171 : 32850 : tmp->length = lenval;
172 : 32850 : tmp->next = state->str;
173 : 32850 : state->str = tmp;
174 : 32850 : state->num++;
1249 andrew@dunslane.net 175 : 32850 : return true;
176 : : }
177 : :
178 : : /*
179 : : * This function is used for query text parsing
180 : : */
181 : : static bool
6197 bruce@momjian.us 182 : 16435 : pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag)
183 : : {
8705 184 [ - + ]: 16435 : if (lenval > 0xffff)
1249 andrew@dunslane.net 185 [ # # ]:UBC 0 : ereturn(state->escontext, false,
186 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
187 : : errmsg("word is too long")));
188 : :
1107 tgl@sss.pgh.pa.us 189 [ - + ]:CBC 16435 : if (!pushquery(state, type, ltree_crc32_sz(strval, lenval),
190 : 16435 : state->curop - state->op, lenval, flag))
1249 andrew@dunslane.net 191 :UBC 0 : return false;
192 : :
8705 bruce@momjian.us 193 [ + + ]:CBC 16445 : while (state->curop - state->op + lenval + 1 >= state->lenop)
194 : : {
5087 peter_e@gmx.net 195 : 10 : int32 tmp = state->curop - state->op;
196 : :
8705 bruce@momjian.us 197 : 10 : state->lenop *= 2;
1208 peter@eisentraut.org 198 : 10 : state->op = (char *) repalloc(state->op, state->lenop);
8705 bruce@momjian.us 199 : 10 : state->curop = state->op + tmp;
200 : : }
1208 peter@eisentraut.org 201 : 16435 : memcpy(state->curop, strval, lenval);
8705 bruce@momjian.us 202 : 16435 : state->curop += lenval;
203 : 16435 : *(state->curop) = '\0';
204 : 16435 : state->curop++;
205 : 16435 : state->sumlen += lenval + 1;
1249 andrew@dunslane.net 206 : 16435 : return true;
207 : : }
208 : :
209 : : #define STACKDEPTH 32
210 : : /*
211 : : * make polish notation of query
212 : : */
213 : : static int32
6197 bruce@momjian.us 214 : 16411 : makepol(QPRS_STATE *state)
215 : : {
5087 peter_e@gmx.net 216 : 16411 : int32 val = 0,
217 : : type;
218 : 16411 : int32 lenval = 0;
7553 tgl@sss.pgh.pa.us 219 : 16411 : char *strval = NULL;
220 : : int32 stack[STACKDEPTH];
5087 peter_e@gmx.net 221 : 16411 : int32 lenstack = 0;
7553 tgl@sss.pgh.pa.us 222 : 16411 : uint16 flag = 0;
223 : :
224 : : /* since this function recurses, it could be driven to stack overflow */
4485 noah@leadboat.com 225 : 16411 : check_stack_depth();
226 : :
8669 bruce@momjian.us 227 [ + + ]: 65644 : while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END)
228 : : {
8705 229 [ + + + + : 65618 : switch (type)
+ - ]
230 : : {
231 : 16435 : case VAL:
1249 andrew@dunslane.net 232 [ - + ]: 16435 : if (!pushval_asis(state, VAL, strval, lenval, flag))
1249 andrew@dunslane.net 233 :UBC 0 : return ERR;
5087 peter_e@gmx.net 234 [ + + + + ]:CBC 24656 : while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
235 [ + + ]: 8 : stack[lenstack - 1] == (int32) '!'))
236 : : {
8705 bruce@momjian.us 237 : 8221 : lenstack--;
1249 andrew@dunslane.net 238 [ - + ]: 8221 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
1249 andrew@dunslane.net 239 :UBC 0 : return ERR;
240 : : }
8705 bruce@momjian.us 241 :CBC 16435 : break;
242 : 16415 : case OPR:
5087 peter_e@gmx.net 243 [ - + - - ]: 16415 : if (lenstack && val == (int32) '|')
244 : : {
1249 andrew@dunslane.net 245 [ # # ]:UBC 0 : if (!pushquery(state, OPR, val, 0, 0, 0))
246 : 0 : return ERR;
247 : : }
248 : : else
249 : : {
8705 bruce@momjian.us 250 [ - + ]:CBC 16415 : if (lenstack == STACKDEPTH)
251 : : /* internal error */
8346 tgl@sss.pgh.pa.us 252 [ # # ]:UBC 0 : elog(ERROR, "stack too short");
8705 bruce@momjian.us 253 :CBC 16415 : stack[lenstack] = val;
254 : 16415 : lenstack++;
255 : : }
256 : 16415 : break;
257 : 16383 : case OPEN:
258 [ - + ]: 16383 : if (makepol(state) == ERR)
8705 bruce@momjian.us 259 :UBC 0 : return ERR;
5087 peter_e@gmx.net 260 [ + + + + ]:CBC 24574 : while (lenstack && (stack[lenstack - 1] == (int32) '&' ||
261 [ - + ]: 1 : stack[lenstack - 1] == (int32) '!'))
262 : : {
8705 bruce@momjian.us 263 : 8191 : lenstack--;
1249 andrew@dunslane.net 264 [ - + ]: 8191 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
1249 andrew@dunslane.net 265 :UBC 0 : return ERR;
266 : : }
8705 bruce@momjian.us 267 :CBC 16383 : break;
268 : 16383 : case CLOSE:
269 [ - + ]: 16383 : while (lenstack)
270 : : {
8705 bruce@momjian.us 271 :UBC 0 : lenstack--;
1249 andrew@dunslane.net 272 [ # # ]: 0 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
273 : 0 : return ERR;
274 : : };
8705 bruce@momjian.us 275 :CBC 16383 : return END;
276 : : break;
277 : 2 : case ERR:
1249 andrew@dunslane.net 278 [ + - + - : 2 : if (SOFT_ERROR_OCCURRED(state->escontext))
+ - ]
279 : 2 : return ERR;
280 : : pg_fallthrough;
281 : : default:
1249 andrew@dunslane.net 282 [ # # ]:UBC 0 : ereturn(state->escontext, ERR,
283 : : (errcode(ERRCODE_SYNTAX_ERROR),
284 : : errmsg("syntax error")));
285 : :
286 : : }
287 : : }
8669 bruce@momjian.us 288 [ + + ]:CBC 29 : while (lenstack)
289 : : {
8705 290 : 3 : lenstack--;
1249 andrew@dunslane.net 291 [ - + ]: 3 : if (!pushquery(state, OPR, stack[lenstack], 0, 0, 0))
1249 andrew@dunslane.net 292 :UBC 0 : return ERR;
293 : : };
8705 bruce@momjian.us 294 :CBC 26 : return END;
295 : : }
296 : :
297 : : /*
298 : : * Recursively fill the "left" fields of an ITEM array that represents
299 : : * a valid postfix tree.
300 : : *
301 : : * state: only needed for error reporting
302 : : * ptr: starting element of array
303 : : * pos: in/out argument, the array index this call is responsible to fill
304 : : *
305 : : * At exit, *pos has been incremented to point after the sub-tree whose
306 : : * top is the entry-time value of *pos.
307 : : *
308 : : * Returns true if okay, false if error (the only possible error is
309 : : * overflow of a "left" field).
310 : : */
311 : : static bool
19 tgl@sss.pgh.pa.us 312 : 32849 : findoprnd(QPRS_STATE *state, ITEM *ptr, int32 *pos)
313 : : {
314 : : int32 mypos;
315 : :
316 : : /* since this function recurses, it could be driven to stack overflow. */
4485 noah@leadboat.com 317 : 32849 : check_stack_depth();
318 : :
319 : : /* get the position this call is supposed to update */
19 tgl@sss.pgh.pa.us 320 : 32849 : mypos = *pos;
321 : :
322 : : /* in all cases, we should increment *pos to advance over this item */
323 : 32849 : (*pos)++;
324 : :
325 [ + + - + ]: 32849 : if (ptr[mypos].type == VAL || ptr[mypos].type == VALTRUE)
326 : : {
327 : : /* base case: a VAL has no operand, so just set its left to zero */
328 : 16434 : ptr[mypos].left = 0;
329 : : }
330 [ + + ]: 16415 : else if (ptr[mypos].val == (int32) '!')
331 : : {
332 : : /* unary operator, likewise easy: operand is just after it */
333 : 6 : ptr[mypos].left = 1;
334 : : /* recurse to scan operand */
335 [ - + ]: 6 : if (!findoprnd(state, ptr, pos))
19 tgl@sss.pgh.pa.us 336 :UBC 0 : return false;
337 : : }
338 : : else
339 : : {
340 : : /* binary operator */
341 : : int32 delta;
342 : :
343 : : /* recurse to scan right operand */
19 tgl@sss.pgh.pa.us 344 [ - + ]:CBC 16409 : if (!findoprnd(state, ptr, pos))
19 tgl@sss.pgh.pa.us 345 :UBC 0 : return false;
346 : : /* we must fill left with offset to left operand's top */
347 : : /* delta can't overflow, see LTXTQUERY_TOO_BIG ... */
19 tgl@sss.pgh.pa.us 348 :CBC 16409 : delta = *pos - mypos;
349 : : /* ... but it might be too large to fit in the 16-bit left field */
350 [ - + ]: 16409 : Assert(delta > 0);
351 [ + + ]: 16409 : if (unlikely(delta > PG_INT16_MAX))
352 [ + - ]: 1 : ereturn(state->escontext, false,
353 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
354 : : errmsg("ltxtquery is too large")));
355 : 16408 : ptr[mypos].left = (int16) delta;
356 : : /* recurse to scan left operand */
357 [ - + ]: 16408 : if (!findoprnd(state, ptr, pos))
19 tgl@sss.pgh.pa.us 358 :UBC 0 : return false;
359 : : }
360 : :
19 tgl@sss.pgh.pa.us 361 :CBC 32848 : return true;
362 : : }
363 : :
364 : :
365 : : /*
366 : : * input
367 : : */
368 : : static ltxtquery *
1249 andrew@dunslane.net 369 : 28 : queryin(char *buf, struct Node *escontext)
370 : : {
371 : : QPRS_STATE state;
372 : : int32 i;
373 : : ltxtquery *query;
374 : : int32 commonlen;
375 : : ITEM *ptr;
376 : : NODE *tmp;
5087 peter_e@gmx.net 377 : 28 : int32 pos = 0;
378 : :
379 : : /* init state */
8705 bruce@momjian.us 380 : 28 : state.buf = buf;
381 : 28 : state.state = WAITOPERAND;
382 : 28 : state.count = 0;
383 : 28 : state.num = 0;
384 : 28 : state.str = NULL;
1249 andrew@dunslane.net 385 : 28 : state.escontext = escontext;
386 : :
387 : : /* init list of operand */
8705 bruce@momjian.us 388 : 28 : state.sumlen = 0;
389 : 28 : state.lenop = 64;
390 : 28 : state.curop = state.op = (char *) palloc(state.lenop);
391 : 28 : *(state.curop) = '\0';
392 : :
393 : : /* parse query & make polish notation (postfix, but in reverse order) */
1249 andrew@dunslane.net 394 [ + + ]: 28 : if (makepol(&state) == ERR)
395 : 2 : return NULL;
8705 bruce@momjian.us 396 [ - + ]: 26 : if (!state.num)
1249 andrew@dunslane.net 397 [ # # ]:UBC 0 : ereturn(escontext, NULL,
398 : : (errcode(ERRCODE_SYNTAX_ERROR),
399 : : errmsg("syntax error"),
400 : : errdetail("Empty query.")));
401 : :
4485 noah@leadboat.com 402 [ - + ]:CBC 26 : if (LTXTQUERY_TOO_BIG(state.num, state.sumlen))
1249 andrew@dunslane.net 403 [ # # ]:UBC 0 : ereturn(escontext, NULL,
404 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
405 : : errmsg("ltxtquery is too large")));
8705 bruce@momjian.us 406 :CBC 26 : commonlen = COMPUTESIZE(state.num, state.sumlen);
407 : :
3735 andres@anarazel.de 408 : 26 : query = (ltxtquery *) palloc0(commonlen);
7031 tgl@sss.pgh.pa.us 409 : 26 : SET_VARSIZE(query, commonlen);
8705 bruce@momjian.us 410 : 26 : query->size = state.num;
411 : 26 : ptr = GETQUERY(query);
412 : :
413 : : /* set item in polish notation */
414 [ + + ]: 32876 : for (i = 0; i < state.num; i++)
415 : : {
416 : 32850 : ptr[i].type = state.str->type;
417 : 32850 : ptr[i].val = state.str->val;
418 : 32850 : ptr[i].distance = state.str->distance;
419 : 32850 : ptr[i].length = state.str->length;
420 : 32850 : ptr[i].flag = state.str->flag;
421 : 32850 : tmp = state.str->next;
422 : 32850 : pfree(state.str);
423 : 32850 : state.str = tmp;
424 : : }
425 : :
426 : : /* set user-friendly operand view */
1208 peter@eisentraut.org 427 : 26 : memcpy(GETOPERAND(query), state.op, state.sumlen);
8705 bruce@momjian.us 428 : 26 : pfree(state.op);
429 : :
430 : : /* set left operand's position for every operator */
431 : 26 : pos = 0;
19 tgl@sss.pgh.pa.us 432 [ - + ]: 26 : if (!findoprnd(&state, ptr, &pos))
19 tgl@sss.pgh.pa.us 433 :UBC 0 : return NULL;
434 : : /* if successful, findoprnd should have scanned the whole array */
19 tgl@sss.pgh.pa.us 435 [ - + ]:CBC 25 : Assert(pos == state.num);
436 : :
8705 bruce@momjian.us 437 : 25 : return query;
438 : : }
439 : :
440 : : /*
441 : : * in without morphology
442 : : */
2250 tgl@sss.pgh.pa.us 443 : 3 : PG_FUNCTION_INFO_V1(ltxtq_in);
444 : : Datum
8705 bruce@momjian.us 445 : 28 : ltxtq_in(PG_FUNCTION_ARGS)
446 : : {
447 : : ltxtquery *res;
448 : :
464 peter@eisentraut.org 449 [ + + ]: 28 : if ((res = queryin(PG_GETARG_POINTER(0), fcinfo->context)) == NULL)
1249 andrew@dunslane.net 450 : 2 : PG_RETURN_NULL();
451 : 25 : PG_RETURN_POINTER(res);
452 : : }
453 : :
454 : : /*
455 : : * ltxtquery type recv function
456 : : *
457 : : * The type is sent as text in binary mode, so this is almost the same
458 : : * as the input function, but it's prefixed with a version number so we
459 : : * can change the binary format sent in future if necessary. For now,
460 : : * only version 1 is supported.
461 : : */
2250 tgl@sss.pgh.pa.us 462 : 2 : PG_FUNCTION_INFO_V1(ltxtq_recv);
463 : : Datum
2250 tgl@sss.pgh.pa.us 464 :UBC 0 : ltxtq_recv(PG_FUNCTION_ARGS)
465 : : {
466 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
467 : 0 : int version = pq_getmsgint(buf, 1);
468 : : char *str;
469 : : int nbytes;
470 : : ltxtquery *res;
471 : :
472 [ # # ]: 0 : if (version != 1)
473 [ # # ]: 0 : elog(ERROR, "unsupported ltxtquery version number %d", version);
474 : :
475 : 0 : str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
1249 andrew@dunslane.net 476 : 0 : res = queryin(str, NULL);
2250 tgl@sss.pgh.pa.us 477 : 0 : pfree(str);
478 : :
479 : 0 : PG_RETURN_POINTER(res);
480 : : }
481 : :
482 : : /*
483 : : * out function
484 : : */
485 : : typedef struct
486 : : {
487 : : ITEM *curpol;
488 : : char *buf;
489 : : char *cur;
490 : : char *op;
491 : : int32 buflen;
492 : : } INFIX;
493 : :
494 : : #define RESIZEBUF(inf,addsize) \
495 : : while( ( (inf)->cur - (inf)->buf ) + (addsize) + 1 >= (inf)->buflen ) \
496 : : { \
497 : : int32 len = (inf)->cur - (inf)->buf; \
498 : : (inf)->buflen *= 2; \
499 : : (inf)->buf = (char*) repalloc( (void*)(inf)->buf, (inf)->buflen ); \
500 : : (inf)->cur = (inf)->buf + len; \
501 : : }
502 : :
503 : : /*
504 : : * recursive walk on tree and print it in
505 : : * infix (human-readable) view
506 : : */
507 : : static void
6771 bruce@momjian.us 508 :CBC 10 : infix(INFIX *in, bool first)
509 : : {
510 : : /* since this function recurses, it could be driven to stack overflow. */
3890 noah@leadboat.com 511 : 10 : check_stack_depth();
512 : :
8705 bruce@momjian.us 513 [ + + ]: 10 : if (in->curpol->type == VAL)
514 : : {
8669 515 : 6 : char *op = in->op + in->curpol->distance;
516 : :
8705 517 [ + + ]: 8 : RESIZEBUF(in, in->curpol->length * 2 + 5);
8669 518 [ + + ]: 32 : while (*op)
519 : : {
8705 520 : 26 : *(in->cur) = *op;
521 : 26 : op++;
522 : 26 : in->cur++;
523 : : }
7395 neilc@samurai.com 524 [ + + ]: 6 : if (in->curpol->flag & LVAR_SUBLEXEME)
525 : : {
8705 bruce@momjian.us 526 : 2 : *(in->cur) = '%';
527 : 2 : in->cur++;
528 : : }
8669 529 [ + + ]: 6 : if (in->curpol->flag & LVAR_INCASE)
530 : : {
8705 531 : 1 : *(in->cur) = '@';
532 : 1 : in->cur++;
533 : : }
8669 534 [ + + ]: 6 : if (in->curpol->flag & LVAR_ANYEND)
535 : : {
8705 536 : 3 : *(in->cur) = '*';
537 : 3 : in->cur++;
538 : : }
539 : 6 : *(in->cur) = '\0';
540 : 6 : in->curpol++;
541 : : }
5087 peter_e@gmx.net 542 [ + + ]: 4 : else if (in->curpol->val == (int32) '!')
543 : : {
8669 bruce@momjian.us 544 : 1 : bool isopr = false;
545 : :
8705 546 [ - + ]: 1 : RESIZEBUF(in, 1);
547 : 1 : *(in->cur) = '!';
548 : 1 : in->cur++;
549 : 1 : *(in->cur) = '\0';
550 : 1 : in->curpol++;
551 [ - + ]: 1 : if (in->curpol->type == OPR)
552 : : {
8705 bruce@momjian.us 553 :UBC 0 : isopr = true;
554 [ # # ]: 0 : RESIZEBUF(in, 2);
555 : 0 : sprintf(in->cur, "( ");
556 : 0 : in->cur = strchr(in->cur, '\0');
557 : : }
8705 bruce@momjian.us 558 :CBC 1 : infix(in, isopr);
559 [ - + ]: 1 : if (isopr)
560 : : {
8705 bruce@momjian.us 561 [ # # ]:UBC 0 : RESIZEBUF(in, 2);
562 : 0 : sprintf(in->cur, " )");
563 : 0 : in->cur = strchr(in->cur, '\0');
564 : : }
565 : : }
566 : : else
567 : : {
5087 peter_e@gmx.net 568 :CBC 3 : int32 op = in->curpol->val;
569 : : INFIX nrm;
570 : :
8705 bruce@momjian.us 571 : 3 : in->curpol++;
5087 peter_e@gmx.net 572 [ - + - - ]: 3 : if (op == (int32) '|' && !first)
573 : : {
8705 bruce@momjian.us 574 [ # # ]:UBC 0 : RESIZEBUF(in, 2);
575 : 0 : sprintf(in->cur, "( ");
576 : 0 : in->cur = strchr(in->cur, '\0');
577 : : }
578 : :
8705 bruce@momjian.us 579 :CBC 3 : nrm.curpol = in->curpol;
580 : 3 : nrm.op = in->op;
581 : 3 : nrm.buflen = 16;
176 michael@paquier.xyz 582 :GNC 3 : nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
583 : :
584 : : /* get right operand */
8705 bruce@momjian.us 585 :CBC 3 : infix(&nrm, false);
586 : :
587 : : /* get & print left operand */
588 : 3 : in->curpol = nrm.curpol;
589 : 3 : infix(in, false);
590 : :
591 : : /* print operator & right operand */
592 [ - + ]: 3 : RESIZEBUF(in, 3 + (nrm.cur - nrm.buf));
593 : 3 : sprintf(in->cur, " %c %s", op, nrm.buf);
594 : 3 : in->cur = strchr(in->cur, '\0');
595 : 3 : pfree(nrm.buf);
596 : :
5087 peter_e@gmx.net 597 [ - + - - ]: 3 : if (op == (int32) '|' && !first)
598 : : {
8705 bruce@momjian.us 599 [ # # ]:UBC 0 : RESIZEBUF(in, 2);
600 : 0 : sprintf(in->cur, " )");
601 : 0 : in->cur = strchr(in->cur, '\0');
602 : : }
603 : : }
8705 bruce@momjian.us 604 :CBC 10 : }
605 : :
2250 tgl@sss.pgh.pa.us 606 : 3 : PG_FUNCTION_INFO_V1(ltxtq_out);
607 : : Datum
8705 bruce@momjian.us 608 : 3 : ltxtq_out(PG_FUNCTION_ARGS)
609 : : {
3176 tgl@sss.pgh.pa.us 610 : 3 : ltxtquery *query = PG_GETARG_LTXTQUERY_P(0);
611 : : INFIX nrm;
612 : :
8705 bruce@momjian.us 613 [ - + ]: 3 : if (query->size == 0)
8346 tgl@sss.pgh.pa.us 614 [ # # ]:UBC 0 : ereport(ERROR,
615 : : (errcode(ERRCODE_SYNTAX_ERROR),
616 : : errmsg("syntax error"),
617 : : errdetail("Empty query.")));
618 : :
8705 bruce@momjian.us 619 :CBC 3 : nrm.curpol = GETQUERY(query);
620 : 3 : nrm.buflen = 32;
176 michael@paquier.xyz 621 :GNC 3 : nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
8705 bruce@momjian.us 622 :CBC 3 : *(nrm.cur) = '\0';
623 : 3 : nrm.op = GETOPERAND(query);
624 : 3 : infix(&nrm, true);
625 : :
626 : 3 : PG_RETURN_POINTER(nrm.buf);
627 : : }
628 : :
629 : : /*
630 : : * ltxtquery type send function
631 : : *
632 : : * The type is sent as text in binary mode, so this is almost the same
633 : : * as the output function, but it's prefixed with a version number so we
634 : : * can change the binary format sent in future if necessary. For now,
635 : : * only version 1 is supported.
636 : : */
2250 tgl@sss.pgh.pa.us 637 : 2 : PG_FUNCTION_INFO_V1(ltxtq_send);
638 : : Datum
2250 tgl@sss.pgh.pa.us 639 :UBC 0 : ltxtq_send(PG_FUNCTION_ARGS)
640 : : {
641 : 0 : ltxtquery *query = PG_GETARG_LTXTQUERY_P(0);
642 : : StringInfoData buf;
643 : 0 : int version = 1;
644 : : INFIX nrm;
645 : :
646 [ # # ]: 0 : if (query->size == 0)
647 [ # # ]: 0 : ereport(ERROR,
648 : : (errcode(ERRCODE_SYNTAX_ERROR),
649 : : errmsg("syntax error"),
650 : : errdetail("Empty query.")));
651 : :
652 : 0 : nrm.curpol = GETQUERY(query);
653 : 0 : nrm.buflen = 32;
176 michael@paquier.xyz 654 :UNC 0 : nrm.cur = nrm.buf = palloc_array(char, nrm.buflen);
2250 tgl@sss.pgh.pa.us 655 :UBC 0 : *(nrm.cur) = '\0';
656 : 0 : nrm.op = GETOPERAND(query);
657 : 0 : infix(&nrm, true);
658 : :
659 : 0 : pq_begintypsend(&buf);
660 : 0 : pq_sendint8(&buf, version);
661 : 0 : pq_sendtext(&buf, nrm.buf, strlen(nrm.buf));
662 : 0 : pfree(nrm.buf);
663 : :
664 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
665 : : }
|