/src/postgres/src/backend/nodes/outfuncs.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * outfuncs.c |
4 | | * Output functions for Postgres tree nodes. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/nodes/outfuncs.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include <ctype.h> |
18 | | |
19 | | #include "access/attnum.h" |
20 | | #include "common/shortest_dec.h" |
21 | | #include "lib/stringinfo.h" |
22 | | #include "miscadmin.h" |
23 | | #include "nodes/bitmapset.h" |
24 | | #include "nodes/nodes.h" |
25 | | #include "nodes/pg_list.h" |
26 | | #include "utils/datum.h" |
27 | | |
28 | | /* State flag that determines how nodeToStringInternal() should treat location fields */ |
29 | | static bool write_location_fields = false; |
30 | | |
31 | | static void outChar(StringInfo str, char c); |
32 | | static void outDouble(StringInfo str, double d); |
33 | | |
34 | | |
35 | | /* |
36 | | * Macros to simplify output of different kinds of fields. Use these |
37 | | * wherever possible to reduce the chance for silly typos. Note that these |
38 | | * hard-wire conventions about the names of the local variables in an Out |
39 | | * routine. |
40 | | */ |
41 | | |
42 | | /* Write the label for the node type */ |
43 | | #define WRITE_NODE_TYPE(nodelabel) \ |
44 | 0 | appendStringInfoString(str, nodelabel) |
45 | | |
46 | | /* Write an integer field (anything written as ":fldname %d") */ |
47 | | #define WRITE_INT_FIELD(fldname) \ |
48 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname) |
49 | | |
50 | | /* Write an unsigned integer field (anything written as ":fldname %u") */ |
51 | | #define WRITE_UINT_FIELD(fldname) \ |
52 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname) |
53 | | |
54 | | /* Write a signed integer field (anything written with INT64_FORMAT) */ |
55 | | #define WRITE_INT64_FIELD(fldname) \ |
56 | 0 | appendStringInfo(str, \ |
57 | 0 | " :" CppAsString(fldname) " " INT64_FORMAT, \ |
58 | 0 | node->fldname) |
59 | | |
60 | | /* Write an unsigned integer field (anything written with UINT64_FORMAT) */ |
61 | | #define WRITE_UINT64_FIELD(fldname) \ |
62 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " " UINT64_FORMAT, \ |
63 | 0 | node->fldname) |
64 | | |
65 | | /* Write an OID field (don't hard-wire assumption that OID is same as uint) */ |
66 | | #define WRITE_OID_FIELD(fldname) \ |
67 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname) |
68 | | |
69 | | /* Write a long-integer field */ |
70 | | #define WRITE_LONG_FIELD(fldname) \ |
71 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %ld", node->fldname) |
72 | | |
73 | | /* Write a char field (ie, one ascii character) */ |
74 | | #define WRITE_CHAR_FIELD(fldname) \ |
75 | 0 | (appendStringInfo(str, " :" CppAsString(fldname) " "), \ |
76 | 0 | outChar(str, node->fldname)) |
77 | | |
78 | | /* Write an enumerated-type field as an integer code */ |
79 | | #define WRITE_ENUM_FIELD(fldname, enumtype) \ |
80 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %d", \ |
81 | 0 | (int) node->fldname) |
82 | | |
83 | | /* Write a float field (actually, they're double) */ |
84 | | #define WRITE_FLOAT_FIELD(fldname) \ |
85 | 0 | (appendStringInfo(str, " :" CppAsString(fldname) " "), \ |
86 | 0 | outDouble(str, node->fldname)) |
87 | | |
88 | | /* Write a boolean field */ |
89 | | #define WRITE_BOOL_FIELD(fldname) \ |
90 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %s", \ |
91 | 0 | booltostr(node->fldname)) |
92 | | |
93 | | /* Write a character-string (possibly NULL) field */ |
94 | | #define WRITE_STRING_FIELD(fldname) \ |
95 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
96 | 0 | outToken(str, node->fldname)) |
97 | | |
98 | | /* Write a parse location field (actually same as INT case) */ |
99 | | #define WRITE_LOCATION_FIELD(fldname) \ |
100 | 0 | appendStringInfo(str, " :" CppAsString(fldname) " %d", write_location_fields ? node->fldname : -1) |
101 | | |
102 | | /* Write a Node field */ |
103 | | #define WRITE_NODE_FIELD(fldname) \ |
104 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
105 | 0 | outNode(str, node->fldname)) |
106 | | |
107 | | /* Write a bitmapset field */ |
108 | | #define WRITE_BITMAPSET_FIELD(fldname) \ |
109 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
110 | 0 | outBitmapset(str, node->fldname)) |
111 | | |
112 | | /* Write a variable-length array (not a List) of Node pointers */ |
113 | | #define WRITE_NODE_ARRAY(fldname, len) \ |
114 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
115 | 0 | writeNodeArray(str, (const Node * const *) node->fldname, len)) |
116 | | |
117 | | /* Write a variable-length array of AttrNumber */ |
118 | | #define WRITE_ATTRNUMBER_ARRAY(fldname, len) \ |
119 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
120 | 0 | writeAttrNumberCols(str, node->fldname, len)) |
121 | | |
122 | | /* Write a variable-length array of Oid */ |
123 | | #define WRITE_OID_ARRAY(fldname, len) \ |
124 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
125 | 0 | writeOidCols(str, node->fldname, len)) |
126 | | |
127 | | /* Write a variable-length array of Index */ |
128 | | #define WRITE_INDEX_ARRAY(fldname, len) \ |
129 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
130 | 0 | writeIndexCols(str, node->fldname, len)) |
131 | | |
132 | | /* Write a variable-length array of int */ |
133 | | #define WRITE_INT_ARRAY(fldname, len) \ |
134 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
135 | 0 | writeIntCols(str, node->fldname, len)) |
136 | | |
137 | | /* Write a variable-length array of bool */ |
138 | | #define WRITE_BOOL_ARRAY(fldname, len) \ |
139 | 0 | (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ |
140 | 0 | writeBoolCols(str, node->fldname, len)) |
141 | | |
142 | 0 | #define booltostr(x) ((x) ? "true" : "false") |
143 | | |
144 | | |
145 | | /* |
146 | | * outToken |
147 | | * Convert an ordinary string (eg, an identifier) into a form that |
148 | | * will be decoded back to a plain token by read.c's functions. |
149 | | * |
150 | | * If a null string pointer is given, it is encoded as '<>'. |
151 | | * An empty string is encoded as '""'. To avoid ambiguity, input |
152 | | * strings beginning with '<' or '"' receive a leading backslash. |
153 | | */ |
154 | | void |
155 | | outToken(StringInfo str, const char *s) |
156 | 0 | { |
157 | 0 | if (s == NULL) |
158 | 0 | { |
159 | 0 | appendStringInfoString(str, "<>"); |
160 | 0 | return; |
161 | 0 | } |
162 | 0 | if (*s == '\0') |
163 | 0 | { |
164 | 0 | appendStringInfoString(str, "\"\""); |
165 | 0 | return; |
166 | 0 | } |
167 | | |
168 | | /* |
169 | | * Look for characters or patterns that are treated specially by read.c |
170 | | * (either in pg_strtok() or in nodeRead()), and therefore need a |
171 | | * protective backslash. |
172 | | */ |
173 | | /* These characters only need to be quoted at the start of the string */ |
174 | 0 | if (*s == '<' || |
175 | 0 | *s == '"' || |
176 | 0 | isdigit((unsigned char) *s) || |
177 | 0 | ((*s == '+' || *s == '-') && |
178 | 0 | (isdigit((unsigned char) s[1]) || s[1] == '.'))) |
179 | 0 | appendStringInfoChar(str, '\\'); |
180 | 0 | while (*s) |
181 | 0 | { |
182 | | /* These chars must be backslashed anywhere in the string */ |
183 | 0 | if (*s == ' ' || *s == '\n' || *s == '\t' || |
184 | 0 | *s == '(' || *s == ')' || *s == '{' || *s == '}' || |
185 | 0 | *s == '\\') |
186 | 0 | appendStringInfoChar(str, '\\'); |
187 | 0 | appendStringInfoChar(str, *s++); |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | /* |
192 | | * Convert one char. Goes through outToken() so that special characters are |
193 | | * escaped. |
194 | | */ |
195 | | static void |
196 | | outChar(StringInfo str, char c) |
197 | 0 | { |
198 | 0 | char in[2]; |
199 | | |
200 | | /* Traditionally, we've represented \0 as <>, so keep doing that */ |
201 | 0 | if (c == '\0') |
202 | 0 | { |
203 | 0 | appendStringInfoString(str, "<>"); |
204 | 0 | return; |
205 | 0 | } |
206 | | |
207 | 0 | in[0] = c; |
208 | 0 | in[1] = '\0'; |
209 | |
|
210 | 0 | outToken(str, in); |
211 | 0 | } |
212 | | |
213 | | /* |
214 | | * Convert a double value, attempting to ensure the value is preserved exactly. |
215 | | */ |
216 | | static void |
217 | | outDouble(StringInfo str, double d) |
218 | 0 | { |
219 | 0 | char buf[DOUBLE_SHORTEST_DECIMAL_LEN]; |
220 | |
|
221 | 0 | double_to_shortest_decimal_buf(d, buf); |
222 | 0 | appendStringInfoString(str, buf); |
223 | 0 | } |
224 | | |
225 | | /* |
226 | | * common implementation for scalar-array-writing functions |
227 | | * |
228 | | * The data format is either "<>" for a NULL pointer or "(item item item)". |
229 | | * fmtstr must include a leading space, and the rest of it must produce |
230 | | * something that will be seen as a single simple token by pg_strtok(). |
231 | | * convfunc can be empty, or the name of a conversion macro or function. |
232 | | */ |
233 | | #define WRITE_SCALAR_ARRAY(fnname, datatype, fmtstr, convfunc) \ |
234 | | static void \ |
235 | 0 | fnname(StringInfo str, const datatype *arr, int len) \ |
236 | 0 | { \ |
237 | 0 | if (arr != NULL) \ |
238 | 0 | { \ |
239 | 0 | appendStringInfoChar(str, '('); \ |
240 | 0 | for (int i = 0; i < len; i++) \ |
241 | 0 | appendStringInfo(str, fmtstr, convfunc(arr[i])); \ |
242 | 0 | appendStringInfoChar(str, ')'); \ |
243 | 0 | } \ |
244 | 0 | else \ |
245 | 0 | appendStringInfoString(str, "<>"); \ |
246 | 0 | } |
247 | | |
248 | 0 | WRITE_SCALAR_ARRAY(writeAttrNumberCols, AttrNumber, " %d",) |
249 | 0 | WRITE_SCALAR_ARRAY(writeOidCols, Oid, " %u",) |
250 | 0 | WRITE_SCALAR_ARRAY(writeIndexCols, Index, " %u",) |
251 | 0 | WRITE_SCALAR_ARRAY(writeIntCols, int, " %d",) |
252 | 0 | WRITE_SCALAR_ARRAY(writeBoolCols, bool, " %s", booltostr) |
253 | | |
254 | | /* |
255 | | * Print an array (not a List) of Node pointers. |
256 | | * |
257 | | * The decoration is identical to that of scalar arrays, but we can't |
258 | | * quite use appendStringInfo() in the loop. |
259 | | */ |
260 | | static void |
261 | | writeNodeArray(StringInfo str, const Node *const *arr, int len) |
262 | 0 | { |
263 | 0 | if (arr != NULL) |
264 | 0 | { |
265 | 0 | appendStringInfoChar(str, '('); |
266 | 0 | for (int i = 0; i < len; i++) |
267 | 0 | { |
268 | 0 | appendStringInfoChar(str, ' '); |
269 | 0 | outNode(str, arr[i]); |
270 | 0 | } |
271 | 0 | appendStringInfoChar(str, ')'); |
272 | 0 | } |
273 | 0 | else |
274 | 0 | appendStringInfoString(str, "<>"); |
275 | 0 | } |
276 | | |
277 | | /* |
278 | | * Print a List. |
279 | | */ |
280 | | static void |
281 | | _outList(StringInfo str, const List *node) |
282 | 0 | { |
283 | 0 | const ListCell *lc; |
284 | |
|
285 | 0 | appendStringInfoChar(str, '('); |
286 | |
|
287 | 0 | if (IsA(node, IntList)) |
288 | 0 | appendStringInfoChar(str, 'i'); |
289 | 0 | else if (IsA(node, OidList)) |
290 | 0 | appendStringInfoChar(str, 'o'); |
291 | 0 | else if (IsA(node, XidList)) |
292 | 0 | appendStringInfoChar(str, 'x'); |
293 | |
|
294 | 0 | foreach(lc, node) |
295 | 0 | { |
296 | | /* |
297 | | * For the sake of backward compatibility, we emit a slightly |
298 | | * different whitespace format for lists of nodes vs. other types of |
299 | | * lists. XXX: is this necessary? |
300 | | */ |
301 | 0 | if (IsA(node, List)) |
302 | 0 | { |
303 | 0 | outNode(str, lfirst(lc)); |
304 | 0 | if (lnext(node, lc)) |
305 | 0 | appendStringInfoChar(str, ' '); |
306 | 0 | } |
307 | 0 | else if (IsA(node, IntList)) |
308 | 0 | appendStringInfo(str, " %d", lfirst_int(lc)); |
309 | 0 | else if (IsA(node, OidList)) |
310 | 0 | appendStringInfo(str, " %u", lfirst_oid(lc)); |
311 | 0 | else if (IsA(node, XidList)) |
312 | 0 | appendStringInfo(str, " %u", lfirst_xid(lc)); |
313 | 0 | else |
314 | 0 | elog(ERROR, "unrecognized list node type: %d", |
315 | 0 | (int) node->type); |
316 | 0 | } |
317 | | |
318 | 0 | appendStringInfoChar(str, ')'); |
319 | 0 | } |
320 | | |
321 | | /* |
322 | | * outBitmapset - |
323 | | * converts a bitmap set of integers |
324 | | * |
325 | | * Note: the output format is "(b int int ...)", similar to an integer List. |
326 | | * |
327 | | * We export this function for use by extensions that define extensible nodes. |
328 | | * That's somewhat historical, though, because calling outNode() will work. |
329 | | */ |
330 | | void |
331 | | outBitmapset(StringInfo str, const Bitmapset *bms) |
332 | 0 | { |
333 | 0 | int x; |
334 | |
|
335 | 0 | appendStringInfoChar(str, '('); |
336 | 0 | appendStringInfoChar(str, 'b'); |
337 | 0 | x = -1; |
338 | 0 | while ((x = bms_next_member(bms, x)) >= 0) |
339 | 0 | appendStringInfo(str, " %d", x); |
340 | 0 | appendStringInfoChar(str, ')'); |
341 | 0 | } |
342 | | |
343 | | /* |
344 | | * Print the value of a Datum given its type. |
345 | | */ |
346 | | void |
347 | | outDatum(StringInfo str, Datum value, int typlen, bool typbyval) |
348 | 0 | { |
349 | 0 | Size length, |
350 | 0 | i; |
351 | 0 | char *s; |
352 | |
|
353 | 0 | length = datumGetSize(value, typbyval, typlen); |
354 | |
|
355 | 0 | if (typbyval) |
356 | 0 | { |
357 | 0 | s = (char *) (&value); |
358 | 0 | appendStringInfo(str, "%u [ ", (unsigned int) length); |
359 | 0 | for (i = 0; i < (Size) sizeof(Datum); i++) |
360 | 0 | appendStringInfo(str, "%d ", (int) (s[i])); |
361 | 0 | appendStringInfoChar(str, ']'); |
362 | 0 | } |
363 | 0 | else |
364 | 0 | { |
365 | 0 | s = (char *) DatumGetPointer(value); |
366 | 0 | if (!s) |
367 | 0 | appendStringInfoString(str, "0 [ ]"); |
368 | 0 | else |
369 | 0 | { |
370 | 0 | appendStringInfo(str, "%u [ ", (unsigned int) length); |
371 | 0 | for (i = 0; i < length; i++) |
372 | 0 | appendStringInfo(str, "%d ", (int) (s[i])); |
373 | 0 | appendStringInfoChar(str, ']'); |
374 | 0 | } |
375 | 0 | } |
376 | 0 | } |
377 | | |
378 | | |
379 | | #include "outfuncs.funcs.c" |
380 | | |
381 | | |
382 | | /* |
383 | | * Support functions for nodes with custom_read_write attribute or |
384 | | * special_read_write attribute |
385 | | */ |
386 | | |
387 | | static void |
388 | | _outConst(StringInfo str, const Const *node) |
389 | 0 | { |
390 | 0 | WRITE_NODE_TYPE("CONST"); |
391 | |
|
392 | 0 | WRITE_OID_FIELD(consttype); |
393 | 0 | WRITE_INT_FIELD(consttypmod); |
394 | 0 | WRITE_OID_FIELD(constcollid); |
395 | 0 | WRITE_INT_FIELD(constlen); |
396 | 0 | WRITE_BOOL_FIELD(constbyval); |
397 | 0 | WRITE_BOOL_FIELD(constisnull); |
398 | 0 | WRITE_LOCATION_FIELD(location); |
399 | |
|
400 | 0 | appendStringInfoString(str, " :constvalue "); |
401 | 0 | if (node->constisnull) |
402 | 0 | appendStringInfoString(str, "<>"); |
403 | 0 | else |
404 | 0 | outDatum(str, node->constvalue, node->constlen, node->constbyval); |
405 | 0 | } |
406 | | |
407 | | static void |
408 | | _outBoolExpr(StringInfo str, const BoolExpr *node) |
409 | 0 | { |
410 | 0 | char *opstr = NULL; |
411 | |
|
412 | 0 | WRITE_NODE_TYPE("BOOLEXPR"); |
413 | | |
414 | | /* do-it-yourself enum representation */ |
415 | 0 | switch (node->boolop) |
416 | 0 | { |
417 | 0 | case AND_EXPR: |
418 | 0 | opstr = "and"; |
419 | 0 | break; |
420 | 0 | case OR_EXPR: |
421 | 0 | opstr = "or"; |
422 | 0 | break; |
423 | 0 | case NOT_EXPR: |
424 | 0 | opstr = "not"; |
425 | 0 | break; |
426 | 0 | } |
427 | 0 | appendStringInfoString(str, " :boolop "); |
428 | 0 | outToken(str, opstr); |
429 | |
|
430 | 0 | WRITE_NODE_FIELD(args); |
431 | 0 | WRITE_LOCATION_FIELD(location); |
432 | 0 | } |
433 | | |
434 | | static void |
435 | | _outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node) |
436 | 0 | { |
437 | 0 | int i; |
438 | |
|
439 | 0 | WRITE_NODE_TYPE("FOREIGNKEYOPTINFO"); |
440 | |
|
441 | 0 | WRITE_UINT_FIELD(con_relid); |
442 | 0 | WRITE_UINT_FIELD(ref_relid); |
443 | 0 | WRITE_INT_FIELD(nkeys); |
444 | 0 | WRITE_ATTRNUMBER_ARRAY(conkey, node->nkeys); |
445 | 0 | WRITE_ATTRNUMBER_ARRAY(confkey, node->nkeys); |
446 | 0 | WRITE_OID_ARRAY(conpfeqop, node->nkeys); |
447 | 0 | WRITE_INT_FIELD(nmatched_ec); |
448 | 0 | WRITE_INT_FIELD(nconst_ec); |
449 | 0 | WRITE_INT_FIELD(nmatched_rcols); |
450 | 0 | WRITE_INT_FIELD(nmatched_ri); |
451 | | /* for compactness, just print the number of matches per column: */ |
452 | 0 | appendStringInfoString(str, " :eclass"); |
453 | 0 | for (i = 0; i < node->nkeys; i++) |
454 | 0 | appendStringInfo(str, " %d", (node->eclass[i] != NULL)); |
455 | 0 | appendStringInfoString(str, " :rinfos"); |
456 | 0 | for (i = 0; i < node->nkeys; i++) |
457 | 0 | appendStringInfo(str, " %d", list_length(node->rinfos[i])); |
458 | 0 | } |
459 | | |
460 | | static void |
461 | | _outEquivalenceClass(StringInfo str, const EquivalenceClass *node) |
462 | 0 | { |
463 | | /* |
464 | | * To simplify reading, we just chase up to the topmost merged EC and |
465 | | * print that, without bothering to show the merge-ees separately. |
466 | | */ |
467 | 0 | while (node->ec_merged) |
468 | 0 | node = node->ec_merged; |
469 | |
|
470 | 0 | WRITE_NODE_TYPE("EQUIVALENCECLASS"); |
471 | |
|
472 | 0 | WRITE_NODE_FIELD(ec_opfamilies); |
473 | 0 | WRITE_OID_FIELD(ec_collation); |
474 | 0 | WRITE_INT_FIELD(ec_childmembers_size); |
475 | 0 | WRITE_NODE_FIELD(ec_members); |
476 | 0 | WRITE_NODE_ARRAY(ec_childmembers, node->ec_childmembers_size); |
477 | 0 | WRITE_NODE_FIELD(ec_sources); |
478 | | /* Only ec_derives_list is written; hash is not serialized. */ |
479 | 0 | WRITE_NODE_FIELD(ec_derives_list); |
480 | 0 | WRITE_BITMAPSET_FIELD(ec_relids); |
481 | 0 | WRITE_BOOL_FIELD(ec_has_const); |
482 | 0 | WRITE_BOOL_FIELD(ec_has_volatile); |
483 | 0 | WRITE_BOOL_FIELD(ec_broken); |
484 | 0 | WRITE_UINT_FIELD(ec_sortref); |
485 | 0 | WRITE_UINT_FIELD(ec_min_security); |
486 | 0 | WRITE_UINT_FIELD(ec_max_security); |
487 | 0 | } |
488 | | |
489 | | static void |
490 | | _outExtensibleNode(StringInfo str, const ExtensibleNode *node) |
491 | 0 | { |
492 | 0 | const ExtensibleNodeMethods *methods; |
493 | |
|
494 | 0 | methods = GetExtensibleNodeMethods(node->extnodename, false); |
495 | |
|
496 | 0 | WRITE_NODE_TYPE("EXTENSIBLENODE"); |
497 | |
|
498 | 0 | WRITE_STRING_FIELD(extnodename); |
499 | | |
500 | | /* serialize the private fields */ |
501 | 0 | methods->nodeOut(str, node); |
502 | 0 | } |
503 | | |
504 | | static void |
505 | | _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) |
506 | 0 | { |
507 | 0 | WRITE_NODE_TYPE("RANGETBLENTRY"); |
508 | |
|
509 | 0 | WRITE_NODE_FIELD(alias); |
510 | 0 | WRITE_NODE_FIELD(eref); |
511 | 0 | WRITE_ENUM_FIELD(rtekind, RTEKind); |
512 | |
|
513 | 0 | switch (node->rtekind) |
514 | 0 | { |
515 | 0 | case RTE_RELATION: |
516 | 0 | WRITE_OID_FIELD(relid); |
517 | 0 | WRITE_BOOL_FIELD(inh); |
518 | 0 | WRITE_CHAR_FIELD(relkind); |
519 | 0 | WRITE_INT_FIELD(rellockmode); |
520 | 0 | WRITE_UINT_FIELD(perminfoindex); |
521 | 0 | WRITE_NODE_FIELD(tablesample); |
522 | 0 | break; |
523 | 0 | case RTE_SUBQUERY: |
524 | 0 | WRITE_NODE_FIELD(subquery); |
525 | 0 | WRITE_BOOL_FIELD(security_barrier); |
526 | | /* we re-use these RELATION fields, too: */ |
527 | 0 | WRITE_OID_FIELD(relid); |
528 | 0 | WRITE_BOOL_FIELD(inh); |
529 | 0 | WRITE_CHAR_FIELD(relkind); |
530 | 0 | WRITE_INT_FIELD(rellockmode); |
531 | 0 | WRITE_UINT_FIELD(perminfoindex); |
532 | 0 | break; |
533 | 0 | case RTE_JOIN: |
534 | 0 | WRITE_ENUM_FIELD(jointype, JoinType); |
535 | 0 | WRITE_INT_FIELD(joinmergedcols); |
536 | 0 | WRITE_NODE_FIELD(joinaliasvars); |
537 | 0 | WRITE_NODE_FIELD(joinleftcols); |
538 | 0 | WRITE_NODE_FIELD(joinrightcols); |
539 | 0 | WRITE_NODE_FIELD(join_using_alias); |
540 | 0 | break; |
541 | 0 | case RTE_FUNCTION: |
542 | 0 | WRITE_NODE_FIELD(functions); |
543 | 0 | WRITE_BOOL_FIELD(funcordinality); |
544 | 0 | break; |
545 | 0 | case RTE_TABLEFUNC: |
546 | 0 | WRITE_NODE_FIELD(tablefunc); |
547 | 0 | break; |
548 | 0 | case RTE_VALUES: |
549 | 0 | WRITE_NODE_FIELD(values_lists); |
550 | 0 | WRITE_NODE_FIELD(coltypes); |
551 | 0 | WRITE_NODE_FIELD(coltypmods); |
552 | 0 | WRITE_NODE_FIELD(colcollations); |
553 | 0 | break; |
554 | 0 | case RTE_CTE: |
555 | 0 | WRITE_STRING_FIELD(ctename); |
556 | 0 | WRITE_UINT_FIELD(ctelevelsup); |
557 | 0 | WRITE_BOOL_FIELD(self_reference); |
558 | 0 | WRITE_NODE_FIELD(coltypes); |
559 | 0 | WRITE_NODE_FIELD(coltypmods); |
560 | 0 | WRITE_NODE_FIELD(colcollations); |
561 | 0 | break; |
562 | 0 | case RTE_NAMEDTUPLESTORE: |
563 | 0 | WRITE_STRING_FIELD(enrname); |
564 | 0 | WRITE_FLOAT_FIELD(enrtuples); |
565 | 0 | WRITE_NODE_FIELD(coltypes); |
566 | 0 | WRITE_NODE_FIELD(coltypmods); |
567 | 0 | WRITE_NODE_FIELD(colcollations); |
568 | | /* we re-use these RELATION fields, too: */ |
569 | 0 | WRITE_OID_FIELD(relid); |
570 | 0 | break; |
571 | 0 | case RTE_RESULT: |
572 | | /* no extra fields */ |
573 | 0 | break; |
574 | 0 | case RTE_GROUP: |
575 | 0 | WRITE_NODE_FIELD(groupexprs); |
576 | 0 | break; |
577 | 0 | default: |
578 | 0 | elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind); |
579 | 0 | break; |
580 | 0 | } |
581 | | |
582 | 0 | WRITE_BOOL_FIELD(lateral); |
583 | 0 | WRITE_BOOL_FIELD(inFromCl); |
584 | 0 | WRITE_NODE_FIELD(securityQuals); |
585 | 0 | } |
586 | | |
587 | | static void |
588 | | _outA_Expr(StringInfo str, const A_Expr *node) |
589 | 0 | { |
590 | 0 | WRITE_NODE_TYPE("A_EXPR"); |
591 | |
|
592 | 0 | switch (node->kind) |
593 | 0 | { |
594 | 0 | case AEXPR_OP: |
595 | 0 | WRITE_NODE_FIELD(name); |
596 | 0 | break; |
597 | 0 | case AEXPR_OP_ANY: |
598 | 0 | appendStringInfoString(str, " ANY"); |
599 | 0 | WRITE_NODE_FIELD(name); |
600 | 0 | break; |
601 | 0 | case AEXPR_OP_ALL: |
602 | 0 | appendStringInfoString(str, " ALL"); |
603 | 0 | WRITE_NODE_FIELD(name); |
604 | 0 | break; |
605 | 0 | case AEXPR_DISTINCT: |
606 | 0 | appendStringInfoString(str, " DISTINCT"); |
607 | 0 | WRITE_NODE_FIELD(name); |
608 | 0 | break; |
609 | 0 | case AEXPR_NOT_DISTINCT: |
610 | 0 | appendStringInfoString(str, " NOT_DISTINCT"); |
611 | 0 | WRITE_NODE_FIELD(name); |
612 | 0 | break; |
613 | 0 | case AEXPR_NULLIF: |
614 | 0 | appendStringInfoString(str, " NULLIF"); |
615 | 0 | WRITE_NODE_FIELD(name); |
616 | 0 | break; |
617 | 0 | case AEXPR_IN: |
618 | 0 | appendStringInfoString(str, " IN"); |
619 | 0 | WRITE_NODE_FIELD(name); |
620 | 0 | break; |
621 | 0 | case AEXPR_LIKE: |
622 | 0 | appendStringInfoString(str, " LIKE"); |
623 | 0 | WRITE_NODE_FIELD(name); |
624 | 0 | break; |
625 | 0 | case AEXPR_ILIKE: |
626 | 0 | appendStringInfoString(str, " ILIKE"); |
627 | 0 | WRITE_NODE_FIELD(name); |
628 | 0 | break; |
629 | 0 | case AEXPR_SIMILAR: |
630 | 0 | appendStringInfoString(str, " SIMILAR"); |
631 | 0 | WRITE_NODE_FIELD(name); |
632 | 0 | break; |
633 | 0 | case AEXPR_BETWEEN: |
634 | 0 | appendStringInfoString(str, " BETWEEN"); |
635 | 0 | WRITE_NODE_FIELD(name); |
636 | 0 | break; |
637 | 0 | case AEXPR_NOT_BETWEEN: |
638 | 0 | appendStringInfoString(str, " NOT_BETWEEN"); |
639 | 0 | WRITE_NODE_FIELD(name); |
640 | 0 | break; |
641 | 0 | case AEXPR_BETWEEN_SYM: |
642 | 0 | appendStringInfoString(str, " BETWEEN_SYM"); |
643 | 0 | WRITE_NODE_FIELD(name); |
644 | 0 | break; |
645 | 0 | case AEXPR_NOT_BETWEEN_SYM: |
646 | 0 | appendStringInfoString(str, " NOT_BETWEEN_SYM"); |
647 | 0 | WRITE_NODE_FIELD(name); |
648 | 0 | break; |
649 | 0 | default: |
650 | 0 | elog(ERROR, "unrecognized A_Expr_Kind: %d", (int) node->kind); |
651 | 0 | break; |
652 | 0 | } |
653 | | |
654 | 0 | WRITE_NODE_FIELD(lexpr); |
655 | 0 | WRITE_NODE_FIELD(rexpr); |
656 | 0 | WRITE_LOCATION_FIELD(rexpr_list_start); |
657 | 0 | WRITE_LOCATION_FIELD(rexpr_list_end); |
658 | 0 | WRITE_LOCATION_FIELD(location); |
659 | 0 | } |
660 | | |
661 | | static void |
662 | | _outInteger(StringInfo str, const Integer *node) |
663 | 0 | { |
664 | 0 | appendStringInfo(str, "%d", node->ival); |
665 | 0 | } |
666 | | |
667 | | static void |
668 | | _outFloat(StringInfo str, const Float *node) |
669 | 0 | { |
670 | | /* |
671 | | * We assume the value is a valid numeric literal and so does not need |
672 | | * quoting. |
673 | | */ |
674 | 0 | appendStringInfoString(str, node->fval); |
675 | 0 | } |
676 | | |
677 | | static void |
678 | | _outBoolean(StringInfo str, const Boolean *node) |
679 | 0 | { |
680 | 0 | appendStringInfoString(str, node->boolval ? "true" : "false"); |
681 | 0 | } |
682 | | |
683 | | static void |
684 | | _outString(StringInfo str, const String *node) |
685 | 0 | { |
686 | | /* |
687 | | * We use outToken to provide escaping of the string's content, but we |
688 | | * don't want it to convert an empty string to '""', because we're putting |
689 | | * double quotes around the string already. |
690 | | */ |
691 | 0 | appendStringInfoChar(str, '"'); |
692 | 0 | if (node->sval[0] != '\0') |
693 | 0 | outToken(str, node->sval); |
694 | 0 | appendStringInfoChar(str, '"'); |
695 | 0 | } |
696 | | |
697 | | static void |
698 | | _outBitString(StringInfo str, const BitString *node) |
699 | 0 | { |
700 | | /* |
701 | | * The lexer will always produce a string starting with 'b' or 'x'. There |
702 | | * might be characters following that that need escaping, but outToken |
703 | | * won't escape the 'b' or 'x'. This is relied on by nodeTokenType. |
704 | | */ |
705 | 0 | Assert(node->bsval[0] == 'b' || node->bsval[0] == 'x'); |
706 | 0 | outToken(str, node->bsval); |
707 | 0 | } |
708 | | |
709 | | static void |
710 | | _outA_Const(StringInfo str, const A_Const *node) |
711 | 0 | { |
712 | 0 | WRITE_NODE_TYPE("A_CONST"); |
713 | |
|
714 | 0 | if (node->isnull) |
715 | 0 | appendStringInfoString(str, " NULL"); |
716 | 0 | else |
717 | 0 | { |
718 | 0 | appendStringInfoString(str, " :val "); |
719 | 0 | outNode(str, &node->val); |
720 | 0 | } |
721 | 0 | WRITE_LOCATION_FIELD(location); |
722 | 0 | } |
723 | | |
724 | | |
725 | | /* |
726 | | * outNode - |
727 | | * converts a Node into ascii string and append it to 'str' |
728 | | */ |
729 | | void |
730 | | outNode(StringInfo str, const void *obj) |
731 | | { |
732 | | /* Guard against stack overflow due to overly complex expressions */ |
733 | | check_stack_depth(); |
734 | | |
735 | | if (obj == NULL) |
736 | | appendStringInfoString(str, "<>"); |
737 | | else if (IsA(obj, List) || IsA(obj, IntList) || IsA(obj, OidList) || |
738 | | IsA(obj, XidList)) |
739 | | _outList(str, obj); |
740 | | /* nodeRead does not want to see { } around these! */ |
741 | | else if (IsA(obj, Integer)) |
742 | | _outInteger(str, (Integer *) obj); |
743 | | else if (IsA(obj, Float)) |
744 | | _outFloat(str, (Float *) obj); |
745 | | else if (IsA(obj, Boolean)) |
746 | | _outBoolean(str, (Boolean *) obj); |
747 | | else if (IsA(obj, String)) |
748 | | _outString(str, (String *) obj); |
749 | | else if (IsA(obj, BitString)) |
750 | | _outBitString(str, (BitString *) obj); |
751 | | else if (IsA(obj, Bitmapset)) |
752 | | outBitmapset(str, (Bitmapset *) obj); |
753 | | else |
754 | | { |
755 | | appendStringInfoChar(str, '{'); |
756 | | switch (nodeTag(obj)) |
757 | | { |
758 | | #include "outfuncs.switch.c" |
759 | | |
760 | | default: |
761 | | |
762 | | /* |
763 | | * This should be an ERROR, but it's too useful to be able to |
764 | | * dump structures that outNode only understands part of. |
765 | | */ |
766 | | elog(WARNING, "could not dump unrecognized node type: %d", |
767 | | (int) nodeTag(obj)); |
768 | | break; |
769 | | } |
770 | | appendStringInfoChar(str, '}'); |
771 | | } |
772 | | } |
773 | | |
774 | | /* |
775 | | * nodeToString - |
776 | | * returns the ascii representation of the Node as a palloc'd string |
777 | | * |
778 | | * write_loc_fields determines whether location fields are output with their |
779 | | * actual value rather than -1. The actual value can be useful for debugging, |
780 | | * but for most uses, the actual value is not useful, since the original query |
781 | | * string is no longer available. |
782 | | */ |
783 | | static char * |
784 | | nodeToStringInternal(const void *obj, bool write_loc_fields) |
785 | 0 | { |
786 | 0 | StringInfoData str; |
787 | 0 | bool save_write_location_fields; |
788 | |
|
789 | 0 | save_write_location_fields = write_location_fields; |
790 | 0 | write_location_fields = write_loc_fields; |
791 | | |
792 | | /* see stringinfo.h for an explanation of this maneuver */ |
793 | 0 | initStringInfo(&str); |
794 | 0 | outNode(&str, obj); |
795 | |
|
796 | 0 | write_location_fields = save_write_location_fields; |
797 | |
|
798 | 0 | return str.data; |
799 | 0 | } |
800 | | |
801 | | /* |
802 | | * Externally visible entry points |
803 | | */ |
804 | | char * |
805 | | nodeToString(const void *obj) |
806 | 0 | { |
807 | 0 | return nodeToStringInternal(obj, false); |
808 | 0 | } |
809 | | |
810 | | char * |
811 | | nodeToStringWithLocations(const void *obj) |
812 | 0 | { |
813 | 0 | return nodeToStringInternal(obj, true); |
814 | 0 | } |
815 | | |
816 | | |
817 | | /* |
818 | | * bmsToString - |
819 | | * returns the ascii representation of the Bitmapset as a palloc'd string |
820 | | */ |
821 | | char * |
822 | | bmsToString(const Bitmapset *bms) |
823 | 0 | { |
824 | 0 | StringInfoData str; |
825 | | |
826 | | /* see stringinfo.h for an explanation of this maneuver */ |
827 | 0 | initStringInfo(&str); |
828 | 0 | outBitmapset(&str, bms); |
829 | 0 | return str.data; |
830 | 0 | } |