diff options
Diffstat (limited to 'src/backend/nodes')
-rw-r--r-- | src/backend/nodes/Makefile | 32 | ||||
-rw-r--r-- | src/backend/nodes/README | 80 | ||||
-rw-r--r-- | src/backend/nodes/bitmapset.c | 1194 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 6023 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 4001 | ||||
-rw-r--r-- | src/backend/nodes/extensible.c | 143 | ||||
-rw-r--r-- | src/backend/nodes/list.c | 1676 | ||||
-rw-r--r-- | src/backend/nodes/makefuncs.c | 820 | ||||
-rw-r--r-- | src/backend/nodes/nodeFuncs.c | 4201 | ||||
-rw-r--r-- | src/backend/nodes/nodes.c | 31 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 4634 | ||||
-rw-r--r-- | src/backend/nodes/params.c | 422 | ||||
-rw-r--r-- | src/backend/nodes/print.c | 506 | ||||
-rw-r--r-- | src/backend/nodes/read.c | 468 | ||||
-rw-r--r-- | src/backend/nodes/readfuncs.c | 3190 | ||||
-rw-r--r-- | src/backend/nodes/tidbitmap.c | 1561 | ||||
-rw-r--r-- | src/backend/nodes/value.c | 83 |
17 files changed, 29065 insertions, 0 deletions
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile new file mode 100644 index 0000000..5d2b12a --- /dev/null +++ b/src/backend/nodes/Makefile @@ -0,0 +1,32 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for backend/nodes +# +# IDENTIFICATION +# src/backend/nodes/Makefile +# +#------------------------------------------------------------------------- + +subdir = src/backend/nodes +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +OBJS = \ + bitmapset.o \ + copyfuncs.o \ + equalfuncs.o \ + extensible.o \ + list.o \ + makefuncs.o \ + nodeFuncs.o \ + nodes.o \ + outfuncs.o \ + params.o \ + print.o \ + read.o \ + readfuncs.o \ + tidbitmap.o \ + value.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/nodes/README b/src/backend/nodes/README new file mode 100644 index 0000000..d066ac5 --- /dev/null +++ b/src/backend/nodes/README @@ -0,0 +1,80 @@ +src/backend/nodes/README + +Node Structures +=============== + +Andrew Yu (11/94) + +Introduction +------------ + +The current node structures are plain old C structures. "Inheritance" is +achieved by convention. No additional functions will be generated. Functions +that manipulate node structures reside in this directory. + + +FILES IN THIS DIRECTORY (src/backend/nodes/) + + General-purpose node manipulation functions: + copyfuncs.c - copy a node tree + equalfuncs.c - compare two node trees + outfuncs.c - convert a node tree to text representation + readfuncs.c - convert text representation back to a node tree + makefuncs.c - creator functions for some common node types + nodeFuncs.c - some other general-purpose manipulation functions + + Specialized manipulation functions: + bitmapset.c - Bitmapset support + list.c - generic list support + params.c - Param support + tidbitmap.c - TIDBitmap support + value.c - support for value nodes + +FILES IN src/include/nodes/ + + Node definitions: + nodes.h - define node tags (NodeTag) + primnodes.h - primitive nodes + parsenodes.h - parse tree nodes + pathnodes.h - path tree nodes and planner internal structures + plannodes.h - plan tree nodes + execnodes.h - executor nodes + memnodes.h - memory nodes + pg_list.h - generic list + + +Steps to Add a Node +------------------- + +Suppose you want to define a node Foo: + +1. Add a tag (T_Foo) to the enum NodeTag in nodes.h. (If you insert the + tag in a way that moves the numbers associated with existing tags, + you'll need to recompile the whole tree after doing this. It doesn't + force initdb though, because the numbers never go to disk.) +2. Add the structure definition to the appropriate include/nodes/???.h file. + If you intend to inherit from, say a Plan node, put Plan as the first field + of your struct definition. +3. If you intend to use copyObject, equal, nodeToString or stringToNode, + add an appropriate function to copyfuncs.c, equalfuncs.c, outfuncs.c + and readfuncs.c accordingly. (Except for frequently used nodes, don't + bother writing a creator function in makefuncs.c) The header comments + in those files give general rules for whether you need to add support. +4. Add cases to the functions in nodeFuncs.c as needed. There are many + other places you'll probably also need to teach about your new node + type. Best bet is to grep for references to one or two similar existing + node types to find all the places to touch. + + +Historical Note +--------------- + +Prior to the current simple C structure definitions, the Node structures +used a pseudo-inheritance system which automatically generated creator and +accessor functions. Since every node inherited from LispValue, the whole thing +was a mess. Here's a little anecdote: + + LispValue definition -- class used to support lisp structures + in C. This is here because we did not want to totally rewrite + planner and executor code which depended on lisp structures when + we ported postgres V1 from lisp to C. -cim 4/23/90 diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c new file mode 100644 index 0000000..0a6c30e --- /dev/null +++ b/src/backend/nodes/bitmapset.c @@ -0,0 +1,1194 @@ +/*------------------------------------------------------------------------- + * + * bitmapset.c + * PostgreSQL generic bitmap set package + * + * A bitmap set can represent any set of nonnegative integers, although + * it is mainly intended for sets where the maximum value is not large, + * say at most a few hundred. By convention, a NULL pointer is always + * accepted by all operations to represent the empty set. (But beware + * that this is not the only representation of the empty set. Use + * bms_is_empty() in preference to testing for NULL.) + * + * + * Copyright (c) 2003-2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/nodes/bitmapset.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "common/hashfn.h" +#include "nodes/bitmapset.h" +#include "nodes/pg_list.h" +#include "port/pg_bitutils.h" + + +#define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD) +#define BITNUM(x) ((x) % BITS_PER_BITMAPWORD) + +#define BITMAPSET_SIZE(nwords) \ + (offsetof(Bitmapset, words) + (nwords) * sizeof(bitmapword)) + +/*---------- + * This is a well-known cute trick for isolating the rightmost one-bit + * in a word. It assumes two's complement arithmetic. Consider any + * nonzero value, and focus attention on the rightmost one. The value is + * then something like + * xxxxxx10000 + * where x's are unspecified bits. The two's complement negative is formed + * by inverting all the bits and adding one. Inversion gives + * yyyyyy01111 + * where each y is the inverse of the corresponding x. Incrementing gives + * yyyyyy10000 + * and then ANDing with the original value gives + * 00000010000 + * This works for all cases except original value = zero, where of course + * we get zero. + *---------- + */ +#define RIGHTMOST_ONE(x) ((signedbitmapword) (x) & -((signedbitmapword) (x))) + +#define HAS_MULTIPLE_ONES(x) ((bitmapword) RIGHTMOST_ONE(x) != (x)) + +/* Select appropriate bit-twiddling functions for bitmap word size */ +#if BITS_PER_BITMAPWORD == 32 +#define bmw_leftmost_one_pos(w) pg_leftmost_one_pos32(w) +#define bmw_rightmost_one_pos(w) pg_rightmost_one_pos32(w) +#define bmw_popcount(w) pg_popcount32(w) +#elif BITS_PER_BITMAPWORD == 64 +#define bmw_leftmost_one_pos(w) pg_leftmost_one_pos64(w) +#define bmw_rightmost_one_pos(w) pg_rightmost_one_pos64(w) +#define bmw_popcount(w) pg_popcount64(w) +#else +#error "invalid BITS_PER_BITMAPWORD" +#endif + + +/* + * bms_copy - make a palloc'd copy of a bitmapset + */ +Bitmapset * +bms_copy(const Bitmapset *a) +{ + Bitmapset *result; + size_t size; + + if (a == NULL) + return NULL; + size = BITMAPSET_SIZE(a->nwords); + result = (Bitmapset *) palloc(size); + memcpy(result, a, size); + return result; +} + +/* + * bms_equal - are two bitmapsets equal? + * + * This is logical not physical equality; in particular, a NULL pointer will + * be reported as equal to a palloc'd value containing no members. + */ +bool +bms_equal(const Bitmapset *a, const Bitmapset *b) +{ + const Bitmapset *shorter; + const Bitmapset *longer; + int shortlen; + int longlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + { + if (b == NULL) + return true; + return bms_is_empty(b); + } + else if (b == NULL) + return bms_is_empty(a); + /* Identify shorter and longer input */ + if (a->nwords <= b->nwords) + { + shorter = a; + longer = b; + } + else + { + shorter = b; + longer = a; + } + /* And process */ + shortlen = shorter->nwords; + for (i = 0; i < shortlen; i++) + { + if (shorter->words[i] != longer->words[i]) + return false; + } + longlen = longer->nwords; + for (; i < longlen; i++) + { + if (longer->words[i] != 0) + return false; + } + return true; +} + +/* + * bms_compare - qsort-style comparator for bitmapsets + * + * This guarantees to report values as equal iff bms_equal would say they are + * equal. Otherwise, the highest-numbered bit that is set in one value but + * not the other determines the result. (This rule means that, for example, + * {6} is greater than {5}, which seems plausible.) + */ +int +bms_compare(const Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return bms_is_empty(b) ? 0 : -1; + else if (b == NULL) + return bms_is_empty(a) ? 0 : +1; + /* Handle cases where one input is longer than the other */ + shortlen = Min(a->nwords, b->nwords); + for (i = shortlen; i < a->nwords; i++) + { + if (a->words[i] != 0) + return +1; + } + for (i = shortlen; i < b->nwords; i++) + { + if (b->words[i] != 0) + return -1; + } + /* Process words in common */ + i = shortlen; + while (--i >= 0) + { + bitmapword aw = a->words[i]; + bitmapword bw = b->words[i]; + + if (aw != bw) + return (aw > bw) ? +1 : -1; + } + return 0; +} + +/* + * bms_make_singleton - build a bitmapset containing a single member + */ +Bitmapset * +bms_make_singleton(int x) +{ + Bitmapset *result; + int wordnum, + bitnum; + + if (x < 0) + elog(ERROR, "negative bitmapset member not allowed"); + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + result = (Bitmapset *) palloc0(BITMAPSET_SIZE(wordnum + 1)); + result->nwords = wordnum + 1; + result->words[wordnum] = ((bitmapword) 1 << bitnum); + return result; +} + +/* + * bms_free - free a bitmapset + * + * Same as pfree except for allowing NULL input + */ +void +bms_free(Bitmapset *a) +{ + if (a) + pfree(a); +} + + +/* + * These operations all make a freshly palloc'd result, + * leaving their inputs untouched + */ + + +/* + * bms_union - set union + */ +Bitmapset * +bms_union(const Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + const Bitmapset *other; + int otherlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return bms_copy(b); + if (b == NULL) + return bms_copy(a); + /* Identify shorter and longer input; copy the longer one */ + if (a->nwords <= b->nwords) + { + result = bms_copy(b); + other = a; + } + else + { + result = bms_copy(a); + other = b; + } + /* And union the shorter input into the result */ + otherlen = other->nwords; + for (i = 0; i < otherlen; i++) + result->words[i] |= other->words[i]; + return result; +} + +/* + * bms_intersect - set intersection + */ +Bitmapset * +bms_intersect(const Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + const Bitmapset *other; + int resultlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL || b == NULL) + return NULL; + /* Identify shorter and longer input; copy the shorter one */ + if (a->nwords <= b->nwords) + { + result = bms_copy(a); + other = b; + } + else + { + result = bms_copy(b); + other = a; + } + /* And intersect the longer input with the result */ + resultlen = result->nwords; + for (i = 0; i < resultlen; i++) + result->words[i] &= other->words[i]; + return result; +} + +/* + * bms_difference - set difference (ie, A without members of B) + */ +Bitmapset * +bms_difference(const Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return NULL; + if (b == NULL) + return bms_copy(a); + /* Copy the left input */ + result = bms_copy(a); + /* And remove b's bits from result */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + result->words[i] &= ~b->words[i]; + return result; +} + +/* + * bms_is_subset - is A a subset of B? + */ +bool +bms_is_subset(const Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int longlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return true; /* empty set is a subset of anything */ + if (b == NULL) + return bms_is_empty(a); + /* Check common words */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + if ((a->words[i] & ~b->words[i]) != 0) + return false; + } + /* Check extra words */ + if (a->nwords > b->nwords) + { + longlen = a->nwords; + for (; i < longlen; i++) + { + if (a->words[i] != 0) + return false; + } + } + return true; +} + +/* + * bms_subset_compare - compare A and B for equality/subset relationships + * + * This is more efficient than testing bms_is_subset in both directions. + */ +BMS_Comparison +bms_subset_compare(const Bitmapset *a, const Bitmapset *b) +{ + BMS_Comparison result; + int shortlen; + int longlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + { + if (b == NULL) + return BMS_EQUAL; + return bms_is_empty(b) ? BMS_EQUAL : BMS_SUBSET1; + } + if (b == NULL) + return bms_is_empty(a) ? BMS_EQUAL : BMS_SUBSET2; + /* Check common words */ + result = BMS_EQUAL; /* status so far */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + bitmapword aword = a->words[i]; + bitmapword bword = b->words[i]; + + if ((aword & ~bword) != 0) + { + /* a is not a subset of b */ + if (result == BMS_SUBSET1) + return BMS_DIFFERENT; + result = BMS_SUBSET2; + } + if ((bword & ~aword) != 0) + { + /* b is not a subset of a */ + if (result == BMS_SUBSET2) + return BMS_DIFFERENT; + result = BMS_SUBSET1; + } + } + /* Check extra words */ + if (a->nwords > b->nwords) + { + longlen = a->nwords; + for (; i < longlen; i++) + { + if (a->words[i] != 0) + { + /* a is not a subset of b */ + if (result == BMS_SUBSET1) + return BMS_DIFFERENT; + result = BMS_SUBSET2; + } + } + } + else if (a->nwords < b->nwords) + { + longlen = b->nwords; + for (; i < longlen; i++) + { + if (b->words[i] != 0) + { + /* b is not a subset of a */ + if (result == BMS_SUBSET2) + return BMS_DIFFERENT; + result = BMS_SUBSET1; + } + } + } + return result; +} + +/* + * bms_is_member - is X a member of A? + */ +bool +bms_is_member(int x, const Bitmapset *a) +{ + int wordnum, + bitnum; + + /* XXX better to just return false for x<0 ? */ + if (x < 0) + elog(ERROR, "negative bitmapset member not allowed"); + if (a == NULL) + return false; + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + if (wordnum >= a->nwords) + return false; + if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + return true; + return false; +} + +/* + * bms_member_index + * determine 0-based index of member x in the bitmap + * + * Returns (-1) when x is not a member. + */ +int +bms_member_index(Bitmapset *a, int x) +{ + int i; + int bitnum; + int wordnum; + int result = 0; + bitmapword mask; + + /* return -1 if not a member of the bitmap */ + if (!bms_is_member(x, a)) + return -1; + + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + + /* count bits in preceding words */ + for (i = 0; i < wordnum; i++) + { + bitmapword w = a->words[i]; + + /* No need to count the bits in a zero word */ + if (w != 0) + result += bmw_popcount(w); + } + + /* + * Now add bits of the last word, but only those before the item. We can + * do that by applying a mask and then using popcount again. To get + * 0-based index, we want to count only preceding bits, not the item + * itself, so we subtract 1. + */ + mask = ((bitmapword) 1 << bitnum) - 1; + result += bmw_popcount(a->words[wordnum] & mask); + + return result; +} + +/* + * bms_overlap - do sets overlap (ie, have a nonempty intersection)? + */ +bool +bms_overlap(const Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL || b == NULL) + return false; + /* Check words in common */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + if ((a->words[i] & b->words[i]) != 0) + return true; + } + return false; +} + +/* + * bms_overlap_list - does a set overlap an integer list? + */ +bool +bms_overlap_list(const Bitmapset *a, const List *b) +{ + ListCell *lc; + int wordnum, + bitnum; + + if (a == NULL || b == NIL) + return false; + + foreach(lc, b) + { + int x = lfirst_int(lc); + + if (x < 0) + elog(ERROR, "negative bitmapset member not allowed"); + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + if (wordnum < a->nwords) + if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + return true; + } + + return false; +} + +/* + * bms_nonempty_difference - do sets have a nonempty difference? + * + * i.e., are any members set in 'a' that are not also set in 'b'. + */ +bool +bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return false; + if (b == NULL) + return !bms_is_empty(a); + /* Check words in common */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + { + if ((a->words[i] & ~b->words[i]) != 0) + return true; + } + /* Check extra words in a */ + for (; i < a->nwords; i++) + { + if (a->words[i] != 0) + return true; + } + return false; +} + +/* + * bms_singleton_member - return the sole integer member of set + * + * Raises error if |a| is not 1. + */ +int +bms_singleton_member(const Bitmapset *a) +{ + int result = -1; + int nwords; + int wordnum; + + if (a == NULL) + elog(ERROR, "bitmapset is empty"); + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + if (result >= 0 || HAS_MULTIPLE_ONES(w)) + elog(ERROR, "bitmapset has multiple members"); + result = wordnum * BITS_PER_BITMAPWORD; + result += bmw_rightmost_one_pos(w); + } + } + if (result < 0) + elog(ERROR, "bitmapset is empty"); + return result; +} + +/* + * bms_get_singleton_member + * + * Test whether the given set is a singleton. + * If so, set *member to the value of its sole member, and return true. + * If not, return false, without changing *member. + * + * This is more convenient and faster than calling bms_membership() and then + * bms_singleton_member(), if we don't care about distinguishing empty sets + * from multiple-member sets. + */ +bool +bms_get_singleton_member(const Bitmapset *a, int *member) +{ + int result = -1; + int nwords; + int wordnum; + + if (a == NULL) + return false; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + if (result >= 0 || HAS_MULTIPLE_ONES(w)) + return false; + result = wordnum * BITS_PER_BITMAPWORD; + result += bmw_rightmost_one_pos(w); + } + } + if (result < 0) + return false; + *member = result; + return true; +} + +/* + * bms_num_members - count members of set + */ +int +bms_num_members(const Bitmapset *a) +{ + int result = 0; + int nwords; + int wordnum; + + if (a == NULL) + return 0; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + /* No need to count the bits in a zero word */ + if (w != 0) + result += bmw_popcount(w); + } + return result; +} + +/* + * bms_membership - does a set have zero, one, or multiple members? + * + * This is faster than making an exact count with bms_num_members(). + */ +BMS_Membership +bms_membership(const Bitmapset *a) +{ + BMS_Membership result = BMS_EMPTY_SET; + int nwords; + int wordnum; + + if (a == NULL) + return BMS_EMPTY_SET; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + if (result != BMS_EMPTY_SET || HAS_MULTIPLE_ONES(w)) + return BMS_MULTIPLE; + result = BMS_SINGLETON; + } + } + return result; +} + +/* + * bms_is_empty - is a set empty? + * + * This is even faster than bms_membership(). + */ +bool +bms_is_empty(const Bitmapset *a) +{ + int nwords; + int wordnum; + + if (a == NULL) + return true; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + return false; + } + return true; +} + + +/* + * These operations all "recycle" their non-const inputs, ie, either + * return the modified input or pfree it if it can't hold the result. + * + * These should generally be used in the style + * + * foo = bms_add_member(foo, x); + */ + + +/* + * bms_add_member - add a specified member to set + * + * Input set is modified or recycled! + */ +Bitmapset * +bms_add_member(Bitmapset *a, int x) +{ + int wordnum, + bitnum; + + if (x < 0) + elog(ERROR, "negative bitmapset member not allowed"); + if (a == NULL) + return bms_make_singleton(x); + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + + /* enlarge the set if necessary */ + if (wordnum >= a->nwords) + { + int oldnwords = a->nwords; + int i; + + a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(wordnum + 1)); + a->nwords = wordnum + 1; + /* zero out the enlarged portion */ + for (i = oldnwords; i < a->nwords; i++) + a->words[i] = 0; + } + + a->words[wordnum] |= ((bitmapword) 1 << bitnum); + return a; +} + +/* + * bms_del_member - remove a specified member from set + * + * No error if x is not currently a member of set + * + * Input set is modified in-place! + */ +Bitmapset * +bms_del_member(Bitmapset *a, int x) +{ + int wordnum, + bitnum; + + if (x < 0) + elog(ERROR, "negative bitmapset member not allowed"); + if (a == NULL) + return NULL; + wordnum = WORDNUM(x); + bitnum = BITNUM(x); + if (wordnum < a->nwords) + a->words[wordnum] &= ~((bitmapword) 1 << bitnum); + return a; +} + +/* + * bms_add_members - like bms_union, but left input is recycled + */ +Bitmapset * +bms_add_members(Bitmapset *a, const Bitmapset *b) +{ + Bitmapset *result; + const Bitmapset *other; + int otherlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return bms_copy(b); + if (b == NULL) + return a; + /* Identify shorter and longer input; copy the longer one if needed */ + if (a->nwords < b->nwords) + { + result = bms_copy(b); + other = a; + } + else + { + result = a; + other = b; + } + /* And union the shorter input into the result */ + otherlen = other->nwords; + for (i = 0; i < otherlen; i++) + result->words[i] |= other->words[i]; + if (result != a) + pfree(a); + return result; +} + +/* + * bms_add_range + * Add members in the range of 'lower' to 'upper' to the set. + * + * Note this could also be done by calling bms_add_member in a loop, however, + * using this function will be faster when the range is large as we work at + * the bitmapword level rather than at bit level. + */ +Bitmapset * +bms_add_range(Bitmapset *a, int lower, int upper) +{ + int lwordnum, + lbitnum, + uwordnum, + ushiftbits, + wordnum; + + /* do nothing if nothing is called for, without further checking */ + if (upper < lower) + return a; + + if (lower < 0) + elog(ERROR, "negative bitmapset member not allowed"); + uwordnum = WORDNUM(upper); + + if (a == NULL) + { + a = (Bitmapset *) palloc0(BITMAPSET_SIZE(uwordnum + 1)); + a->nwords = uwordnum + 1; + } + else if (uwordnum >= a->nwords) + { + int oldnwords = a->nwords; + int i; + + /* ensure we have enough words to store the upper bit */ + a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1)); + a->nwords = uwordnum + 1; + /* zero out the enlarged portion */ + for (i = oldnwords; i < a->nwords; i++) + a->words[i] = 0; + } + + wordnum = lwordnum = WORDNUM(lower); + + lbitnum = BITNUM(lower); + ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(upper) + 1); + + /* + * Special case when lwordnum is the same as uwordnum we must perform the + * upper and lower masking on the word. + */ + if (lwordnum == uwordnum) + { + a->words[lwordnum] |= ~(bitmapword) (((bitmapword) 1 << lbitnum) - 1) + & (~(bitmapword) 0) >> ushiftbits; + } + else + { + /* turn on lbitnum and all bits left of it */ + a->words[wordnum++] |= ~(bitmapword) (((bitmapword) 1 << lbitnum) - 1); + + /* turn on all bits for any intermediate words */ + while (wordnum < uwordnum) + a->words[wordnum++] = ~(bitmapword) 0; + + /* turn on upper's bit and all bits right of it. */ + a->words[uwordnum] |= (~(bitmapword) 0) >> ushiftbits; + } + + return a; +} + +/* + * bms_int_members - like bms_intersect, but left input is recycled + */ +Bitmapset * +bms_int_members(Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return NULL; + if (b == NULL) + { + pfree(a); + return NULL; + } + /* Intersect b into a; we need never copy */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + a->words[i] &= b->words[i]; + for (; i < a->nwords; i++) + a->words[i] = 0; + return a; +} + +/* + * bms_del_members - like bms_difference, but left input is recycled + */ +Bitmapset * +bms_del_members(Bitmapset *a, const Bitmapset *b) +{ + int shortlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return NULL; + if (b == NULL) + return a; + /* Remove b's bits from a; we need never copy */ + shortlen = Min(a->nwords, b->nwords); + for (i = 0; i < shortlen; i++) + a->words[i] &= ~b->words[i]; + return a; +} + +/* + * bms_join - like bms_union, but *both* inputs are recycled + */ +Bitmapset * +bms_join(Bitmapset *a, Bitmapset *b) +{ + Bitmapset *result; + Bitmapset *other; + int otherlen; + int i; + + /* Handle cases where either input is NULL */ + if (a == NULL) + return b; + if (b == NULL) + return a; + /* Identify shorter and longer input; use longer one as result */ + if (a->nwords < b->nwords) + { + result = b; + other = a; + } + else + { + result = a; + other = b; + } + /* And union the shorter input into the result */ + otherlen = other->nwords; + for (i = 0; i < otherlen; i++) + result->words[i] |= other->words[i]; + if (other != result) /* pure paranoia */ + pfree(other); + return result; +} + +/* + * bms_first_member - find and remove first member of a set + * + * Returns -1 if set is empty. NB: set is destructively modified! + * + * This is intended as support for iterating through the members of a set. + * The typical pattern is + * + * while ((x = bms_first_member(inputset)) >= 0) + * process member x; + * + * CAUTION: this destroys the content of "inputset". If the set must + * not be modified, use bms_next_member instead. + */ +int +bms_first_member(Bitmapset *a) +{ + int nwords; + int wordnum; + + if (a == NULL) + return -1; + nwords = a->nwords; + for (wordnum = 0; wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + if (w != 0) + { + int result; + + w = RIGHTMOST_ONE(w); + a->words[wordnum] &= ~w; + + result = wordnum * BITS_PER_BITMAPWORD; + result += bmw_rightmost_one_pos(w); + return result; + } + } + return -1; +} + +/* + * bms_next_member - find next member of a set + * + * Returns smallest member greater than "prevbit", or -2 if there is none. + * "prevbit" must NOT be less than -1, or the behavior is unpredictable. + * + * This is intended as support for iterating through the members of a set. + * The typical pattern is + * + * x = -1; + * while ((x = bms_next_member(inputset, x)) >= 0) + * process member x; + * + * Notice that when there are no more members, we return -2, not -1 as you + * might expect. The rationale for that is to allow distinguishing the + * loop-not-started state (x == -1) from the loop-completed state (x == -2). + * It makes no difference in simple loop usage, but complex iteration logic + * might need such an ability. + */ +int +bms_next_member(const Bitmapset *a, int prevbit) +{ + int nwords; + int wordnum; + bitmapword mask; + + if (a == NULL) + return -2; + nwords = a->nwords; + prevbit++; + mask = (~(bitmapword) 0) << BITNUM(prevbit); + for (wordnum = WORDNUM(prevbit); wordnum < nwords; wordnum++) + { + bitmapword w = a->words[wordnum]; + + /* ignore bits before prevbit */ + w &= mask; + + if (w != 0) + { + int result; + + result = wordnum * BITS_PER_BITMAPWORD; + result += bmw_rightmost_one_pos(w); + return result; + } + + /* in subsequent words, consider all bits */ + mask = (~(bitmapword) 0); + } + return -2; +} + +/* + * bms_prev_member - find prev member of a set + * + * Returns largest member less than "prevbit", or -2 if there is none. + * "prevbit" must NOT be more than one above the highest possible bit that can + * be set at the Bitmapset at its current size. + * + * To ease finding the highest set bit for the initial loop, the special + * prevbit value of -1 can be passed to have the function find the highest + * valued member in the set. + * + * This is intended as support for iterating through the members of a set in + * reverse. The typical pattern is + * + * x = -1; + * while ((x = bms_prev_member(inputset, x)) >= 0) + * process member x; + * + * Notice that when there are no more members, we return -2, not -1 as you + * might expect. The rationale for that is to allow distinguishing the + * loop-not-started state (x == -1) from the loop-completed state (x == -2). + * It makes no difference in simple loop usage, but complex iteration logic + * might need such an ability. + */ + +int +bms_prev_member(const Bitmapset *a, int prevbit) +{ + int wordnum; + int ushiftbits; + bitmapword mask; + + /* + * If set is NULL or if there are no more bits to the right then we've + * nothing to do. + */ + if (a == NULL || prevbit == 0) + return -2; + + /* transform -1 to the highest possible bit we could have set */ + if (prevbit == -1) + prevbit = a->nwords * BITS_PER_BITMAPWORD - 1; + else + prevbit--; + + ushiftbits = BITS_PER_BITMAPWORD - (BITNUM(prevbit) + 1); + mask = (~(bitmapword) 0) >> ushiftbits; + for (wordnum = WORDNUM(prevbit); wordnum >= 0; wordnum--) + { + bitmapword w = a->words[wordnum]; + + /* mask out bits left of prevbit */ + w &= mask; + + if (w != 0) + { + int result; + + result = wordnum * BITS_PER_BITMAPWORD; + result += bmw_leftmost_one_pos(w); + return result; + } + + /* in subsequent words, consider all bits */ + mask = (~(bitmapword) 0); + } + return -2; +} + +/* + * bms_hash_value - compute a hash key for a Bitmapset + * + * Note: we must ensure that any two bitmapsets that are bms_equal() will + * hash to the same value; in practice this means that trailing all-zero + * words must not affect the result. Hence we strip those before applying + * hash_any(). + */ +uint32 +bms_hash_value(const Bitmapset *a) +{ + int lastword; + + if (a == NULL) + return 0; /* All empty sets hash to 0 */ + for (lastword = a->nwords; --lastword >= 0;) + { + if (a->words[lastword] != 0) + break; + } + if (lastword < 0) + return 0; /* All empty sets hash to 0 */ + return DatumGetUInt32(hash_any((const unsigned char *) a->words, + (lastword + 1) * sizeof(bitmapword))); +} + +/* + * bitmap_hash - hash function for keys that are (pointers to) Bitmapsets + * + * Note: don't forget to specify bitmap_match as the match function! + */ +uint32 +bitmap_hash(const void *key, Size keysize) +{ + Assert(keysize == sizeof(Bitmapset *)); + return bms_hash_value(*((const Bitmapset *const *) key)); +} + +/* + * bitmap_match - match function to use with bitmap_hash + */ +int +bitmap_match(const void *key1, const void *key2, Size keysize) +{ + Assert(keysize == sizeof(Bitmapset *)); + return !bms_equal(*((const Bitmapset *const *) key1), + *((const Bitmapset *const *) key2)); +} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c new file mode 100644 index 0000000..b39b770 --- /dev/null +++ b/src/backend/nodes/copyfuncs.c @@ -0,0 +1,6023 @@ +/*------------------------------------------------------------------------- + * + * copyfuncs.c + * Copy functions for Postgres tree nodes. + * + * NOTE: we currently support copying all node types found in parse and + * plan trees. We do not support copying executor state trees; there + * is no need for that, and no point in maintaining all the code that + * would be needed. We also do not support copying Path trees, mainly + * because the circular linkages between RelOptInfo and Path nodes can't + * be handled easily in a simple depth-first traversal. + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/nodes/copyfuncs.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "miscadmin.h" +#include "nodes/extensible.h" +#include "nodes/pathnodes.h" +#include "nodes/plannodes.h" +#include "utils/datum.h" +#include "utils/rel.h" + + +/* + * Macros to simplify copying of different kinds of fields. Use these + * wherever possible to reduce the chance for silly typos. Note that these + * hard-wire the convention that the local variables in a Copy routine are + * named 'newnode' and 'from'. + */ + +/* Copy a simple scalar field (int, float, bool, enum, etc) */ +#define COPY_SCALAR_FIELD(fldname) \ + (newnode->fldname = from->fldname) + +/* Copy a field that is a pointer to some kind of Node or Node tree */ +#define COPY_NODE_FIELD(fldname) \ + (newnode->fldname = copyObjectImpl(from->fldname)) + +/* Copy a field that is a pointer to a Bitmapset */ +#define COPY_BITMAPSET_FIELD(fldname) \ + (newnode->fldname = bms_copy(from->fldname)) + +/* Copy a field that is a pointer to a C string, or perhaps NULL */ +#define COPY_STRING_FIELD(fldname) \ + (newnode->fldname = from->fldname ? pstrdup(from->fldname) : (char *) NULL) + +/* Copy a field that is an inline array */ +#define COPY_ARRAY_FIELD(fldname) \ + memcpy(newnode->fldname, from->fldname, sizeof(newnode->fldname)) + +/* Copy a field that is a pointer to a simple palloc'd object of size sz */ +#define COPY_POINTER_FIELD(fldname, sz) \ + do { \ + Size _size = (sz); \ + if (_size > 0) \ + { \ + newnode->fldname = palloc(_size); \ + memcpy(newnode->fldname, from->fldname, _size); \ + } \ + } while (0) + +/* Copy a parse location field (for Copy, this is same as scalar case) */ +#define COPY_LOCATION_FIELD(fldname) \ + (newnode->fldname = from->fldname) + + +/* **************************************************************** + * plannodes.h copy functions + * **************************************************************** + */ + +/* + * _copyPlannedStmt + */ +static PlannedStmt * +_copyPlannedStmt(const PlannedStmt *from) +{ + PlannedStmt *newnode = makeNode(PlannedStmt); + + COPY_SCALAR_FIELD(commandType); + COPY_SCALAR_FIELD(queryId); + COPY_SCALAR_FIELD(hasReturning); + COPY_SCALAR_FIELD(hasModifyingCTE); + COPY_SCALAR_FIELD(canSetTag); + COPY_SCALAR_FIELD(transientPlan); + COPY_SCALAR_FIELD(dependsOnRole); + COPY_SCALAR_FIELD(parallelModeNeeded); + COPY_SCALAR_FIELD(jitFlags); + COPY_NODE_FIELD(planTree); + COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(resultRelations); + COPY_NODE_FIELD(appendRelations); + COPY_NODE_FIELD(subplans); + COPY_BITMAPSET_FIELD(rewindPlanIDs); + COPY_NODE_FIELD(rowMarks); + COPY_NODE_FIELD(relationOids); + COPY_NODE_FIELD(invalItems); + COPY_NODE_FIELD(paramExecTypes); + COPY_NODE_FIELD(utilityStmt); + COPY_LOCATION_FIELD(stmt_location); + COPY_SCALAR_FIELD(stmt_len); + + return newnode; +} + +/* + * CopyPlanFields + * + * This function copies the fields of the Plan node. It is used by + * all the copy functions for classes which inherit from Plan. + */ +static void +CopyPlanFields(const Plan *from, Plan *newnode) +{ + COPY_SCALAR_FIELD(startup_cost); + COPY_SCALAR_FIELD(total_cost); + COPY_SCALAR_FIELD(plan_rows); + COPY_SCALAR_FIELD(plan_width); + COPY_SCALAR_FIELD(parallel_aware); + COPY_SCALAR_FIELD(parallel_safe); + COPY_SCALAR_FIELD(async_capable); + COPY_SCALAR_FIELD(plan_node_id); + COPY_NODE_FIELD(targetlist); + COPY_NODE_FIELD(qual); + COPY_NODE_FIELD(lefttree); + COPY_NODE_FIELD(righttree); + COPY_NODE_FIELD(initPlan); + COPY_BITMAPSET_FIELD(extParam); + COPY_BITMAPSET_FIELD(allParam); +} + +/* + * _copyPlan + */ +static Plan * +_copyPlan(const Plan *from) +{ + Plan *newnode = makeNode(Plan); + + /* + * copy node superclass fields + */ + CopyPlanFields(from, newnode); + + return newnode; +} + + +/* + * _copyResult + */ +static Result * +_copyResult(const Result *from) +{ + Result *newnode = makeNode(Result); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(resconstantqual); + + return newnode; +} + +/* + * _copyProjectSet + */ +static ProjectSet * +_copyProjectSet(const ProjectSet *from) +{ + ProjectSet *newnode = makeNode(ProjectSet); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + return newnode; +} + +/* + * _copyModifyTable + */ +static ModifyTable * +_copyModifyTable(const ModifyTable *from) +{ + ModifyTable *newnode = makeNode(ModifyTable); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(operation); + COPY_SCALAR_FIELD(canSetTag); + COPY_SCALAR_FIELD(nominalRelation); + COPY_SCALAR_FIELD(rootRelation); + COPY_SCALAR_FIELD(partColsUpdated); + COPY_NODE_FIELD(resultRelations); + COPY_NODE_FIELD(updateColnosLists); + COPY_NODE_FIELD(withCheckOptionLists); + COPY_NODE_FIELD(returningLists); + COPY_NODE_FIELD(fdwPrivLists); + COPY_BITMAPSET_FIELD(fdwDirectModifyPlans); + COPY_NODE_FIELD(rowMarks); + COPY_SCALAR_FIELD(epqParam); + COPY_SCALAR_FIELD(onConflictAction); + COPY_NODE_FIELD(arbiterIndexes); + COPY_NODE_FIELD(onConflictSet); + COPY_NODE_FIELD(onConflictCols); + COPY_NODE_FIELD(onConflictWhere); + COPY_SCALAR_FIELD(exclRelRTI); + COPY_NODE_FIELD(exclRelTlist); + COPY_NODE_FIELD(mergeActionLists); + + return newnode; +} + +/* + * _copyAppend + */ +static Append * +_copyAppend(const Append *from) +{ + Append *newnode = makeNode(Append); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_BITMAPSET_FIELD(apprelids); + COPY_NODE_FIELD(appendplans); + COPY_SCALAR_FIELD(nasyncplans); + COPY_SCALAR_FIELD(first_partial_plan); + COPY_NODE_FIELD(part_prune_info); + + return newnode; +} + +/* + * _copyMergeAppend + */ +static MergeAppend * +_copyMergeAppend(const MergeAppend *from) +{ + MergeAppend *newnode = makeNode(MergeAppend); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_BITMAPSET_FIELD(apprelids); + COPY_NODE_FIELD(mergeplans); + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); + COPY_NODE_FIELD(part_prune_info); + + return newnode; +} + +/* + * _copyRecursiveUnion + */ +static RecursiveUnion * +_copyRecursiveUnion(const RecursiveUnion *from) +{ + RecursiveUnion *newnode = makeNode(RecursiveUnion); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(wtParam); + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(dupCollations, from->numCols * sizeof(Oid)); + COPY_SCALAR_FIELD(numGroups); + + return newnode; +} + +/* + * _copyBitmapAnd + */ +static BitmapAnd * +_copyBitmapAnd(const BitmapAnd *from) +{ + BitmapAnd *newnode = makeNode(BitmapAnd); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(bitmapplans); + + return newnode; +} + +/* + * _copyBitmapOr + */ +static BitmapOr * +_copyBitmapOr(const BitmapOr *from) +{ + BitmapOr *newnode = makeNode(BitmapOr); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(isshared); + COPY_NODE_FIELD(bitmapplans); + + return newnode; +} + +/* + * _copyGather + */ +static Gather * +_copyGather(const Gather *from) +{ + Gather *newnode = makeNode(Gather); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(num_workers); + COPY_SCALAR_FIELD(rescan_param); + COPY_SCALAR_FIELD(single_copy); + COPY_SCALAR_FIELD(invisible); + COPY_BITMAPSET_FIELD(initParam); + + return newnode; +} + +/* + * _copyGatherMerge + */ +static GatherMerge * +_copyGatherMerge(const GatherMerge *from) +{ + GatherMerge *newnode = makeNode(GatherMerge); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(num_workers); + COPY_SCALAR_FIELD(rescan_param); + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); + COPY_BITMAPSET_FIELD(initParam); + + return newnode; +} + +/* + * CopyScanFields + * + * This function copies the fields of the Scan node. It is used by + * all the copy functions for classes which inherit from Scan. + */ +static void +CopyScanFields(const Scan *from, Scan *newnode) +{ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(scanrelid); +} + +/* + * _copyScan + */ +static Scan * +_copyScan(const Scan *from) +{ + Scan *newnode = makeNode(Scan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + return newnode; +} + +/* + * _copySeqScan + */ +static SeqScan * +_copySeqScan(const SeqScan *from) +{ + SeqScan *newnode = makeNode(SeqScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + return newnode; +} + +/* + * _copySampleScan + */ +static SampleScan * +_copySampleScan(const SampleScan *from) +{ + SampleScan *newnode = makeNode(SampleScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(tablesample); + + return newnode; +} + +/* + * _copyIndexScan + */ +static IndexScan * +_copyIndexScan(const IndexScan *from) +{ + IndexScan *newnode = makeNode(IndexScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(indexid); + COPY_NODE_FIELD(indexqual); + COPY_NODE_FIELD(indexqualorig); + COPY_NODE_FIELD(indexorderby); + COPY_NODE_FIELD(indexorderbyorig); + COPY_NODE_FIELD(indexorderbyops); + COPY_SCALAR_FIELD(indexorderdir); + + return newnode; +} + +/* + * _copyIndexOnlyScan + */ +static IndexOnlyScan * +_copyIndexOnlyScan(const IndexOnlyScan *from) +{ + IndexOnlyScan *newnode = makeNode(IndexOnlyScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(indexid); + COPY_NODE_FIELD(indexqual); + COPY_NODE_FIELD(recheckqual); + COPY_NODE_FIELD(indexorderby); + COPY_NODE_FIELD(indextlist); + COPY_SCALAR_FIELD(indexorderdir); + + return newnode; +} + +/* + * _copyBitmapIndexScan + */ +static BitmapIndexScan * +_copyBitmapIndexScan(const BitmapIndexScan *from) +{ + BitmapIndexScan *newnode = makeNode(BitmapIndexScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(indexid); + COPY_SCALAR_FIELD(isshared); + COPY_NODE_FIELD(indexqual); + COPY_NODE_FIELD(indexqualorig); + + return newnode; +} + +/* + * _copyBitmapHeapScan + */ +static BitmapHeapScan * +_copyBitmapHeapScan(const BitmapHeapScan *from) +{ + BitmapHeapScan *newnode = makeNode(BitmapHeapScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(bitmapqualorig); + + return newnode; +} + +/* + * _copyTidScan + */ +static TidScan * +_copyTidScan(const TidScan *from) +{ + TidScan *newnode = makeNode(TidScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(tidquals); + + return newnode; +} + +/* + * _copyTidRangeScan + */ +static TidRangeScan * +_copyTidRangeScan(const TidRangeScan *from) +{ + TidRangeScan *newnode = makeNode(TidRangeScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(tidrangequals); + + return newnode; +} + +/* + * _copySubqueryScan + */ +static SubqueryScan * +_copySubqueryScan(const SubqueryScan *from) +{ + SubqueryScan *newnode = makeNode(SubqueryScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(subplan); + COPY_SCALAR_FIELD(scanstatus); + + return newnode; +} + +/* + * _copyFunctionScan + */ +static FunctionScan * +_copyFunctionScan(const FunctionScan *from) +{ + FunctionScan *newnode = makeNode(FunctionScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(functions); + COPY_SCALAR_FIELD(funcordinality); + + return newnode; +} + +/* + * _copyTableFuncScan + */ +static TableFuncScan * +_copyTableFuncScan(const TableFuncScan *from) +{ + TableFuncScan *newnode = makeNode(TableFuncScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(tablefunc); + + return newnode; +} + +/* + * _copyValuesScan + */ +static ValuesScan * +_copyValuesScan(const ValuesScan *from) +{ + ValuesScan *newnode = makeNode(ValuesScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(values_lists); + + return newnode; +} + +/* + * _copyCteScan + */ +static CteScan * +_copyCteScan(const CteScan *from) +{ + CteScan *newnode = makeNode(CteScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(ctePlanId); + COPY_SCALAR_FIELD(cteParam); + + return newnode; +} + +/* + * _copyNamedTuplestoreScan + */ +static NamedTuplestoreScan * +_copyNamedTuplestoreScan(const NamedTuplestoreScan *from) +{ + NamedTuplestoreScan *newnode = makeNode(NamedTuplestoreScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_STRING_FIELD(enrname); + + return newnode; +} + +/* + * _copyWorkTableScan + */ +static WorkTableScan * +_copyWorkTableScan(const WorkTableScan *from) +{ + WorkTableScan *newnode = makeNode(WorkTableScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(wtParam); + + return newnode; +} + +/* + * _copyForeignScan + */ +static ForeignScan * +_copyForeignScan(const ForeignScan *from) +{ + ForeignScan *newnode = makeNode(ForeignScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(operation); + COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(fs_server); + COPY_NODE_FIELD(fdw_exprs); + COPY_NODE_FIELD(fdw_private); + COPY_NODE_FIELD(fdw_scan_tlist); + COPY_NODE_FIELD(fdw_recheck_quals); + COPY_BITMAPSET_FIELD(fs_relids); + COPY_SCALAR_FIELD(fsSystemCol); + + return newnode; +} + +/* + * _copyCustomScan + */ +static CustomScan * +_copyCustomScan(const CustomScan *from) +{ + CustomScan *newnode = makeNode(CustomScan); + + /* + * copy node superclass fields + */ + CopyScanFields((const Scan *) from, (Scan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(flags); + COPY_NODE_FIELD(custom_plans); + COPY_NODE_FIELD(custom_exprs); + COPY_NODE_FIELD(custom_private); + COPY_NODE_FIELD(custom_scan_tlist); + COPY_BITMAPSET_FIELD(custom_relids); + + /* + * NOTE: The method field of CustomScan is required to be a pointer to a + * static table of callback functions. So we don't copy the table itself, + * just reference the original one. + */ + COPY_SCALAR_FIELD(methods); + + return newnode; +} + +/* + * CopyJoinFields + * + * This function copies the fields of the Join node. It is used by + * all the copy functions for classes which inherit from Join. + */ +static void +CopyJoinFields(const Join *from, Join *newnode) +{ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(jointype); + COPY_SCALAR_FIELD(inner_unique); + COPY_NODE_FIELD(joinqual); +} + + +/* + * _copyJoin + */ +static Join * +_copyJoin(const Join *from) +{ + Join *newnode = makeNode(Join); + + /* + * copy node superclass fields + */ + CopyJoinFields(from, newnode); + + return newnode; +} + + +/* + * _copyNestLoop + */ +static NestLoop * +_copyNestLoop(const NestLoop *from) +{ + NestLoop *newnode = makeNode(NestLoop); + + /* + * copy node superclass fields + */ + CopyJoinFields((const Join *) from, (Join *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(nestParams); + + return newnode; +} + + +/* + * _copyMergeJoin + */ +static MergeJoin * +_copyMergeJoin(const MergeJoin *from) +{ + MergeJoin *newnode = makeNode(MergeJoin); + int numCols; + + /* + * copy node superclass fields + */ + CopyJoinFields((const Join *) from, (Join *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(skip_mark_restore); + COPY_NODE_FIELD(mergeclauses); + numCols = list_length(from->mergeclauses); + COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid)); + COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid)); + COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int)); + COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool)); + + return newnode; +} + +/* + * _copyHashJoin + */ +static HashJoin * +_copyHashJoin(const HashJoin *from) +{ + HashJoin *newnode = makeNode(HashJoin); + + /* + * copy node superclass fields + */ + CopyJoinFields((const Join *) from, (Join *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(hashclauses); + COPY_NODE_FIELD(hashoperators); + COPY_NODE_FIELD(hashcollations); + COPY_NODE_FIELD(hashkeys); + + return newnode; +} + + +/* + * _copyMaterial + */ +static Material * +_copyMaterial(const Material *from) +{ + Material *newnode = makeNode(Material); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + return newnode; +} + + +/* + * _copyMemoize + */ +static Memoize * +_copyMemoize(const Memoize *from) +{ + Memoize *newnode = makeNode(Memoize); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(numKeys); + COPY_POINTER_FIELD(hashOperators, sizeof(Oid) * from->numKeys); + COPY_POINTER_FIELD(collations, sizeof(Oid) * from->numKeys); + COPY_NODE_FIELD(param_exprs); + COPY_SCALAR_FIELD(singlerow); + COPY_SCALAR_FIELD(binary_mode); + COPY_SCALAR_FIELD(est_entries); + COPY_BITMAPSET_FIELD(keyparamids); + + return newnode; +} + + +/* + * CopySortFields + * + * This function copies the fields of the Sort node. It is used by + * all the copy functions for classes which inherit from Sort. + */ +static void +CopySortFields(const Sort *from, Sort *newnode) +{ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); +} + +/* + * _copySort + */ +static Sort * +_copySort(const Sort *from) +{ + Sort *newnode = makeNode(Sort); + + /* + * copy node superclass fields + */ + CopySortFields(from, newnode); + + return newnode; +} + + +/* + * _copyIncrementalSort + */ +static IncrementalSort * +_copyIncrementalSort(const IncrementalSort *from) +{ + IncrementalSort *newnode = makeNode(IncrementalSort); + + /* + * copy node superclass fields + */ + CopySortFields((const Sort *) from, (Sort *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(nPresortedCols); + + return newnode; +} + + +/* + * _copyGroup + */ +static Group * +_copyGroup(const Group *from) +{ + Group *newnode = makeNode(Group); + + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(grpCollations, from->numCols * sizeof(Oid)); + + return newnode; +} + +/* + * _copyAgg + */ +static Agg * +_copyAgg(const Agg *from) +{ + Agg *newnode = makeNode(Agg); + + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(aggstrategy); + COPY_SCALAR_FIELD(aggsplit); + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(grpColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(grpOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(grpCollations, from->numCols * sizeof(Oid)); + COPY_SCALAR_FIELD(numGroups); + COPY_SCALAR_FIELD(transitionSpace); + COPY_BITMAPSET_FIELD(aggParams); + COPY_NODE_FIELD(groupingSets); + COPY_NODE_FIELD(chain); + + return newnode; +} + +/* + * _copyWindowAgg + */ +static WindowAgg * +_copyWindowAgg(const WindowAgg *from) +{ + WindowAgg *newnode = makeNode(WindowAgg); + + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(winref); + COPY_SCALAR_FIELD(partNumCols); + COPY_POINTER_FIELD(partColIdx, from->partNumCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(partOperators, from->partNumCols * sizeof(Oid)); + COPY_POINTER_FIELD(partCollations, from->partNumCols * sizeof(Oid)); + COPY_SCALAR_FIELD(ordNumCols); + COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid)); + COPY_POINTER_FIELD(ordCollations, from->ordNumCols * sizeof(Oid)); + COPY_SCALAR_FIELD(frameOptions); + COPY_NODE_FIELD(startOffset); + COPY_NODE_FIELD(endOffset); + COPY_NODE_FIELD(runCondition); + COPY_NODE_FIELD(runConditionOrig); + COPY_SCALAR_FIELD(startInRangeFunc); + COPY_SCALAR_FIELD(endInRangeFunc); + COPY_SCALAR_FIELD(inRangeColl); + COPY_SCALAR_FIELD(inRangeAsc); + COPY_SCALAR_FIELD(inRangeNullsFirst); + COPY_SCALAR_FIELD(topWindow); + + return newnode; +} + +/* + * _copyUnique + */ +static Unique * +_copyUnique(const Unique *from) +{ + Unique *newnode = makeNode(Unique); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(uniqColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(uniqOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(uniqCollations, from->numCols * sizeof(Oid)); + + return newnode; +} + +/* + * _copyHash + */ +static Hash * +_copyHash(const Hash *from) +{ + Hash *newnode = makeNode(Hash); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(hashkeys); + COPY_SCALAR_FIELD(skewTable); + COPY_SCALAR_FIELD(skewColumn); + COPY_SCALAR_FIELD(skewInherit); + COPY_SCALAR_FIELD(rows_total); + + return newnode; +} + +/* + * _copySetOp + */ +static SetOp * +_copySetOp(const SetOp *from) +{ + SetOp *newnode = makeNode(SetOp); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_SCALAR_FIELD(cmd); + COPY_SCALAR_FIELD(strategy); + COPY_SCALAR_FIELD(numCols); + COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(dupOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(dupCollations, from->numCols * sizeof(Oid)); + COPY_SCALAR_FIELD(flagColIdx); + COPY_SCALAR_FIELD(firstFlag); + COPY_SCALAR_FIELD(numGroups); + + return newnode; +} + +/* + * _copyLockRows + */ +static LockRows * +_copyLockRows(const LockRows *from) +{ + LockRows *newnode = makeNode(LockRows); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(rowMarks); + COPY_SCALAR_FIELD(epqParam); + + return newnode; +} + +/* + * _copyLimit + */ +static Limit * +_copyLimit(const Limit *from) +{ + Limit *newnode = makeNode(Limit); + + /* + * copy node superclass fields + */ + CopyPlanFields((const Plan *) from, (Plan *) newnode); + + /* + * copy remainder of node + */ + COPY_NODE_FIELD(limitOffset); + COPY_NODE_FIELD(limitCount); + COPY_SCALAR_FIELD(limitOption); + COPY_SCALAR_FIELD(uniqNumCols); + COPY_POINTER_FIELD(uniqColIdx, from->uniqNumCols * sizeof(AttrNumber)); + COPY_POINTER_FIELD(uniqOperators, from->uniqNumCols * sizeof(Oid)); + COPY_POINTER_FIELD(uniqCollations, from->uniqNumCols * sizeof(Oid)); + + return newnode; +} + +/* + * _copyNestLoopParam + */ +static NestLoopParam * +_copyNestLoopParam(const NestLoopParam *from) +{ + NestLoopParam *newnode = makeNode(NestLoopParam); + + COPY_SCALAR_FIELD(paramno); + COPY_NODE_FIELD(paramval); + + return newnode; +} + +/* + * _copyPlanRowMark + */ +static PlanRowMark * +_copyPlanRowMark(const PlanRowMark *from) +{ + PlanRowMark *newnode = makeNode(PlanRowMark); + + COPY_SCALAR_FIELD(rti); + COPY_SCALAR_FIELD(prti); + COPY_SCALAR_FIELD(rowmarkId); + COPY_SCALAR_FIELD(markType); + COPY_SCALAR_FIELD(allMarkTypes); + COPY_SCALAR_FIELD(strength); + COPY_SCALAR_FIELD(waitPolicy); + COPY_SCALAR_FIELD(isParent); + + return newnode; +} + +static PartitionPruneInfo * +_copyPartitionPruneInfo(const PartitionPruneInfo *from) +{ + PartitionPruneInfo *newnode = makeNode(PartitionPruneInfo); + + COPY_NODE_FIELD(prune_infos); + COPY_BITMAPSET_FIELD(other_subplans); + + return newnode; +} + +static PartitionedRelPruneInfo * +_copyPartitionedRelPruneInfo(const PartitionedRelPruneInfo *from) +{ + PartitionedRelPruneInfo *newnode = makeNode(PartitionedRelPruneInfo); + + COPY_SCALAR_FIELD(rtindex); + COPY_BITMAPSET_FIELD(present_parts); + COPY_SCALAR_FIELD(nparts); + COPY_POINTER_FIELD(subplan_map, from->nparts * sizeof(int)); + COPY_POINTER_FIELD(subpart_map, from->nparts * sizeof(int)); + COPY_POINTER_FIELD(relid_map, from->nparts * sizeof(Oid)); + COPY_NODE_FIELD(initial_pruning_steps); + COPY_NODE_FIELD(exec_pruning_steps); + COPY_BITMAPSET_FIELD(execparamids); + + return newnode; +} + +/* + * _copyPartitionPruneStepOp + */ +static PartitionPruneStepOp * +_copyPartitionPruneStepOp(const PartitionPruneStepOp *from) +{ + PartitionPruneStepOp *newnode = makeNode(PartitionPruneStepOp); + + COPY_SCALAR_FIELD(step.step_id); + COPY_SCALAR_FIELD(opstrategy); + COPY_NODE_FIELD(exprs); + COPY_NODE_FIELD(cmpfns); + COPY_BITMAPSET_FIELD(nullkeys); + + return newnode; +} + +/* + * _copyPartitionPruneStepCombine + */ +static PartitionPruneStepCombine * +_copyPartitionPruneStepCombine(const PartitionPruneStepCombine *from) +{ + PartitionPruneStepCombine *newnode = makeNode(PartitionPruneStepCombine); + + COPY_SCALAR_FIELD(step.step_id); + COPY_SCALAR_FIELD(combineOp); + COPY_NODE_FIELD(source_stepids); + + return newnode; +} + +/* + * _copyPlanInvalItem + */ +static PlanInvalItem * +_copyPlanInvalItem(const PlanInvalItem *from) +{ + PlanInvalItem *newnode = makeNode(PlanInvalItem); + + COPY_SCALAR_FIELD(cacheId); + COPY_SCALAR_FIELD(hashValue); + + return newnode; +} + +/* **************************************************************** + * primnodes.h copy functions + * **************************************************************** + */ + +/* + * _copyAlias + */ +static Alias * +_copyAlias(const Alias *from) +{ + Alias *newnode = makeNode(Alias); + + COPY_STRING_FIELD(aliasname); + COPY_NODE_FIELD(colnames); + + return newnode; +} + +/* + * _copyRangeVar + */ +static RangeVar * +_copyRangeVar(const RangeVar *from) +{ + RangeVar *newnode = makeNode(RangeVar); + + COPY_STRING_FIELD(catalogname); + COPY_STRING_FIELD(schemaname); + COPY_STRING_FIELD(relname); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(relpersistence); + COPY_NODE_FIELD(alias); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyTableFunc + */ +static TableFunc * +_copyTableFunc(const TableFunc *from) +{ + TableFunc *newnode = makeNode(TableFunc); + + COPY_NODE_FIELD(ns_uris); + COPY_NODE_FIELD(ns_names); + COPY_NODE_FIELD(docexpr); + COPY_NODE_FIELD(rowexpr); + COPY_NODE_FIELD(colnames); + COPY_NODE_FIELD(coltypes); + COPY_NODE_FIELD(coltypmods); + COPY_NODE_FIELD(colcollations); + COPY_NODE_FIELD(colexprs); + COPY_NODE_FIELD(coldefexprs); + COPY_BITMAPSET_FIELD(notnulls); + COPY_SCALAR_FIELD(ordinalitycol); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyIntoClause + */ +static IntoClause * +_copyIntoClause(const IntoClause *from) +{ + IntoClause *newnode = makeNode(IntoClause); + + COPY_NODE_FIELD(rel); + COPY_NODE_FIELD(colNames); + COPY_STRING_FIELD(accessMethod); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(onCommit); + COPY_STRING_FIELD(tableSpaceName); + COPY_NODE_FIELD(viewQuery); + COPY_SCALAR_FIELD(skipData); + + return newnode; +} + +/* + * We don't need a _copyExpr because Expr is an abstract supertype which + * should never actually get instantiated. Also, since it has no common + * fields except NodeTag, there's no need for a helper routine to factor + * out copying the common fields... + */ + +/* + * _copyVar + */ +static Var * +_copyVar(const Var *from) +{ + Var *newnode = makeNode(Var); + + COPY_SCALAR_FIELD(varno); + COPY_SCALAR_FIELD(varattno); + COPY_SCALAR_FIELD(vartype); + COPY_SCALAR_FIELD(vartypmod); + COPY_SCALAR_FIELD(varcollid); + COPY_SCALAR_FIELD(varlevelsup); + COPY_SCALAR_FIELD(varnosyn); + COPY_SCALAR_FIELD(varattnosyn); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyConst + */ +static Const * +_copyConst(const Const *from) +{ + Const *newnode = makeNode(Const); + + COPY_SCALAR_FIELD(consttype); + COPY_SCALAR_FIELD(consttypmod); + COPY_SCALAR_FIELD(constcollid); + COPY_SCALAR_FIELD(constlen); + + if (from->constbyval || from->constisnull) + { + /* + * passed by value so just copy the datum. Also, don't try to copy + * struct when value is null! + */ + newnode->constvalue = from->constvalue; + } + else + { + /* + * passed by reference. We need a palloc'd copy. + */ + newnode->constvalue = datumCopy(from->constvalue, + from->constbyval, + from->constlen); + } + + COPY_SCALAR_FIELD(constisnull); + COPY_SCALAR_FIELD(constbyval); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyParam + */ +static Param * +_copyParam(const Param *from) +{ + Param *newnode = makeNode(Param); + + COPY_SCALAR_FIELD(paramkind); + COPY_SCALAR_FIELD(paramid); + COPY_SCALAR_FIELD(paramtype); + COPY_SCALAR_FIELD(paramtypmod); + COPY_SCALAR_FIELD(paramcollid); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyAggref + */ +static Aggref * +_copyAggref(const Aggref *from) +{ + Aggref *newnode = makeNode(Aggref); + + COPY_SCALAR_FIELD(aggfnoid); + COPY_SCALAR_FIELD(aggtype); + COPY_SCALAR_FIELD(aggcollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_SCALAR_FIELD(aggtranstype); + COPY_NODE_FIELD(aggargtypes); + COPY_NODE_FIELD(aggdirectargs); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(aggorder); + COPY_NODE_FIELD(aggdistinct); + COPY_NODE_FIELD(aggfilter); + COPY_SCALAR_FIELD(aggstar); + COPY_SCALAR_FIELD(aggvariadic); + COPY_SCALAR_FIELD(aggkind); + COPY_SCALAR_FIELD(agglevelsup); + COPY_SCALAR_FIELD(aggsplit); + COPY_SCALAR_FIELD(aggno); + COPY_SCALAR_FIELD(aggtransno); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyGroupingFunc + */ +static GroupingFunc * +_copyGroupingFunc(const GroupingFunc *from) +{ + GroupingFunc *newnode = makeNode(GroupingFunc); + + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(refs); + COPY_NODE_FIELD(cols); + COPY_SCALAR_FIELD(agglevelsup); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyWindowFunc + */ +static WindowFunc * +_copyWindowFunc(const WindowFunc *from) +{ + WindowFunc *newnode = makeNode(WindowFunc); + + COPY_SCALAR_FIELD(winfnoid); + COPY_SCALAR_FIELD(wintype); + COPY_SCALAR_FIELD(wincollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(aggfilter); + COPY_SCALAR_FIELD(winref); + COPY_SCALAR_FIELD(winstar); + COPY_SCALAR_FIELD(winagg); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copySubscriptingRef + */ +static SubscriptingRef * +_copySubscriptingRef(const SubscriptingRef *from) +{ + SubscriptingRef *newnode = makeNode(SubscriptingRef); + + COPY_SCALAR_FIELD(refcontainertype); + COPY_SCALAR_FIELD(refelemtype); + COPY_SCALAR_FIELD(refrestype); + COPY_SCALAR_FIELD(reftypmod); + COPY_SCALAR_FIELD(refcollid); + COPY_NODE_FIELD(refupperindexpr); + COPY_NODE_FIELD(reflowerindexpr); + COPY_NODE_FIELD(refexpr); + COPY_NODE_FIELD(refassgnexpr); + + return newnode; +} + +/* + * _copyFuncExpr + */ +static FuncExpr * +_copyFuncExpr(const FuncExpr *from) +{ + FuncExpr *newnode = makeNode(FuncExpr); + + COPY_SCALAR_FIELD(funcid); + COPY_SCALAR_FIELD(funcresulttype); + COPY_SCALAR_FIELD(funcretset); + COPY_SCALAR_FIELD(funcvariadic); + COPY_SCALAR_FIELD(funcformat); + COPY_SCALAR_FIELD(funccollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyNamedArgExpr * + */ +static NamedArgExpr * +_copyNamedArgExpr(const NamedArgExpr *from) +{ + NamedArgExpr *newnode = makeNode(NamedArgExpr); + + COPY_NODE_FIELD(arg); + COPY_STRING_FIELD(name); + COPY_SCALAR_FIELD(argnumber); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyOpExpr + */ +static OpExpr * +_copyOpExpr(const OpExpr *from) +{ + OpExpr *newnode = makeNode(OpExpr); + + COPY_SCALAR_FIELD(opno); + COPY_SCALAR_FIELD(opfuncid); + COPY_SCALAR_FIELD(opresulttype); + COPY_SCALAR_FIELD(opretset); + COPY_SCALAR_FIELD(opcollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyDistinctExpr (same as OpExpr) + */ +static DistinctExpr * +_copyDistinctExpr(const DistinctExpr *from) +{ + DistinctExpr *newnode = makeNode(DistinctExpr); + + COPY_SCALAR_FIELD(opno); + COPY_SCALAR_FIELD(opfuncid); + COPY_SCALAR_FIELD(opresulttype); + COPY_SCALAR_FIELD(opretset); + COPY_SCALAR_FIELD(opcollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyNullIfExpr (same as OpExpr) + */ +static NullIfExpr * +_copyNullIfExpr(const NullIfExpr *from) +{ + NullIfExpr *newnode = makeNode(NullIfExpr); + + COPY_SCALAR_FIELD(opno); + COPY_SCALAR_FIELD(opfuncid); + COPY_SCALAR_FIELD(opresulttype); + COPY_SCALAR_FIELD(opretset); + COPY_SCALAR_FIELD(opcollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyScalarArrayOpExpr + */ +static ScalarArrayOpExpr * +_copyScalarArrayOpExpr(const ScalarArrayOpExpr *from) +{ + ScalarArrayOpExpr *newnode = makeNode(ScalarArrayOpExpr); + + COPY_SCALAR_FIELD(opno); + COPY_SCALAR_FIELD(opfuncid); + COPY_SCALAR_FIELD(hashfuncid); + COPY_SCALAR_FIELD(negfuncid); + COPY_SCALAR_FIELD(useOr); + COPY_SCALAR_FIELD(inputcollid); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyBoolExpr + */ +static BoolExpr * +_copyBoolExpr(const BoolExpr *from) +{ + BoolExpr *newnode = makeNode(BoolExpr); + + COPY_SCALAR_FIELD(boolop); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copySubLink + */ +static SubLink * +_copySubLink(const SubLink *from) +{ + SubLink *newnode = makeNode(SubLink); + + COPY_SCALAR_FIELD(subLinkType); + COPY_SCALAR_FIELD(subLinkId); + COPY_NODE_FIELD(testexpr); + COPY_NODE_FIELD(operName); + COPY_NODE_FIELD(subselect); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copySubPlan + */ +static SubPlan * +_copySubPlan(const SubPlan *from) +{ + SubPlan *newnode = makeNode(SubPlan); + + COPY_SCALAR_FIELD(subLinkType); + COPY_NODE_FIELD(testexpr); + COPY_NODE_FIELD(paramIds); + COPY_SCALAR_FIELD(plan_id); + COPY_STRING_FIELD(plan_name); + COPY_SCALAR_FIELD(firstColType); + COPY_SCALAR_FIELD(firstColTypmod); + COPY_SCALAR_FIELD(firstColCollation); + COPY_SCALAR_FIELD(useHashTable); + COPY_SCALAR_FIELD(unknownEqFalse); + COPY_SCALAR_FIELD(parallel_safe); + COPY_NODE_FIELD(setParam); + COPY_NODE_FIELD(parParam); + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(startup_cost); + COPY_SCALAR_FIELD(per_call_cost); + + return newnode; +} + +/* + * _copyAlternativeSubPlan + */ +static AlternativeSubPlan * +_copyAlternativeSubPlan(const AlternativeSubPlan *from) +{ + AlternativeSubPlan *newnode = makeNode(AlternativeSubPlan); + + COPY_NODE_FIELD(subplans); + + return newnode; +} + +/* + * _copyFieldSelect + */ +static FieldSelect * +_copyFieldSelect(const FieldSelect *from) +{ + FieldSelect *newnode = makeNode(FieldSelect); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(fieldnum); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(resulttypmod); + COPY_SCALAR_FIELD(resultcollid); + + return newnode; +} + +/* + * _copyFieldStore + */ +static FieldStore * +_copyFieldStore(const FieldStore *from) +{ + FieldStore *newnode = makeNode(FieldStore); + + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(newvals); + COPY_NODE_FIELD(fieldnums); + COPY_SCALAR_FIELD(resulttype); + + return newnode; +} + +/* + * _copyRelabelType + */ +static RelabelType * +_copyRelabelType(const RelabelType *from) +{ + RelabelType *newnode = makeNode(RelabelType); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(resulttypmod); + COPY_SCALAR_FIELD(resultcollid); + COPY_SCALAR_FIELD(relabelformat); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCoerceViaIO + */ +static CoerceViaIO * +_copyCoerceViaIO(const CoerceViaIO *from) +{ + CoerceViaIO *newnode = makeNode(CoerceViaIO); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(resultcollid); + COPY_SCALAR_FIELD(coerceformat); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyArrayCoerceExpr + */ +static ArrayCoerceExpr * +_copyArrayCoerceExpr(const ArrayCoerceExpr *from) +{ + ArrayCoerceExpr *newnode = makeNode(ArrayCoerceExpr); + + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(elemexpr); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(resulttypmod); + COPY_SCALAR_FIELD(resultcollid); + COPY_SCALAR_FIELD(coerceformat); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyConvertRowtypeExpr + */ +static ConvertRowtypeExpr * +_copyConvertRowtypeExpr(const ConvertRowtypeExpr *from) +{ + ConvertRowtypeExpr *newnode = makeNode(ConvertRowtypeExpr); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(convertformat); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCollateExpr + */ +static CollateExpr * +_copyCollateExpr(const CollateExpr *from) +{ + CollateExpr *newnode = makeNode(CollateExpr); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(collOid); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCaseExpr + */ +static CaseExpr * +_copyCaseExpr(const CaseExpr *from) +{ + CaseExpr *newnode = makeNode(CaseExpr); + + COPY_SCALAR_FIELD(casetype); + COPY_SCALAR_FIELD(casecollid); + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(defresult); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCaseWhen + */ +static CaseWhen * +_copyCaseWhen(const CaseWhen *from) +{ + CaseWhen *newnode = makeNode(CaseWhen); + + COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(result); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCaseTestExpr + */ +static CaseTestExpr * +_copyCaseTestExpr(const CaseTestExpr *from) +{ + CaseTestExpr *newnode = makeNode(CaseTestExpr); + + COPY_SCALAR_FIELD(typeId); + COPY_SCALAR_FIELD(typeMod); + COPY_SCALAR_FIELD(collation); + + return newnode; +} + +/* + * _copyArrayExpr + */ +static ArrayExpr * +_copyArrayExpr(const ArrayExpr *from) +{ + ArrayExpr *newnode = makeNode(ArrayExpr); + + COPY_SCALAR_FIELD(array_typeid); + COPY_SCALAR_FIELD(array_collid); + COPY_SCALAR_FIELD(element_typeid); + COPY_NODE_FIELD(elements); + COPY_SCALAR_FIELD(multidims); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyRowExpr + */ +static RowExpr * +_copyRowExpr(const RowExpr *from) +{ + RowExpr *newnode = makeNode(RowExpr); + + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(row_typeid); + COPY_SCALAR_FIELD(row_format); + COPY_NODE_FIELD(colnames); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyRowCompareExpr + */ +static RowCompareExpr * +_copyRowCompareExpr(const RowCompareExpr *from) +{ + RowCompareExpr *newnode = makeNode(RowCompareExpr); + + COPY_SCALAR_FIELD(rctype); + COPY_NODE_FIELD(opnos); + COPY_NODE_FIELD(opfamilies); + COPY_NODE_FIELD(inputcollids); + COPY_NODE_FIELD(largs); + COPY_NODE_FIELD(rargs); + + return newnode; +} + +/* + * _copyCoalesceExpr + */ +static CoalesceExpr * +_copyCoalesceExpr(const CoalesceExpr *from) +{ + CoalesceExpr *newnode = makeNode(CoalesceExpr); + + COPY_SCALAR_FIELD(coalescetype); + COPY_SCALAR_FIELD(coalescecollid); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyMinMaxExpr + */ +static MinMaxExpr * +_copyMinMaxExpr(const MinMaxExpr *from) +{ + MinMaxExpr *newnode = makeNode(MinMaxExpr); + + COPY_SCALAR_FIELD(minmaxtype); + COPY_SCALAR_FIELD(minmaxcollid); + COPY_SCALAR_FIELD(inputcollid); + COPY_SCALAR_FIELD(op); + COPY_NODE_FIELD(args); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copySQLValueFunction + */ +static SQLValueFunction * +_copySQLValueFunction(const SQLValueFunction *from) +{ + SQLValueFunction *newnode = makeNode(SQLValueFunction); + + COPY_SCALAR_FIELD(op); + COPY_SCALAR_FIELD(type); + COPY_SCALAR_FIELD(typmod); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyXmlExpr + */ +static XmlExpr * +_copyXmlExpr(const XmlExpr *from) +{ + XmlExpr *newnode = makeNode(XmlExpr); + + COPY_SCALAR_FIELD(op); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(named_args); + COPY_NODE_FIELD(arg_names); + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(xmloption); + COPY_SCALAR_FIELD(type); + COPY_SCALAR_FIELD(typmod); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyNullTest + */ +static NullTest * +_copyNullTest(const NullTest *from) +{ + NullTest *newnode = makeNode(NullTest); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(nulltesttype); + COPY_SCALAR_FIELD(argisrow); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyBooleanTest + */ +static BooleanTest * +_copyBooleanTest(const BooleanTest *from) +{ + BooleanTest *newnode = makeNode(BooleanTest); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(booltesttype); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCoerceToDomain + */ +static CoerceToDomain * +_copyCoerceToDomain(const CoerceToDomain *from) +{ + CoerceToDomain *newnode = makeNode(CoerceToDomain); + + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(resulttype); + COPY_SCALAR_FIELD(resulttypmod); + COPY_SCALAR_FIELD(resultcollid); + COPY_SCALAR_FIELD(coercionformat); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCoerceToDomainValue + */ +static CoerceToDomainValue * +_copyCoerceToDomainValue(const CoerceToDomainValue *from) +{ + CoerceToDomainValue *newnode = makeNode(CoerceToDomainValue); + + COPY_SCALAR_FIELD(typeId); + COPY_SCALAR_FIELD(typeMod); + COPY_SCALAR_FIELD(collation); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copySetToDefault + */ +static SetToDefault * +_copySetToDefault(const SetToDefault *from) +{ + SetToDefault *newnode = makeNode(SetToDefault); + + COPY_SCALAR_FIELD(typeId); + COPY_SCALAR_FIELD(typeMod); + COPY_SCALAR_FIELD(collation); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +/* + * _copyCurrentOfExpr + */ +static CurrentOfExpr * +_copyCurrentOfExpr(const CurrentOfExpr *from) +{ + CurrentOfExpr *newnode = makeNode(CurrentOfExpr); + + COPY_SCALAR_FIELD(cvarno); + COPY_STRING_FIELD(cursor_name); + COPY_SCALAR_FIELD(cursor_param); + + return newnode; +} + + /* + * _copyNextValueExpr + */ +static NextValueExpr * +_copyNextValueExpr(const NextValueExpr *from) +{ + NextValueExpr *newnode = makeNode(NextValueExpr); + + COPY_SCALAR_FIELD(seqid); + COPY_SCALAR_FIELD(typeId); + + return newnode; +} + +/* + * _copyInferenceElem + */ +static InferenceElem * +_copyInferenceElem(const InferenceElem *from) +{ + InferenceElem *newnode = makeNode(InferenceElem); + + COPY_NODE_FIELD(expr); + COPY_SCALAR_FIELD(infercollid); + COPY_SCALAR_FIELD(inferopclass); + + return newnode; +} + +/* + * _copyTargetEntry + */ +static TargetEntry * +_copyTargetEntry(const TargetEntry *from) +{ + TargetEntry *newnode = makeNode(TargetEntry); + + COPY_NODE_FIELD(expr); + COPY_SCALAR_FIELD(resno); + COPY_STRING_FIELD(resname); + COPY_SCALAR_FIELD(ressortgroupref); + COPY_SCALAR_FIELD(resorigtbl); + COPY_SCALAR_FIELD(resorigcol); + COPY_SCALAR_FIELD(resjunk); + + return newnode; +} + +/* + * _copyRangeTblRef + */ +static RangeTblRef * +_copyRangeTblRef(const RangeTblRef *from) +{ + RangeTblRef *newnode = makeNode(RangeTblRef); + + COPY_SCALAR_FIELD(rtindex); + + return newnode; +} + +/* + * _copyJoinExpr + */ +static JoinExpr * +_copyJoinExpr(const JoinExpr *from) +{ + JoinExpr *newnode = makeNode(JoinExpr); + + COPY_SCALAR_FIELD(jointype); + COPY_SCALAR_FIELD(isNatural); + COPY_NODE_FIELD(larg); + COPY_NODE_FIELD(rarg); + COPY_NODE_FIELD(usingClause); + COPY_NODE_FIELD(join_using_alias); + COPY_NODE_FIELD(quals); + COPY_NODE_FIELD(alias); + COPY_SCALAR_FIELD(rtindex); + + return newnode; +} + +/* + * _copyFromExpr + */ +static FromExpr * +_copyFromExpr(const FromExpr *from) +{ + FromExpr *newnode = makeNode(FromExpr); + + COPY_NODE_FIELD(fromlist); + COPY_NODE_FIELD(quals); + + return newnode; +} + +/* + * _copyOnConflictExpr + */ +static OnConflictExpr * +_copyOnConflictExpr(const OnConflictExpr *from) +{ + OnConflictExpr *newnode = makeNode(OnConflictExpr); + + COPY_SCALAR_FIELD(action); + COPY_NODE_FIELD(arbiterElems); + COPY_NODE_FIELD(arbiterWhere); + COPY_SCALAR_FIELD(constraint); + COPY_NODE_FIELD(onConflictSet); + COPY_NODE_FIELD(onConflictWhere); + COPY_SCALAR_FIELD(exclRelIndex); + COPY_NODE_FIELD(exclRelTlist); + + return newnode; +} + +/* **************************************************************** + * pathnodes.h copy functions + * + * We don't support copying RelOptInfo, IndexOptInfo, or Path nodes. + * There are some subsidiary structs that are useful to copy, though. + * **************************************************************** + */ + +/* + * _copyPathKey + */ +static PathKey * +_copyPathKey(const PathKey *from) +{ + PathKey *newnode = makeNode(PathKey); + + /* EquivalenceClasses are never moved, so just shallow-copy the pointer */ + COPY_SCALAR_FIELD(pk_eclass); + COPY_SCALAR_FIELD(pk_opfamily); + COPY_SCALAR_FIELD(pk_strategy); + COPY_SCALAR_FIELD(pk_nulls_first); + + return newnode; +} + +/* + * _copyRestrictInfo + */ +static RestrictInfo * +_copyRestrictInfo(const RestrictInfo *from) +{ + RestrictInfo *newnode = makeNode(RestrictInfo); + + COPY_NODE_FIELD(clause); + COPY_SCALAR_FIELD(is_pushed_down); + COPY_SCALAR_FIELD(outerjoin_delayed); + COPY_SCALAR_FIELD(can_join); + COPY_SCALAR_FIELD(pseudoconstant); + COPY_SCALAR_FIELD(leakproof); + COPY_SCALAR_FIELD(has_volatile); + COPY_SCALAR_FIELD(security_level); + COPY_BITMAPSET_FIELD(clause_relids); + COPY_BITMAPSET_FIELD(required_relids); + COPY_BITMAPSET_FIELD(outer_relids); + COPY_BITMAPSET_FIELD(nullable_relids); + COPY_BITMAPSET_FIELD(left_relids); + COPY_BITMAPSET_FIELD(right_relids); + COPY_NODE_FIELD(orclause); + /* EquivalenceClasses are never copied, so shallow-copy the pointers */ + COPY_SCALAR_FIELD(parent_ec); + COPY_SCALAR_FIELD(eval_cost); + COPY_SCALAR_FIELD(norm_selec); + COPY_SCALAR_FIELD(outer_selec); + COPY_NODE_FIELD(mergeopfamilies); + /* EquivalenceClasses are never copied, so shallow-copy the pointers */ + COPY_SCALAR_FIELD(left_ec); + COPY_SCALAR_FIELD(right_ec); + COPY_SCALAR_FIELD(left_em); + COPY_SCALAR_FIELD(right_em); + /* MergeScanSelCache isn't a Node, so hard to copy; just reset cache */ + newnode->scansel_cache = NIL; + COPY_SCALAR_FIELD(outer_is_left); + COPY_SCALAR_FIELD(hashjoinoperator); + COPY_SCALAR_FIELD(left_bucketsize); + COPY_SCALAR_FIELD(right_bucketsize); + COPY_SCALAR_FIELD(left_mcvfreq); + COPY_SCALAR_FIELD(right_mcvfreq); + COPY_SCALAR_FIELD(left_hasheqoperator); + COPY_SCALAR_FIELD(right_hasheqoperator); + + return newnode; +} + +/* + * _copyPlaceHolderVar + */ +static PlaceHolderVar * +_copyPlaceHolderVar(const PlaceHolderVar *from) +{ + PlaceHolderVar *newnode = makeNode(PlaceHolderVar); + + COPY_NODE_FIELD(phexpr); + COPY_BITMAPSET_FIELD(phrels); + COPY_SCALAR_FIELD(phid); + COPY_SCALAR_FIELD(phlevelsup); + + return newnode; +} + +/* + * _copySpecialJoinInfo + */ +static SpecialJoinInfo * +_copySpecialJoinInfo(const SpecialJoinInfo *from) +{ + SpecialJoinInfo *newnode = makeNode(SpecialJoinInfo); + + COPY_BITMAPSET_FIELD(min_lefthand); + COPY_BITMAPSET_FIELD(min_righthand); + COPY_BITMAPSET_FIELD(syn_lefthand); + COPY_BITMAPSET_FIELD(syn_righthand); + COPY_SCALAR_FIELD(jointype); + COPY_SCALAR_FIELD(lhs_strict); + COPY_SCALAR_FIELD(delay_upper_joins); + COPY_SCALAR_FIELD(semi_can_btree); + COPY_SCALAR_FIELD(semi_can_hash); + COPY_NODE_FIELD(semi_operators); + COPY_NODE_FIELD(semi_rhs_exprs); + + return newnode; +} + +/* + * _copyAppendRelInfo + */ +static AppendRelInfo * +_copyAppendRelInfo(const AppendRelInfo *from) +{ + AppendRelInfo *newnode = makeNode(AppendRelInfo); + + COPY_SCALAR_FIELD(parent_relid); + COPY_SCALAR_FIELD(child_relid); + COPY_SCALAR_FIELD(parent_reltype); + COPY_SCALAR_FIELD(child_reltype); + COPY_NODE_FIELD(translated_vars); + COPY_SCALAR_FIELD(num_child_cols); + COPY_POINTER_FIELD(parent_colnos, from->num_child_cols * sizeof(AttrNumber)); + COPY_SCALAR_FIELD(parent_reloid); + + return newnode; +} + +/* + * _copyPlaceHolderInfo + */ +static PlaceHolderInfo * +_copyPlaceHolderInfo(const PlaceHolderInfo *from) +{ + PlaceHolderInfo *newnode = makeNode(PlaceHolderInfo); + + COPY_SCALAR_FIELD(phid); + COPY_NODE_FIELD(ph_var); + COPY_BITMAPSET_FIELD(ph_eval_at); + COPY_BITMAPSET_FIELD(ph_lateral); + COPY_BITMAPSET_FIELD(ph_needed); + COPY_SCALAR_FIELD(ph_width); + + return newnode; +} + +/* **************************************************************** + * parsenodes.h copy functions + * **************************************************************** + */ + +static RangeTblEntry * +_copyRangeTblEntry(const RangeTblEntry *from) +{ + RangeTblEntry *newnode = makeNode(RangeTblEntry); + + COPY_SCALAR_FIELD(rtekind); + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(relkind); + COPY_SCALAR_FIELD(rellockmode); + COPY_NODE_FIELD(tablesample); + COPY_NODE_FIELD(subquery); + COPY_SCALAR_FIELD(security_barrier); + COPY_SCALAR_FIELD(jointype); + COPY_SCALAR_FIELD(joinmergedcols); + COPY_NODE_FIELD(joinaliasvars); + COPY_NODE_FIELD(joinleftcols); + COPY_NODE_FIELD(joinrightcols); + COPY_NODE_FIELD(join_using_alias); + COPY_NODE_FIELD(functions); + COPY_SCALAR_FIELD(funcordinality); + COPY_NODE_FIELD(tablefunc); + COPY_NODE_FIELD(values_lists); + COPY_STRING_FIELD(ctename); + COPY_SCALAR_FIELD(ctelevelsup); + COPY_SCALAR_FIELD(self_reference); + COPY_NODE_FIELD(coltypes); + COPY_NODE_FIELD(coltypmods); + COPY_NODE_FIELD(colcollations); + COPY_STRING_FIELD(enrname); + COPY_SCALAR_FIELD(enrtuples); + COPY_NODE_FIELD(alias); + COPY_NODE_FIELD(eref); + COPY_SCALAR_FIELD(lateral); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(inFromCl); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + COPY_NODE_FIELD(securityQuals); + + return newnode; +} + +static RangeTblFunction * +_copyRangeTblFunction(const RangeTblFunction *from) +{ + RangeTblFunction *newnode = makeNode(RangeTblFunction); + + COPY_NODE_FIELD(funcexpr); + COPY_SCALAR_FIELD(funccolcount); + COPY_NODE_FIELD(funccolnames); + COPY_NODE_FIELD(funccoltypes); + COPY_NODE_FIELD(funccoltypmods); + COPY_NODE_FIELD(funccolcollations); + COPY_BITMAPSET_FIELD(funcparams); + + return newnode; +} + +static TableSampleClause * +_copyTableSampleClause(const TableSampleClause *from) +{ + TableSampleClause *newnode = makeNode(TableSampleClause); + + COPY_SCALAR_FIELD(tsmhandler); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(repeatable); + + return newnode; +} + +static WithCheckOption * +_copyWithCheckOption(const WithCheckOption *from) +{ + WithCheckOption *newnode = makeNode(WithCheckOption); + + COPY_SCALAR_FIELD(kind); + COPY_STRING_FIELD(relname); + COPY_STRING_FIELD(polname); + COPY_NODE_FIELD(qual); + COPY_SCALAR_FIELD(cascaded); + + return newnode; +} + +static SortGroupClause * +_copySortGroupClause(const SortGroupClause *from) +{ + SortGroupClause *newnode = makeNode(SortGroupClause); + + COPY_SCALAR_FIELD(tleSortGroupRef); + COPY_SCALAR_FIELD(eqop); + COPY_SCALAR_FIELD(sortop); + COPY_SCALAR_FIELD(nulls_first); + COPY_SCALAR_FIELD(hashable); + + return newnode; +} + +static GroupingSet * +_copyGroupingSet(const GroupingSet *from) +{ + GroupingSet *newnode = makeNode(GroupingSet); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(content); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static WindowClause * +_copyWindowClause(const WindowClause *from) +{ + WindowClause *newnode = makeNode(WindowClause); + + COPY_STRING_FIELD(name); + COPY_STRING_FIELD(refname); + COPY_NODE_FIELD(partitionClause); + COPY_NODE_FIELD(orderClause); + COPY_SCALAR_FIELD(frameOptions); + COPY_NODE_FIELD(startOffset); + COPY_NODE_FIELD(endOffset); + COPY_NODE_FIELD(runCondition); + COPY_SCALAR_FIELD(startInRangeFunc); + COPY_SCALAR_FIELD(endInRangeFunc); + COPY_SCALAR_FIELD(inRangeColl); + COPY_SCALAR_FIELD(inRangeAsc); + COPY_SCALAR_FIELD(inRangeNullsFirst); + COPY_SCALAR_FIELD(winref); + COPY_SCALAR_FIELD(copiedOrder); + + return newnode; +} + +static RowMarkClause * +_copyRowMarkClause(const RowMarkClause *from) +{ + RowMarkClause *newnode = makeNode(RowMarkClause); + + COPY_SCALAR_FIELD(rti); + COPY_SCALAR_FIELD(strength); + COPY_SCALAR_FIELD(waitPolicy); + COPY_SCALAR_FIELD(pushedDown); + + return newnode; +} + +static WithClause * +_copyWithClause(const WithClause *from) +{ + WithClause *newnode = makeNode(WithClause); + + COPY_NODE_FIELD(ctes); + COPY_SCALAR_FIELD(recursive); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static InferClause * +_copyInferClause(const InferClause *from) +{ + InferClause *newnode = makeNode(InferClause); + + COPY_NODE_FIELD(indexElems); + COPY_NODE_FIELD(whereClause); + COPY_STRING_FIELD(conname); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static OnConflictClause * +_copyOnConflictClause(const OnConflictClause *from) +{ + OnConflictClause *newnode = makeNode(OnConflictClause); + + COPY_SCALAR_FIELD(action); + COPY_NODE_FIELD(infer); + COPY_NODE_FIELD(targetList); + COPY_NODE_FIELD(whereClause); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static CTESearchClause * +_copyCTESearchClause(const CTESearchClause *from) +{ + CTESearchClause *newnode = makeNode(CTESearchClause); + + COPY_NODE_FIELD(search_col_list); + COPY_SCALAR_FIELD(search_breadth_first); + COPY_STRING_FIELD(search_seq_column); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static CTECycleClause * +_copyCTECycleClause(const CTECycleClause *from) +{ + CTECycleClause *newnode = makeNode(CTECycleClause); + + COPY_NODE_FIELD(cycle_col_list); + COPY_STRING_FIELD(cycle_mark_column); + COPY_NODE_FIELD(cycle_mark_value); + COPY_NODE_FIELD(cycle_mark_default); + COPY_STRING_FIELD(cycle_path_column); + COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(cycle_mark_type); + COPY_SCALAR_FIELD(cycle_mark_typmod); + COPY_SCALAR_FIELD(cycle_mark_collation); + COPY_SCALAR_FIELD(cycle_mark_neop); + + return newnode; +} + +static CommonTableExpr * +_copyCommonTableExpr(const CommonTableExpr *from) +{ + CommonTableExpr *newnode = makeNode(CommonTableExpr); + + COPY_STRING_FIELD(ctename); + COPY_NODE_FIELD(aliascolnames); + COPY_SCALAR_FIELD(ctematerialized); + COPY_NODE_FIELD(ctequery); + COPY_NODE_FIELD(search_clause); + COPY_NODE_FIELD(cycle_clause); + COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(cterecursive); + COPY_SCALAR_FIELD(cterefcount); + COPY_NODE_FIELD(ctecolnames); + COPY_NODE_FIELD(ctecoltypes); + COPY_NODE_FIELD(ctecoltypmods); + COPY_NODE_FIELD(ctecolcollations); + + return newnode; +} + +static MergeWhenClause * +_copyMergeWhenClause(const MergeWhenClause *from) +{ + MergeWhenClause *newnode = makeNode(MergeWhenClause); + + COPY_SCALAR_FIELD(matched); + COPY_SCALAR_FIELD(commandType); + COPY_SCALAR_FIELD(override); + COPY_NODE_FIELD(condition); + COPY_NODE_FIELD(targetList); + COPY_NODE_FIELD(values); + return newnode; +} + +static MergeAction * +_copyMergeAction(const MergeAction *from) +{ + MergeAction *newnode = makeNode(MergeAction); + + COPY_SCALAR_FIELD(matched); + COPY_SCALAR_FIELD(commandType); + COPY_SCALAR_FIELD(override); + COPY_NODE_FIELD(qual); + COPY_NODE_FIELD(targetList); + COPY_NODE_FIELD(updateColnos); + + return newnode; +} + +static A_Expr * +_copyA_Expr(const A_Expr *from) +{ + A_Expr *newnode = makeNode(A_Expr); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(name); + COPY_NODE_FIELD(lexpr); + COPY_NODE_FIELD(rexpr); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static ColumnRef * +_copyColumnRef(const ColumnRef *from) +{ + ColumnRef *newnode = makeNode(ColumnRef); + + COPY_NODE_FIELD(fields); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static ParamRef * +_copyParamRef(const ParamRef *from) +{ + ParamRef *newnode = makeNode(ParamRef); + + COPY_SCALAR_FIELD(number); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static A_Const * +_copyA_Const(const A_Const *from) +{ + A_Const *newnode = makeNode(A_Const); + + COPY_SCALAR_FIELD(isnull); + if (!from->isnull) + { + /* This part must duplicate other _copy*() functions. */ + COPY_SCALAR_FIELD(val.node.type); + switch (nodeTag(&from->val)) + { + case T_Integer: + COPY_SCALAR_FIELD(val.ival.ival); + break; + case T_Float: + COPY_STRING_FIELD(val.fval.fval); + break; + case T_Boolean: + COPY_SCALAR_FIELD(val.boolval.boolval); + break; + case T_String: + COPY_STRING_FIELD(val.sval.sval); + break; + case T_BitString: + COPY_STRING_FIELD(val.bsval.bsval); + break; + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(&from->val)); + break; + } + } + + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static FuncCall * +_copyFuncCall(const FuncCall *from) +{ + FuncCall *newnode = makeNode(FuncCall); + + COPY_NODE_FIELD(funcname); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(agg_order); + COPY_NODE_FIELD(agg_filter); + COPY_NODE_FIELD(over); + COPY_SCALAR_FIELD(agg_within_group); + COPY_SCALAR_FIELD(agg_star); + COPY_SCALAR_FIELD(agg_distinct); + COPY_SCALAR_FIELD(func_variadic); + COPY_SCALAR_FIELD(funcformat); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static A_Star * +_copyA_Star(const A_Star *from) +{ + A_Star *newnode = makeNode(A_Star); + + return newnode; +} + +static A_Indices * +_copyA_Indices(const A_Indices *from) +{ + A_Indices *newnode = makeNode(A_Indices); + + COPY_SCALAR_FIELD(is_slice); + COPY_NODE_FIELD(lidx); + COPY_NODE_FIELD(uidx); + + return newnode; +} + +static A_Indirection * +_copyA_Indirection(const A_Indirection *from) +{ + A_Indirection *newnode = makeNode(A_Indirection); + + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(indirection); + + return newnode; +} + +static A_ArrayExpr * +_copyA_ArrayExpr(const A_ArrayExpr *from) +{ + A_ArrayExpr *newnode = makeNode(A_ArrayExpr); + + COPY_NODE_FIELD(elements); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static ResTarget * +_copyResTarget(const ResTarget *from) +{ + ResTarget *newnode = makeNode(ResTarget); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(indirection); + COPY_NODE_FIELD(val); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static MultiAssignRef * +_copyMultiAssignRef(const MultiAssignRef *from) +{ + MultiAssignRef *newnode = makeNode(MultiAssignRef); + + COPY_NODE_FIELD(source); + COPY_SCALAR_FIELD(colno); + COPY_SCALAR_FIELD(ncolumns); + + return newnode; +} + +static TypeName * +_copyTypeName(const TypeName *from) +{ + TypeName *newnode = makeNode(TypeName); + + COPY_NODE_FIELD(names); + COPY_SCALAR_FIELD(typeOid); + COPY_SCALAR_FIELD(setof); + COPY_SCALAR_FIELD(pct_type); + COPY_NODE_FIELD(typmods); + COPY_SCALAR_FIELD(typemod); + COPY_NODE_FIELD(arrayBounds); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static SortBy * +_copySortBy(const SortBy *from) +{ + SortBy *newnode = makeNode(SortBy); + + COPY_NODE_FIELD(node); + COPY_SCALAR_FIELD(sortby_dir); + COPY_SCALAR_FIELD(sortby_nulls); + COPY_NODE_FIELD(useOp); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static WindowDef * +_copyWindowDef(const WindowDef *from) +{ + WindowDef *newnode = makeNode(WindowDef); + + COPY_STRING_FIELD(name); + COPY_STRING_FIELD(refname); + COPY_NODE_FIELD(partitionClause); + COPY_NODE_FIELD(orderClause); + COPY_SCALAR_FIELD(frameOptions); + COPY_NODE_FIELD(startOffset); + COPY_NODE_FIELD(endOffset); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static RangeSubselect * +_copyRangeSubselect(const RangeSubselect *from) +{ + RangeSubselect *newnode = makeNode(RangeSubselect); + + COPY_SCALAR_FIELD(lateral); + COPY_NODE_FIELD(subquery); + COPY_NODE_FIELD(alias); + + return newnode; +} + +static RangeFunction * +_copyRangeFunction(const RangeFunction *from) +{ + RangeFunction *newnode = makeNode(RangeFunction); + + COPY_SCALAR_FIELD(lateral); + COPY_SCALAR_FIELD(ordinality); + COPY_SCALAR_FIELD(is_rowsfrom); + COPY_NODE_FIELD(functions); + COPY_NODE_FIELD(alias); + COPY_NODE_FIELD(coldeflist); + + return newnode; +} + +static RangeTableSample * +_copyRangeTableSample(const RangeTableSample *from) +{ + RangeTableSample *newnode = makeNode(RangeTableSample); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(method); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(repeatable); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static RangeTableFunc * +_copyRangeTableFunc(const RangeTableFunc *from) +{ + RangeTableFunc *newnode = makeNode(RangeTableFunc); + + COPY_SCALAR_FIELD(lateral); + COPY_NODE_FIELD(docexpr); + COPY_NODE_FIELD(rowexpr); + COPY_NODE_FIELD(namespaces); + COPY_NODE_FIELD(columns); + COPY_NODE_FIELD(alias); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static RangeTableFuncCol * +_copyRangeTableFuncCol(const RangeTableFuncCol *from) +{ + RangeTableFuncCol *newnode = makeNode(RangeTableFuncCol); + + COPY_STRING_FIELD(colname); + COPY_NODE_FIELD(typeName); + COPY_SCALAR_FIELD(for_ordinality); + COPY_SCALAR_FIELD(is_not_null); + COPY_NODE_FIELD(colexpr); + COPY_NODE_FIELD(coldefexpr); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static TypeCast * +_copyTypeCast(const TypeCast *from) +{ + TypeCast *newnode = makeNode(TypeCast); + + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(typeName); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static CollateClause * +_copyCollateClause(const CollateClause *from) +{ + CollateClause *newnode = makeNode(CollateClause); + + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(collname); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static IndexElem * +_copyIndexElem(const IndexElem *from) +{ + IndexElem *newnode = makeNode(IndexElem); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(expr); + COPY_STRING_FIELD(indexcolname); + COPY_NODE_FIELD(collation); + COPY_NODE_FIELD(opclass); + COPY_NODE_FIELD(opclassopts); + COPY_SCALAR_FIELD(ordering); + COPY_SCALAR_FIELD(nulls_ordering); + + return newnode; +} + +static StatsElem * +_copyStatsElem(const StatsElem *from) +{ + StatsElem *newnode = makeNode(StatsElem); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(expr); + + return newnode; +} + +static ColumnDef * +_copyColumnDef(const ColumnDef *from) +{ + ColumnDef *newnode = makeNode(ColumnDef); + + COPY_STRING_FIELD(colname); + COPY_NODE_FIELD(typeName); + COPY_STRING_FIELD(compression); + COPY_SCALAR_FIELD(inhcount); + COPY_SCALAR_FIELD(is_local); + COPY_SCALAR_FIELD(is_not_null); + COPY_SCALAR_FIELD(is_from_type); + COPY_SCALAR_FIELD(storage); + COPY_NODE_FIELD(raw_default); + COPY_NODE_FIELD(cooked_default); + COPY_SCALAR_FIELD(identity); + COPY_NODE_FIELD(identitySequence); + COPY_SCALAR_FIELD(generated); + COPY_NODE_FIELD(collClause); + COPY_SCALAR_FIELD(collOid); + COPY_NODE_FIELD(constraints); + COPY_NODE_FIELD(fdwoptions); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static Constraint * +_copyConstraint(const Constraint *from) +{ + Constraint *newnode = makeNode(Constraint); + + COPY_SCALAR_FIELD(contype); + COPY_STRING_FIELD(conname); + COPY_SCALAR_FIELD(deferrable); + COPY_SCALAR_FIELD(initdeferred); + COPY_LOCATION_FIELD(location); + COPY_SCALAR_FIELD(is_no_inherit); + COPY_NODE_FIELD(raw_expr); + COPY_STRING_FIELD(cooked_expr); + COPY_SCALAR_FIELD(generated_when); + COPY_SCALAR_FIELD(nulls_not_distinct); + COPY_NODE_FIELD(keys); + COPY_NODE_FIELD(including); + COPY_NODE_FIELD(exclusions); + COPY_NODE_FIELD(options); + COPY_STRING_FIELD(indexname); + COPY_STRING_FIELD(indexspace); + COPY_SCALAR_FIELD(reset_default_tblspc); + COPY_STRING_FIELD(access_method); + COPY_NODE_FIELD(where_clause); + COPY_NODE_FIELD(pktable); + COPY_NODE_FIELD(fk_attrs); + COPY_NODE_FIELD(pk_attrs); + COPY_SCALAR_FIELD(fk_matchtype); + COPY_SCALAR_FIELD(fk_upd_action); + COPY_SCALAR_FIELD(fk_del_action); + COPY_NODE_FIELD(fk_del_set_cols); + COPY_NODE_FIELD(old_conpfeqop); + COPY_SCALAR_FIELD(old_pktable_oid); + COPY_SCALAR_FIELD(skip_validation); + COPY_SCALAR_FIELD(initially_valid); + + return newnode; +} + +static DefElem * +_copyDefElem(const DefElem *from) +{ + DefElem *newnode = makeNode(DefElem); + + COPY_STRING_FIELD(defnamespace); + COPY_STRING_FIELD(defname); + COPY_NODE_FIELD(arg); + COPY_SCALAR_FIELD(defaction); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static LockingClause * +_copyLockingClause(const LockingClause *from) +{ + LockingClause *newnode = makeNode(LockingClause); + + COPY_NODE_FIELD(lockedRels); + COPY_SCALAR_FIELD(strength); + COPY_SCALAR_FIELD(waitPolicy); + + return newnode; +} + +static XmlSerialize * +_copyXmlSerialize(const XmlSerialize *from) +{ + XmlSerialize *newnode = makeNode(XmlSerialize); + + COPY_SCALAR_FIELD(xmloption); + COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(typeName); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static RoleSpec * +_copyRoleSpec(const RoleSpec *from) +{ + RoleSpec *newnode = makeNode(RoleSpec); + + COPY_SCALAR_FIELD(roletype); + COPY_STRING_FIELD(rolename); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static TriggerTransition * +_copyTriggerTransition(const TriggerTransition *from) +{ + TriggerTransition *newnode = makeNode(TriggerTransition); + + COPY_STRING_FIELD(name); + COPY_SCALAR_FIELD(isNew); + COPY_SCALAR_FIELD(isTable); + + return newnode; +} + +static Query * +_copyQuery(const Query *from) +{ + Query *newnode = makeNode(Query); + + COPY_SCALAR_FIELD(commandType); + COPY_SCALAR_FIELD(querySource); + COPY_SCALAR_FIELD(queryId); + COPY_SCALAR_FIELD(canSetTag); + COPY_NODE_FIELD(utilityStmt); + COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(hasAggs); + COPY_SCALAR_FIELD(hasWindowFuncs); + COPY_SCALAR_FIELD(hasTargetSRFs); + COPY_SCALAR_FIELD(hasSubLinks); + COPY_SCALAR_FIELD(hasDistinctOn); + COPY_SCALAR_FIELD(hasRecursive); + COPY_SCALAR_FIELD(hasModifyingCTE); + COPY_SCALAR_FIELD(hasForUpdate); + COPY_SCALAR_FIELD(hasRowSecurity); + COPY_SCALAR_FIELD(isReturn); + COPY_NODE_FIELD(cteList); + COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(jointree); + COPY_NODE_FIELD(targetList); + COPY_SCALAR_FIELD(override); + COPY_NODE_FIELD(onConflict); + COPY_NODE_FIELD(returningList); + COPY_NODE_FIELD(groupClause); + COPY_SCALAR_FIELD(groupDistinct); + COPY_NODE_FIELD(groupingSets); + COPY_NODE_FIELD(havingQual); + COPY_NODE_FIELD(windowClause); + COPY_NODE_FIELD(distinctClause); + COPY_NODE_FIELD(sortClause); + COPY_NODE_FIELD(limitOffset); + COPY_NODE_FIELD(limitCount); + COPY_SCALAR_FIELD(limitOption); + COPY_NODE_FIELD(rowMarks); + COPY_NODE_FIELD(setOperations); + COPY_NODE_FIELD(constraintDeps); + COPY_NODE_FIELD(withCheckOptions); + COPY_NODE_FIELD(mergeActionList); + COPY_SCALAR_FIELD(mergeUseOuterJoin); + COPY_LOCATION_FIELD(stmt_location); + COPY_SCALAR_FIELD(stmt_len); + + return newnode; +} + +static RawStmt * +_copyRawStmt(const RawStmt *from) +{ + RawStmt *newnode = makeNode(RawStmt); + + COPY_NODE_FIELD(stmt); + COPY_LOCATION_FIELD(stmt_location); + COPY_SCALAR_FIELD(stmt_len); + + return newnode; +} + +static InsertStmt * +_copyInsertStmt(const InsertStmt *from) +{ + InsertStmt *newnode = makeNode(InsertStmt); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(cols); + COPY_NODE_FIELD(selectStmt); + COPY_NODE_FIELD(onConflictClause); + COPY_NODE_FIELD(returningList); + COPY_NODE_FIELD(withClause); + COPY_SCALAR_FIELD(override); + + return newnode; +} + +static DeleteStmt * +_copyDeleteStmt(const DeleteStmt *from) +{ + DeleteStmt *newnode = makeNode(DeleteStmt); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(usingClause); + COPY_NODE_FIELD(whereClause); + COPY_NODE_FIELD(returningList); + COPY_NODE_FIELD(withClause); + + return newnode; +} + +static UpdateStmt * +_copyUpdateStmt(const UpdateStmt *from) +{ + UpdateStmt *newnode = makeNode(UpdateStmt); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(targetList); + COPY_NODE_FIELD(whereClause); + COPY_NODE_FIELD(fromClause); + COPY_NODE_FIELD(returningList); + COPY_NODE_FIELD(withClause); + + return newnode; +} + +static MergeStmt * +_copyMergeStmt(const MergeStmt *from) +{ + MergeStmt *newnode = makeNode(MergeStmt); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(sourceRelation); + COPY_NODE_FIELD(joinCondition); + COPY_NODE_FIELD(mergeWhenClauses); + COPY_NODE_FIELD(withClause); + + return newnode; +} + +static SelectStmt * +_copySelectStmt(const SelectStmt *from) +{ + SelectStmt *newnode = makeNode(SelectStmt); + + COPY_NODE_FIELD(distinctClause); + COPY_NODE_FIELD(intoClause); + COPY_NODE_FIELD(targetList); + COPY_NODE_FIELD(fromClause); + COPY_NODE_FIELD(whereClause); + COPY_NODE_FIELD(groupClause); + COPY_SCALAR_FIELD(groupDistinct); + COPY_NODE_FIELD(havingClause); + COPY_NODE_FIELD(windowClause); + COPY_NODE_FIELD(valuesLists); + COPY_NODE_FIELD(sortClause); + COPY_NODE_FIELD(limitOffset); + COPY_NODE_FIELD(limitCount); + COPY_SCALAR_FIELD(limitOption); + COPY_NODE_FIELD(lockingClause); + COPY_NODE_FIELD(withClause); + COPY_SCALAR_FIELD(op); + COPY_SCALAR_FIELD(all); + COPY_NODE_FIELD(larg); + COPY_NODE_FIELD(rarg); + + return newnode; +} + +static SetOperationStmt * +_copySetOperationStmt(const SetOperationStmt *from) +{ + SetOperationStmt *newnode = makeNode(SetOperationStmt); + + COPY_SCALAR_FIELD(op); + COPY_SCALAR_FIELD(all); + COPY_NODE_FIELD(larg); + COPY_NODE_FIELD(rarg); + COPY_NODE_FIELD(colTypes); + COPY_NODE_FIELD(colTypmods); + COPY_NODE_FIELD(colCollations); + COPY_NODE_FIELD(groupClauses); + + return newnode; +} + +static ReturnStmt * +_copyReturnStmt(const ReturnStmt *from) +{ + ReturnStmt *newnode = makeNode(ReturnStmt); + + COPY_NODE_FIELD(returnval); + + return newnode; +} + +static PLAssignStmt * +_copyPLAssignStmt(const PLAssignStmt *from) +{ + PLAssignStmt *newnode = makeNode(PLAssignStmt); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(indirection); + COPY_SCALAR_FIELD(nnames); + COPY_NODE_FIELD(val); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static AlterTableStmt * +_copyAlterTableStmt(const AlterTableStmt *from) +{ + AlterTableStmt *newnode = makeNode(AlterTableStmt); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(cmds); + COPY_SCALAR_FIELD(objtype); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static AlterTableCmd * +_copyAlterTableCmd(const AlterTableCmd *from) +{ + AlterTableCmd *newnode = makeNode(AlterTableCmd); + + COPY_SCALAR_FIELD(subtype); + COPY_STRING_FIELD(name); + COPY_SCALAR_FIELD(num); + COPY_NODE_FIELD(newowner); + COPY_NODE_FIELD(def); + COPY_SCALAR_FIELD(behavior); + COPY_SCALAR_FIELD(missing_ok); + COPY_SCALAR_FIELD(recurse); + + return newnode; +} + +static AlterCollationStmt * +_copyAlterCollationStmt(const AlterCollationStmt *from) +{ + AlterCollationStmt *newnode = makeNode(AlterCollationStmt); + + COPY_NODE_FIELD(collname); + + return newnode; +} + +static AlterDomainStmt * +_copyAlterDomainStmt(const AlterDomainStmt *from) +{ + AlterDomainStmt *newnode = makeNode(AlterDomainStmt); + + COPY_SCALAR_FIELD(subtype); + COPY_NODE_FIELD(typeName); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(def); + COPY_SCALAR_FIELD(behavior); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static GrantStmt * +_copyGrantStmt(const GrantStmt *from) +{ + GrantStmt *newnode = makeNode(GrantStmt); + + COPY_SCALAR_FIELD(is_grant); + COPY_SCALAR_FIELD(targtype); + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(objects); + COPY_NODE_FIELD(privileges); + COPY_NODE_FIELD(grantees); + COPY_SCALAR_FIELD(grant_option); + COPY_NODE_FIELD(grantor); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +static ObjectWithArgs * +_copyObjectWithArgs(const ObjectWithArgs *from) +{ + ObjectWithArgs *newnode = makeNode(ObjectWithArgs); + + COPY_NODE_FIELD(objname); + COPY_NODE_FIELD(objargs); + COPY_NODE_FIELD(objfuncargs); + COPY_SCALAR_FIELD(args_unspecified); + + return newnode; +} + +static AccessPriv * +_copyAccessPriv(const AccessPriv *from) +{ + AccessPriv *newnode = makeNode(AccessPriv); + + COPY_STRING_FIELD(priv_name); + COPY_NODE_FIELD(cols); + + return newnode; +} + +static GrantRoleStmt * +_copyGrantRoleStmt(const GrantRoleStmt *from) +{ + GrantRoleStmt *newnode = makeNode(GrantRoleStmt); + + COPY_NODE_FIELD(granted_roles); + COPY_NODE_FIELD(grantee_roles); + COPY_SCALAR_FIELD(is_grant); + COPY_SCALAR_FIELD(admin_opt); + COPY_NODE_FIELD(grantor); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +static AlterDefaultPrivilegesStmt * +_copyAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *from) +{ + AlterDefaultPrivilegesStmt *newnode = makeNode(AlterDefaultPrivilegesStmt); + + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(action); + + return newnode; +} + +static DeclareCursorStmt * +_copyDeclareCursorStmt(const DeclareCursorStmt *from) +{ + DeclareCursorStmt *newnode = makeNode(DeclareCursorStmt); + + COPY_STRING_FIELD(portalname); + COPY_SCALAR_FIELD(options); + COPY_NODE_FIELD(query); + + return newnode; +} + +static ClosePortalStmt * +_copyClosePortalStmt(const ClosePortalStmt *from) +{ + ClosePortalStmt *newnode = makeNode(ClosePortalStmt); + + COPY_STRING_FIELD(portalname); + + return newnode; +} + +static CallStmt * +_copyCallStmt(const CallStmt *from) +{ + CallStmt *newnode = makeNode(CallStmt); + + COPY_NODE_FIELD(funccall); + COPY_NODE_FIELD(funcexpr); + COPY_NODE_FIELD(outargs); + + return newnode; +} + +static ClusterStmt * +_copyClusterStmt(const ClusterStmt *from) +{ + ClusterStmt *newnode = makeNode(ClusterStmt); + + COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(indexname); + COPY_NODE_FIELD(params); + + return newnode; +} + +static CopyStmt * +_copyCopyStmt(const CopyStmt *from) +{ + CopyStmt *newnode = makeNode(CopyStmt); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(query); + COPY_NODE_FIELD(attlist); + COPY_SCALAR_FIELD(is_from); + COPY_SCALAR_FIELD(is_program); + COPY_STRING_FIELD(filename); + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(whereClause); + + return newnode; +} + +/* + * CopyCreateStmtFields + * + * This function copies the fields of the CreateStmt node. It is used by + * copy functions for classes which inherit from CreateStmt. + */ +static void +CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode) +{ + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(tableElts); + COPY_NODE_FIELD(inhRelations); + COPY_NODE_FIELD(partspec); + COPY_NODE_FIELD(partbound); + COPY_NODE_FIELD(ofTypename); + COPY_NODE_FIELD(constraints); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(oncommit); + COPY_STRING_FIELD(tablespacename); + COPY_STRING_FIELD(accessMethod); + COPY_SCALAR_FIELD(if_not_exists); +} + +static CreateStmt * +_copyCreateStmt(const CreateStmt *from) +{ + CreateStmt *newnode = makeNode(CreateStmt); + + CopyCreateStmtFields(from, newnode); + + return newnode; +} + +static TableLikeClause * +_copyTableLikeClause(const TableLikeClause *from) +{ + TableLikeClause *newnode = makeNode(TableLikeClause); + + COPY_NODE_FIELD(relation); + COPY_SCALAR_FIELD(options); + COPY_SCALAR_FIELD(relationOid); + + return newnode; +} + +static DefineStmt * +_copyDefineStmt(const DefineStmt *from) +{ + DefineStmt *newnode = makeNode(DefineStmt); + + COPY_SCALAR_FIELD(kind); + COPY_SCALAR_FIELD(oldstyle); + COPY_NODE_FIELD(defnames); + COPY_NODE_FIELD(args); + COPY_NODE_FIELD(definition); + COPY_SCALAR_FIELD(if_not_exists); + COPY_SCALAR_FIELD(replace); + + return newnode; +} + +static DropStmt * +_copyDropStmt(const DropStmt *from) +{ + DropStmt *newnode = makeNode(DropStmt); + + COPY_NODE_FIELD(objects); + COPY_SCALAR_FIELD(removeType); + COPY_SCALAR_FIELD(behavior); + COPY_SCALAR_FIELD(missing_ok); + COPY_SCALAR_FIELD(concurrent); + + return newnode; +} + +static TruncateStmt * +_copyTruncateStmt(const TruncateStmt *from) +{ + TruncateStmt *newnode = makeNode(TruncateStmt); + + COPY_NODE_FIELD(relations); + COPY_SCALAR_FIELD(restart_seqs); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +static CommentStmt * +_copyCommentStmt(const CommentStmt *from) +{ + CommentStmt *newnode = makeNode(CommentStmt); + + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(object); + COPY_STRING_FIELD(comment); + + return newnode; +} + +static SecLabelStmt * +_copySecLabelStmt(const SecLabelStmt *from) +{ + SecLabelStmt *newnode = makeNode(SecLabelStmt); + + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(object); + COPY_STRING_FIELD(provider); + COPY_STRING_FIELD(label); + + return newnode; +} + +static FetchStmt * +_copyFetchStmt(const FetchStmt *from) +{ + FetchStmt *newnode = makeNode(FetchStmt); + + COPY_SCALAR_FIELD(direction); + COPY_SCALAR_FIELD(howMany); + COPY_STRING_FIELD(portalname); + COPY_SCALAR_FIELD(ismove); + + return newnode; +} + +static IndexStmt * +_copyIndexStmt(const IndexStmt *from) +{ + IndexStmt *newnode = makeNode(IndexStmt); + + COPY_STRING_FIELD(idxname); + COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(accessMethod); + COPY_STRING_FIELD(tableSpace); + COPY_NODE_FIELD(indexParams); + COPY_NODE_FIELD(indexIncludingParams); + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(whereClause); + COPY_NODE_FIELD(excludeOpNames); + COPY_STRING_FIELD(idxcomment); + COPY_SCALAR_FIELD(indexOid); + COPY_SCALAR_FIELD(oldNode); + COPY_SCALAR_FIELD(oldCreateSubid); + COPY_SCALAR_FIELD(oldFirstRelfilenodeSubid); + COPY_SCALAR_FIELD(unique); + COPY_SCALAR_FIELD(nulls_not_distinct); + COPY_SCALAR_FIELD(primary); + COPY_SCALAR_FIELD(isconstraint); + COPY_SCALAR_FIELD(deferrable); + COPY_SCALAR_FIELD(initdeferred); + COPY_SCALAR_FIELD(transformed); + COPY_SCALAR_FIELD(concurrent); + COPY_SCALAR_FIELD(if_not_exists); + COPY_SCALAR_FIELD(reset_default_tblspc); + + return newnode; +} + +static CreateStatsStmt * +_copyCreateStatsStmt(const CreateStatsStmt *from) +{ + CreateStatsStmt *newnode = makeNode(CreateStatsStmt); + + COPY_NODE_FIELD(defnames); + COPY_NODE_FIELD(stat_types); + COPY_NODE_FIELD(exprs); + COPY_NODE_FIELD(relations); + COPY_STRING_FIELD(stxcomment); + COPY_SCALAR_FIELD(transformed); + COPY_SCALAR_FIELD(if_not_exists); + + return newnode; +} + +static AlterStatsStmt * +_copyAlterStatsStmt(const AlterStatsStmt *from) +{ + AlterStatsStmt *newnode = makeNode(AlterStatsStmt); + + COPY_NODE_FIELD(defnames); + COPY_SCALAR_FIELD(stxstattarget); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static CreateFunctionStmt * +_copyCreateFunctionStmt(const CreateFunctionStmt *from) +{ + CreateFunctionStmt *newnode = makeNode(CreateFunctionStmt); + + COPY_SCALAR_FIELD(is_procedure); + COPY_SCALAR_FIELD(replace); + COPY_NODE_FIELD(funcname); + COPY_NODE_FIELD(parameters); + COPY_NODE_FIELD(returnType); + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(sql_body); + + return newnode; +} + +static FunctionParameter * +_copyFunctionParameter(const FunctionParameter *from) +{ + FunctionParameter *newnode = makeNode(FunctionParameter); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(argType); + COPY_SCALAR_FIELD(mode); + COPY_NODE_FIELD(defexpr); + + return newnode; +} + +static AlterFunctionStmt * +_copyAlterFunctionStmt(const AlterFunctionStmt *from) +{ + AlterFunctionStmt *newnode = makeNode(AlterFunctionStmt); + + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(func); + COPY_NODE_FIELD(actions); + + return newnode; +} + +static DoStmt * +_copyDoStmt(const DoStmt *from) +{ + DoStmt *newnode = makeNode(DoStmt); + + COPY_NODE_FIELD(args); + + return newnode; +} + +static RenameStmt * +_copyRenameStmt(const RenameStmt *from) +{ + RenameStmt *newnode = makeNode(RenameStmt); + + COPY_SCALAR_FIELD(renameType); + COPY_SCALAR_FIELD(relationType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_STRING_FIELD(subname); + COPY_STRING_FIELD(newname); + COPY_SCALAR_FIELD(behavior); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static AlterObjectDependsStmt * +_copyAlterObjectDependsStmt(const AlterObjectDependsStmt *from) +{ + AlterObjectDependsStmt *newnode = makeNode(AlterObjectDependsStmt); + + COPY_SCALAR_FIELD(objectType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_NODE_FIELD(extname); + COPY_SCALAR_FIELD(remove); + + return newnode; +} + +static AlterObjectSchemaStmt * +_copyAlterObjectSchemaStmt(const AlterObjectSchemaStmt *from) +{ + AlterObjectSchemaStmt *newnode = makeNode(AlterObjectSchemaStmt); + + COPY_SCALAR_FIELD(objectType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_STRING_FIELD(newschema); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static AlterOwnerStmt * +_copyAlterOwnerStmt(const AlterOwnerStmt *from) +{ + AlterOwnerStmt *newnode = makeNode(AlterOwnerStmt); + + COPY_SCALAR_FIELD(objectType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_NODE_FIELD(newowner); + + return newnode; +} + +static AlterOperatorStmt * +_copyAlterOperatorStmt(const AlterOperatorStmt *from) +{ + AlterOperatorStmt *newnode = makeNode(AlterOperatorStmt); + + COPY_NODE_FIELD(opername); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterTypeStmt * +_copyAlterTypeStmt(const AlterTypeStmt *from) +{ + AlterTypeStmt *newnode = makeNode(AlterTypeStmt); + + COPY_NODE_FIELD(typeName); + COPY_NODE_FIELD(options); + + return newnode; +} + +static RuleStmt * +_copyRuleStmt(const RuleStmt *from) +{ + RuleStmt *newnode = makeNode(RuleStmt); + + COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(rulename); + COPY_NODE_FIELD(whereClause); + COPY_SCALAR_FIELD(event); + COPY_SCALAR_FIELD(instead); + COPY_NODE_FIELD(actions); + COPY_SCALAR_FIELD(replace); + + return newnode; +} + +static NotifyStmt * +_copyNotifyStmt(const NotifyStmt *from) +{ + NotifyStmt *newnode = makeNode(NotifyStmt); + + COPY_STRING_FIELD(conditionname); + COPY_STRING_FIELD(payload); + + return newnode; +} + +static ListenStmt * +_copyListenStmt(const ListenStmt *from) +{ + ListenStmt *newnode = makeNode(ListenStmt); + + COPY_STRING_FIELD(conditionname); + + return newnode; +} + +static UnlistenStmt * +_copyUnlistenStmt(const UnlistenStmt *from) +{ + UnlistenStmt *newnode = makeNode(UnlistenStmt); + + COPY_STRING_FIELD(conditionname); + + return newnode; +} + +static TransactionStmt * +_copyTransactionStmt(const TransactionStmt *from) +{ + TransactionStmt *newnode = makeNode(TransactionStmt); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(options); + COPY_STRING_FIELD(savepoint_name); + COPY_STRING_FIELD(gid); + COPY_SCALAR_FIELD(chain); + + return newnode; +} + +static CompositeTypeStmt * +_copyCompositeTypeStmt(const CompositeTypeStmt *from) +{ + CompositeTypeStmt *newnode = makeNode(CompositeTypeStmt); + + COPY_NODE_FIELD(typevar); + COPY_NODE_FIELD(coldeflist); + + return newnode; +} + +static CreateEnumStmt * +_copyCreateEnumStmt(const CreateEnumStmt *from) +{ + CreateEnumStmt *newnode = makeNode(CreateEnumStmt); + + COPY_NODE_FIELD(typeName); + COPY_NODE_FIELD(vals); + + return newnode; +} + +static CreateRangeStmt * +_copyCreateRangeStmt(const CreateRangeStmt *from) +{ + CreateRangeStmt *newnode = makeNode(CreateRangeStmt); + + COPY_NODE_FIELD(typeName); + COPY_NODE_FIELD(params); + + return newnode; +} + +static AlterEnumStmt * +_copyAlterEnumStmt(const AlterEnumStmt *from) +{ + AlterEnumStmt *newnode = makeNode(AlterEnumStmt); + + COPY_NODE_FIELD(typeName); + COPY_STRING_FIELD(oldVal); + COPY_STRING_FIELD(newVal); + COPY_STRING_FIELD(newValNeighbor); + COPY_SCALAR_FIELD(newValIsAfter); + COPY_SCALAR_FIELD(skipIfNewValExists); + + return newnode; +} + +static ViewStmt * +_copyViewStmt(const ViewStmt *from) +{ + ViewStmt *newnode = makeNode(ViewStmt); + + COPY_NODE_FIELD(view); + COPY_NODE_FIELD(aliases); + COPY_NODE_FIELD(query); + COPY_SCALAR_FIELD(replace); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(withCheckOption); + + return newnode; +} + +static LoadStmt * +_copyLoadStmt(const LoadStmt *from) +{ + LoadStmt *newnode = makeNode(LoadStmt); + + COPY_STRING_FIELD(filename); + + return newnode; +} + +static CreateDomainStmt * +_copyCreateDomainStmt(const CreateDomainStmt *from) +{ + CreateDomainStmt *newnode = makeNode(CreateDomainStmt); + + COPY_NODE_FIELD(domainname); + COPY_NODE_FIELD(typeName); + COPY_NODE_FIELD(collClause); + COPY_NODE_FIELD(constraints); + + return newnode; +} + +static CreateOpClassStmt * +_copyCreateOpClassStmt(const CreateOpClassStmt *from) +{ + CreateOpClassStmt *newnode = makeNode(CreateOpClassStmt); + + COPY_NODE_FIELD(opclassname); + COPY_NODE_FIELD(opfamilyname); + COPY_STRING_FIELD(amname); + COPY_NODE_FIELD(datatype); + COPY_NODE_FIELD(items); + COPY_SCALAR_FIELD(isDefault); + + return newnode; +} + +static CreateOpClassItem * +_copyCreateOpClassItem(const CreateOpClassItem *from) +{ + CreateOpClassItem *newnode = makeNode(CreateOpClassItem); + + COPY_SCALAR_FIELD(itemtype); + COPY_NODE_FIELD(name); + COPY_SCALAR_FIELD(number); + COPY_NODE_FIELD(order_family); + COPY_NODE_FIELD(class_args); + COPY_NODE_FIELD(storedtype); + + return newnode; +} + +static CreateOpFamilyStmt * +_copyCreateOpFamilyStmt(const CreateOpFamilyStmt *from) +{ + CreateOpFamilyStmt *newnode = makeNode(CreateOpFamilyStmt); + + COPY_NODE_FIELD(opfamilyname); + COPY_STRING_FIELD(amname); + + return newnode; +} + +static AlterOpFamilyStmt * +_copyAlterOpFamilyStmt(const AlterOpFamilyStmt *from) +{ + AlterOpFamilyStmt *newnode = makeNode(AlterOpFamilyStmt); + + COPY_NODE_FIELD(opfamilyname); + COPY_STRING_FIELD(amname); + COPY_SCALAR_FIELD(isDrop); + COPY_NODE_FIELD(items); + + return newnode; +} + +static CreatedbStmt * +_copyCreatedbStmt(const CreatedbStmt *from) +{ + CreatedbStmt *newnode = makeNode(CreatedbStmt); + + COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterDatabaseStmt * +_copyAlterDatabaseStmt(const AlterDatabaseStmt *from) +{ + AlterDatabaseStmt *newnode = makeNode(AlterDatabaseStmt); + + COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterDatabaseRefreshCollStmt * +_copyAlterDatabaseRefreshCollStmt(const AlterDatabaseRefreshCollStmt *from) +{ + AlterDatabaseRefreshCollStmt *newnode = makeNode(AlterDatabaseRefreshCollStmt); + + COPY_STRING_FIELD(dbname); + + return newnode; +} + +static AlterDatabaseSetStmt * +_copyAlterDatabaseSetStmt(const AlterDatabaseSetStmt *from) +{ + AlterDatabaseSetStmt *newnode = makeNode(AlterDatabaseSetStmt); + + COPY_STRING_FIELD(dbname); + COPY_NODE_FIELD(setstmt); + + return newnode; +} + +static DropdbStmt * +_copyDropdbStmt(const DropdbStmt *from) +{ + DropdbStmt *newnode = makeNode(DropdbStmt); + + COPY_STRING_FIELD(dbname); + COPY_SCALAR_FIELD(missing_ok); + COPY_NODE_FIELD(options); + + return newnode; +} + +static VacuumStmt * +_copyVacuumStmt(const VacuumStmt *from) +{ + VacuumStmt *newnode = makeNode(VacuumStmt); + + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(rels); + COPY_SCALAR_FIELD(is_vacuumcmd); + + return newnode; +} + +static VacuumRelation * +_copyVacuumRelation(const VacuumRelation *from) +{ + VacuumRelation *newnode = makeNode(VacuumRelation); + + COPY_NODE_FIELD(relation); + COPY_SCALAR_FIELD(oid); + COPY_NODE_FIELD(va_cols); + + return newnode; +} + +static ExplainStmt * +_copyExplainStmt(const ExplainStmt *from) +{ + ExplainStmt *newnode = makeNode(ExplainStmt); + + COPY_NODE_FIELD(query); + COPY_NODE_FIELD(options); + + return newnode; +} + +static CreateTableAsStmt * +_copyCreateTableAsStmt(const CreateTableAsStmt *from) +{ + CreateTableAsStmt *newnode = makeNode(CreateTableAsStmt); + + COPY_NODE_FIELD(query); + COPY_NODE_FIELD(into); + COPY_SCALAR_FIELD(objtype); + COPY_SCALAR_FIELD(is_select_into); + COPY_SCALAR_FIELD(if_not_exists); + + return newnode; +} + +static RefreshMatViewStmt * +_copyRefreshMatViewStmt(const RefreshMatViewStmt *from) +{ + RefreshMatViewStmt *newnode = makeNode(RefreshMatViewStmt); + + COPY_SCALAR_FIELD(concurrent); + COPY_SCALAR_FIELD(skipData); + COPY_NODE_FIELD(relation); + + return newnode; +} + +static ReplicaIdentityStmt * +_copyReplicaIdentityStmt(const ReplicaIdentityStmt *from) +{ + ReplicaIdentityStmt *newnode = makeNode(ReplicaIdentityStmt); + + COPY_SCALAR_FIELD(identity_type); + COPY_STRING_FIELD(name); + + return newnode; +} + +static AlterSystemStmt * +_copyAlterSystemStmt(const AlterSystemStmt *from) +{ + AlterSystemStmt *newnode = makeNode(AlterSystemStmt); + + COPY_NODE_FIELD(setstmt); + + return newnode; +} + +static CreateSeqStmt * +_copyCreateSeqStmt(const CreateSeqStmt *from) +{ + CreateSeqStmt *newnode = makeNode(CreateSeqStmt); + + COPY_NODE_FIELD(sequence); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(ownerId); + COPY_SCALAR_FIELD(for_identity); + COPY_SCALAR_FIELD(if_not_exists); + + return newnode; +} + +static AlterSeqStmt * +_copyAlterSeqStmt(const AlterSeqStmt *from) +{ + AlterSeqStmt *newnode = makeNode(AlterSeqStmt); + + COPY_NODE_FIELD(sequence); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(for_identity); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static VariableSetStmt * +_copyVariableSetStmt(const VariableSetStmt *from) +{ + VariableSetStmt *newnode = makeNode(VariableSetStmt); + + COPY_SCALAR_FIELD(kind); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(is_local); + + return newnode; +} + +static VariableShowStmt * +_copyVariableShowStmt(const VariableShowStmt *from) +{ + VariableShowStmt *newnode = makeNode(VariableShowStmt); + + COPY_STRING_FIELD(name); + + return newnode; +} + +static DiscardStmt * +_copyDiscardStmt(const DiscardStmt *from) +{ + DiscardStmt *newnode = makeNode(DiscardStmt); + + COPY_SCALAR_FIELD(target); + + return newnode; +} + +static CreateTableSpaceStmt * +_copyCreateTableSpaceStmt(const CreateTableSpaceStmt *from) +{ + CreateTableSpaceStmt *newnode = makeNode(CreateTableSpaceStmt); + + COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(owner); + COPY_STRING_FIELD(location); + COPY_NODE_FIELD(options); + + return newnode; +} + +static DropTableSpaceStmt * +_copyDropTableSpaceStmt(const DropTableSpaceStmt *from) +{ + DropTableSpaceStmt *newnode = makeNode(DropTableSpaceStmt); + + COPY_STRING_FIELD(tablespacename); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static AlterTableSpaceOptionsStmt * +_copyAlterTableSpaceOptionsStmt(const AlterTableSpaceOptionsStmt *from) +{ + AlterTableSpaceOptionsStmt *newnode = makeNode(AlterTableSpaceOptionsStmt); + + COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(isReset); + + return newnode; +} + +static AlterTableMoveAllStmt * +_copyAlterTableMoveAllStmt(const AlterTableMoveAllStmt *from) +{ + AlterTableMoveAllStmt *newnode = makeNode(AlterTableMoveAllStmt); + + COPY_STRING_FIELD(orig_tablespacename); + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(roles); + COPY_STRING_FIELD(new_tablespacename); + COPY_SCALAR_FIELD(nowait); + + return newnode; +} + +static CreateExtensionStmt * +_copyCreateExtensionStmt(const CreateExtensionStmt *from) +{ + CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt); + + COPY_STRING_FIELD(extname); + COPY_SCALAR_FIELD(if_not_exists); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterExtensionStmt * +_copyAlterExtensionStmt(const AlterExtensionStmt *from) +{ + AlterExtensionStmt *newnode = makeNode(AlterExtensionStmt); + + COPY_STRING_FIELD(extname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterExtensionContentsStmt * +_copyAlterExtensionContentsStmt(const AlterExtensionContentsStmt *from) +{ + AlterExtensionContentsStmt *newnode = makeNode(AlterExtensionContentsStmt); + + COPY_STRING_FIELD(extname); + COPY_SCALAR_FIELD(action); + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(object); + + return newnode; +} + +static CreateFdwStmt * +_copyCreateFdwStmt(const CreateFdwStmt *from) +{ + CreateFdwStmt *newnode = makeNode(CreateFdwStmt); + + COPY_STRING_FIELD(fdwname); + COPY_NODE_FIELD(func_options); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterFdwStmt * +_copyAlterFdwStmt(const AlterFdwStmt *from) +{ + AlterFdwStmt *newnode = makeNode(AlterFdwStmt); + + COPY_STRING_FIELD(fdwname); + COPY_NODE_FIELD(func_options); + COPY_NODE_FIELD(options); + + return newnode; +} + +static CreateForeignServerStmt * +_copyCreateForeignServerStmt(const CreateForeignServerStmt *from) +{ + CreateForeignServerStmt *newnode = makeNode(CreateForeignServerStmt); + + COPY_STRING_FIELD(servername); + COPY_STRING_FIELD(servertype); + COPY_STRING_FIELD(version); + COPY_STRING_FIELD(fdwname); + COPY_SCALAR_FIELD(if_not_exists); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterForeignServerStmt * +_copyAlterForeignServerStmt(const AlterForeignServerStmt *from) +{ + AlterForeignServerStmt *newnode = makeNode(AlterForeignServerStmt); + + COPY_STRING_FIELD(servername); + COPY_STRING_FIELD(version); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(has_version); + + return newnode; +} + +static CreateUserMappingStmt * +_copyCreateUserMappingStmt(const CreateUserMappingStmt *from) +{ + CreateUserMappingStmt *newnode = makeNode(CreateUserMappingStmt); + + COPY_NODE_FIELD(user); + COPY_STRING_FIELD(servername); + COPY_SCALAR_FIELD(if_not_exists); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterUserMappingStmt * +_copyAlterUserMappingStmt(const AlterUserMappingStmt *from) +{ + AlterUserMappingStmt *newnode = makeNode(AlterUserMappingStmt); + + COPY_NODE_FIELD(user); + COPY_STRING_FIELD(servername); + COPY_NODE_FIELD(options); + + return newnode; +} + +static DropUserMappingStmt * +_copyDropUserMappingStmt(const DropUserMappingStmt *from) +{ + DropUserMappingStmt *newnode = makeNode(DropUserMappingStmt); + + COPY_NODE_FIELD(user); + COPY_STRING_FIELD(servername); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static CreateForeignTableStmt * +_copyCreateForeignTableStmt(const CreateForeignTableStmt *from) +{ + CreateForeignTableStmt *newnode = makeNode(CreateForeignTableStmt); + + CopyCreateStmtFields((const CreateStmt *) from, (CreateStmt *) newnode); + + COPY_STRING_FIELD(servername); + COPY_NODE_FIELD(options); + + return newnode; +} + +static ImportForeignSchemaStmt * +_copyImportForeignSchemaStmt(const ImportForeignSchemaStmt *from) +{ + ImportForeignSchemaStmt *newnode = makeNode(ImportForeignSchemaStmt); + + COPY_STRING_FIELD(server_name); + COPY_STRING_FIELD(remote_schema); + COPY_STRING_FIELD(local_schema); + COPY_SCALAR_FIELD(list_type); + COPY_NODE_FIELD(table_list); + COPY_NODE_FIELD(options); + + return newnode; +} + +static CreateTransformStmt * +_copyCreateTransformStmt(const CreateTransformStmt *from) +{ + CreateTransformStmt *newnode = makeNode(CreateTransformStmt); + + COPY_SCALAR_FIELD(replace); + COPY_NODE_FIELD(type_name); + COPY_STRING_FIELD(lang); + COPY_NODE_FIELD(fromsql); + COPY_NODE_FIELD(tosql); + + return newnode; +} + +static CreateAmStmt * +_copyCreateAmStmt(const CreateAmStmt *from) +{ + CreateAmStmt *newnode = makeNode(CreateAmStmt); + + COPY_STRING_FIELD(amname); + COPY_NODE_FIELD(handler_name); + COPY_SCALAR_FIELD(amtype); + + return newnode; +} + +static CreateTrigStmt * +_copyCreateTrigStmt(const CreateTrigStmt *from) +{ + CreateTrigStmt *newnode = makeNode(CreateTrigStmt); + + COPY_SCALAR_FIELD(replace); + COPY_SCALAR_FIELD(isconstraint); + COPY_STRING_FIELD(trigname); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(funcname); + COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(row); + COPY_SCALAR_FIELD(timing); + COPY_SCALAR_FIELD(events); + COPY_NODE_FIELD(columns); + COPY_NODE_FIELD(whenClause); + COPY_NODE_FIELD(transitionRels); + COPY_SCALAR_FIELD(deferrable); + COPY_SCALAR_FIELD(initdeferred); + COPY_NODE_FIELD(constrrel); + + return newnode; +} + +static CreateEventTrigStmt * +_copyCreateEventTrigStmt(const CreateEventTrigStmt *from) +{ + CreateEventTrigStmt *newnode = makeNode(CreateEventTrigStmt); + + COPY_STRING_FIELD(trigname); + COPY_STRING_FIELD(eventname); + COPY_NODE_FIELD(whenclause); + COPY_NODE_FIELD(funcname); + + return newnode; +} + +static AlterEventTrigStmt * +_copyAlterEventTrigStmt(const AlterEventTrigStmt *from) +{ + AlterEventTrigStmt *newnode = makeNode(AlterEventTrigStmt); + + COPY_STRING_FIELD(trigname); + COPY_SCALAR_FIELD(tgenabled); + + return newnode; +} + +static CreatePLangStmt * +_copyCreatePLangStmt(const CreatePLangStmt *from) +{ + CreatePLangStmt *newnode = makeNode(CreatePLangStmt); + + COPY_SCALAR_FIELD(replace); + COPY_STRING_FIELD(plname); + COPY_NODE_FIELD(plhandler); + COPY_NODE_FIELD(plinline); + COPY_NODE_FIELD(plvalidator); + COPY_SCALAR_FIELD(pltrusted); + + return newnode; +} + +static CreateRoleStmt * +_copyCreateRoleStmt(const CreateRoleStmt *from) +{ + CreateRoleStmt *newnode = makeNode(CreateRoleStmt); + + COPY_SCALAR_FIELD(stmt_type); + COPY_STRING_FIELD(role); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterRoleStmt * +_copyAlterRoleStmt(const AlterRoleStmt *from) +{ + AlterRoleStmt *newnode = makeNode(AlterRoleStmt); + + COPY_NODE_FIELD(role); + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(action); + + return newnode; +} + +static AlterRoleSetStmt * +_copyAlterRoleSetStmt(const AlterRoleSetStmt *from) +{ + AlterRoleSetStmt *newnode = makeNode(AlterRoleSetStmt); + + COPY_NODE_FIELD(role); + COPY_STRING_FIELD(database); + COPY_NODE_FIELD(setstmt); + + return newnode; +} + +static DropRoleStmt * +_copyDropRoleStmt(const DropRoleStmt *from) +{ + DropRoleStmt *newnode = makeNode(DropRoleStmt); + + COPY_NODE_FIELD(roles); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static LockStmt * +_copyLockStmt(const LockStmt *from) +{ + LockStmt *newnode = makeNode(LockStmt); + + COPY_NODE_FIELD(relations); + COPY_SCALAR_FIELD(mode); + COPY_SCALAR_FIELD(nowait); + + return newnode; +} + +static ConstraintsSetStmt * +_copyConstraintsSetStmt(const ConstraintsSetStmt *from) +{ + ConstraintsSetStmt *newnode = makeNode(ConstraintsSetStmt); + + COPY_NODE_FIELD(constraints); + COPY_SCALAR_FIELD(deferred); + + return newnode; +} + +static ReindexStmt * +_copyReindexStmt(const ReindexStmt *from) +{ + ReindexStmt *newnode = makeNode(ReindexStmt); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(params); + + return newnode; +} + +static CreateSchemaStmt * +_copyCreateSchemaStmt(const CreateSchemaStmt *from) +{ + CreateSchemaStmt *newnode = makeNode(CreateSchemaStmt); + + COPY_STRING_FIELD(schemaname); + COPY_NODE_FIELD(authrole); + COPY_NODE_FIELD(schemaElts); + COPY_SCALAR_FIELD(if_not_exists); + + return newnode; +} + +static CreateConversionStmt * +_copyCreateConversionStmt(const CreateConversionStmt *from) +{ + CreateConversionStmt *newnode = makeNode(CreateConversionStmt); + + COPY_NODE_FIELD(conversion_name); + COPY_STRING_FIELD(for_encoding_name); + COPY_STRING_FIELD(to_encoding_name); + COPY_NODE_FIELD(func_name); + COPY_SCALAR_FIELD(def); + + return newnode; +} + +static CreateCastStmt * +_copyCreateCastStmt(const CreateCastStmt *from) +{ + CreateCastStmt *newnode = makeNode(CreateCastStmt); + + COPY_NODE_FIELD(sourcetype); + COPY_NODE_FIELD(targettype); + COPY_NODE_FIELD(func); + COPY_SCALAR_FIELD(context); + COPY_SCALAR_FIELD(inout); + + return newnode; +} + +static PrepareStmt * +_copyPrepareStmt(const PrepareStmt *from) +{ + PrepareStmt *newnode = makeNode(PrepareStmt); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(argtypes); + COPY_NODE_FIELD(query); + + return newnode; +} + +static ExecuteStmt * +_copyExecuteStmt(const ExecuteStmt *from) +{ + ExecuteStmt *newnode = makeNode(ExecuteStmt); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(params); + + return newnode; +} + +static DeallocateStmt * +_copyDeallocateStmt(const DeallocateStmt *from) +{ + DeallocateStmt *newnode = makeNode(DeallocateStmt); + + COPY_STRING_FIELD(name); + + return newnode; +} + +static DropOwnedStmt * +_copyDropOwnedStmt(const DropOwnedStmt *from) +{ + DropOwnedStmt *newnode = makeNode(DropOwnedStmt); + + COPY_NODE_FIELD(roles); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +static ReassignOwnedStmt * +_copyReassignOwnedStmt(const ReassignOwnedStmt *from) +{ + ReassignOwnedStmt *newnode = makeNode(ReassignOwnedStmt); + + COPY_NODE_FIELD(roles); + COPY_NODE_FIELD(newrole); + + return newnode; +} + +static AlterTSDictionaryStmt * +_copyAlterTSDictionaryStmt(const AlterTSDictionaryStmt *from) +{ + AlterTSDictionaryStmt *newnode = makeNode(AlterTSDictionaryStmt); + + COPY_NODE_FIELD(dictname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterTSConfigurationStmt * +_copyAlterTSConfigurationStmt(const AlterTSConfigurationStmt *from) +{ + AlterTSConfigurationStmt *newnode = makeNode(AlterTSConfigurationStmt); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(cfgname); + COPY_NODE_FIELD(tokentype); + COPY_NODE_FIELD(dicts); + COPY_SCALAR_FIELD(override); + COPY_SCALAR_FIELD(replace); + COPY_SCALAR_FIELD(missing_ok); + + return newnode; +} + +static CreatePolicyStmt * +_copyCreatePolicyStmt(const CreatePolicyStmt *from) +{ + CreatePolicyStmt *newnode = makeNode(CreatePolicyStmt); + + COPY_STRING_FIELD(policy_name); + COPY_NODE_FIELD(table); + COPY_STRING_FIELD(cmd_name); + COPY_SCALAR_FIELD(permissive); + COPY_NODE_FIELD(roles); + COPY_NODE_FIELD(qual); + COPY_NODE_FIELD(with_check); + + return newnode; +} + +static AlterPolicyStmt * +_copyAlterPolicyStmt(const AlterPolicyStmt *from) +{ + AlterPolicyStmt *newnode = makeNode(AlterPolicyStmt); + + COPY_STRING_FIELD(policy_name); + COPY_NODE_FIELD(table); + COPY_NODE_FIELD(roles); + COPY_NODE_FIELD(qual); + COPY_NODE_FIELD(with_check); + + return newnode; +} + +static PartitionElem * +_copyPartitionElem(const PartitionElem *from) +{ + PartitionElem *newnode = makeNode(PartitionElem); + + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(expr); + COPY_NODE_FIELD(collation); + COPY_NODE_FIELD(opclass); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static PartitionSpec * +_copyPartitionSpec(const PartitionSpec *from) +{ + PartitionSpec *newnode = makeNode(PartitionSpec); + + COPY_STRING_FIELD(strategy); + COPY_NODE_FIELD(partParams); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static PartitionBoundSpec * +_copyPartitionBoundSpec(const PartitionBoundSpec *from) +{ + PartitionBoundSpec *newnode = makeNode(PartitionBoundSpec); + + COPY_SCALAR_FIELD(strategy); + COPY_SCALAR_FIELD(is_default); + COPY_SCALAR_FIELD(modulus); + COPY_SCALAR_FIELD(remainder); + COPY_NODE_FIELD(listdatums); + COPY_NODE_FIELD(lowerdatums); + COPY_NODE_FIELD(upperdatums); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static PartitionRangeDatum * +_copyPartitionRangeDatum(const PartitionRangeDatum *from) +{ + PartitionRangeDatum *newnode = makeNode(PartitionRangeDatum); + + COPY_SCALAR_FIELD(kind); + COPY_NODE_FIELD(value); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static PartitionCmd * +_copyPartitionCmd(const PartitionCmd *from) +{ + PartitionCmd *newnode = makeNode(PartitionCmd); + + COPY_NODE_FIELD(name); + COPY_NODE_FIELD(bound); + COPY_SCALAR_FIELD(concurrent); + + return newnode; +} + +static PublicationObjSpec * +_copyPublicationObject(const PublicationObjSpec *from) +{ + PublicationObjSpec *newnode = makeNode(PublicationObjSpec); + + COPY_SCALAR_FIELD(pubobjtype); + COPY_STRING_FIELD(name); + COPY_NODE_FIELD(pubtable); + COPY_LOCATION_FIELD(location); + + return newnode; +} + +static PublicationTable * +_copyPublicationTable(const PublicationTable *from) +{ + PublicationTable *newnode = makeNode(PublicationTable); + + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(whereClause); + COPY_NODE_FIELD(columns); + + return newnode; +} + +static CreatePublicationStmt * +_copyCreatePublicationStmt(const CreatePublicationStmt *from) +{ + CreatePublicationStmt *newnode = makeNode(CreatePublicationStmt); + + COPY_STRING_FIELD(pubname); + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(pubobjects); + COPY_SCALAR_FIELD(for_all_tables); + + return newnode; +} + +static AlterPublicationStmt * +_copyAlterPublicationStmt(const AlterPublicationStmt *from) +{ + AlterPublicationStmt *newnode = makeNode(AlterPublicationStmt); + + COPY_STRING_FIELD(pubname); + COPY_NODE_FIELD(options); + COPY_NODE_FIELD(pubobjects); + COPY_SCALAR_FIELD(for_all_tables); + COPY_SCALAR_FIELD(action); + + return newnode; +} + +static CreateSubscriptionStmt * +_copyCreateSubscriptionStmt(const CreateSubscriptionStmt *from) +{ + CreateSubscriptionStmt *newnode = makeNode(CreateSubscriptionStmt); + + COPY_STRING_FIELD(subname); + COPY_STRING_FIELD(conninfo); + COPY_NODE_FIELD(publication); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterSubscriptionStmt * +_copyAlterSubscriptionStmt(const AlterSubscriptionStmt *from) +{ + AlterSubscriptionStmt *newnode = makeNode(AlterSubscriptionStmt); + + COPY_SCALAR_FIELD(kind); + COPY_STRING_FIELD(subname); + COPY_STRING_FIELD(conninfo); + COPY_NODE_FIELD(publication); + COPY_NODE_FIELD(options); + + return newnode; +} + +static DropSubscriptionStmt * +_copyDropSubscriptionStmt(const DropSubscriptionStmt *from) +{ + DropSubscriptionStmt *newnode = makeNode(DropSubscriptionStmt); + + COPY_STRING_FIELD(subname); + COPY_SCALAR_FIELD(missing_ok); + COPY_SCALAR_FIELD(behavior); + + return newnode; +} + +/* **************************************************************** + * extensible.h copy functions + * **************************************************************** + */ +static ExtensibleNode * +_copyExtensibleNode(const ExtensibleNode *from) +{ + ExtensibleNode *newnode; + const ExtensibleNodeMethods *methods; + + methods = GetExtensibleNodeMethods(from->extnodename, false); + newnode = (ExtensibleNode *) newNode(methods->node_size, + T_ExtensibleNode); + COPY_STRING_FIELD(extnodename); + + /* copy the private fields */ + methods->nodeCopy(newnode, from); + + return newnode; +} + +/* **************************************************************** + * value.h copy functions + * **************************************************************** + */ +static Integer * +_copyInteger(const Integer *from) +{ + Integer *newnode = makeNode(Integer); + + COPY_SCALAR_FIELD(ival); + + return newnode; +} + +static Float * +_copyFloat(const Float *from) +{ + Float *newnode = makeNode(Float); + + COPY_STRING_FIELD(fval); + + return newnode; +} + +static Boolean * +_copyBoolean(const Boolean *from) +{ + Boolean *newnode = makeNode(Boolean); + + COPY_SCALAR_FIELD(boolval); + + return newnode; +} + +static String * +_copyString(const String *from) +{ + String *newnode = makeNode(String); + + COPY_STRING_FIELD(sval); + + return newnode; +} + +static BitString * +_copyBitString(const BitString *from) +{ + BitString *newnode = makeNode(BitString); + + COPY_STRING_FIELD(bsval); + + return newnode; +} + + +static ForeignKeyCacheInfo * +_copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from) +{ + ForeignKeyCacheInfo *newnode = makeNode(ForeignKeyCacheInfo); + + COPY_SCALAR_FIELD(conoid); + COPY_SCALAR_FIELD(conrelid); + COPY_SCALAR_FIELD(confrelid); + COPY_SCALAR_FIELD(nkeys); + COPY_ARRAY_FIELD(conkey); + COPY_ARRAY_FIELD(confkey); + COPY_ARRAY_FIELD(conpfeqop); + + return newnode; +} + +/* + * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h + * + * Create a copy of a Node tree or list. This is a "deep" copy: all + * substructure is copied too, recursively. + */ +void * +copyObjectImpl(const void *from) +{ + void *retval; + + if (from == NULL) + return NULL; + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + switch (nodeTag(from)) + { + /* + * PLAN NODES + */ + case T_PlannedStmt: + retval = _copyPlannedStmt(from); + break; + case T_Plan: + retval = _copyPlan(from); + break; + case T_Result: + retval = _copyResult(from); + break; + case T_ProjectSet: + retval = _copyProjectSet(from); + break; + case T_ModifyTable: + retval = _copyModifyTable(from); + break; + case T_Append: + retval = _copyAppend(from); + break; + case T_MergeAppend: + retval = _copyMergeAppend(from); + break; + case T_RecursiveUnion: + retval = _copyRecursiveUnion(from); + break; + case T_BitmapAnd: + retval = _copyBitmapAnd(from); + break; + case T_BitmapOr: + retval = _copyBitmapOr(from); + break; + case T_Scan: + retval = _copyScan(from); + break; + case T_Gather: + retval = _copyGather(from); + break; + case T_GatherMerge: + retval = _copyGatherMerge(from); + break; + case T_SeqScan: + retval = _copySeqScan(from); + break; + case T_SampleScan: + retval = _copySampleScan(from); + break; + case T_IndexScan: + retval = _copyIndexScan(from); + break; + case T_IndexOnlyScan: + retval = _copyIndexOnlyScan(from); + break; + case T_BitmapIndexScan: + retval = _copyBitmapIndexScan(from); + break; + case T_BitmapHeapScan: + retval = _copyBitmapHeapScan(from); + break; + case T_TidScan: + retval = _copyTidScan(from); + break; + case T_TidRangeScan: + retval = _copyTidRangeScan(from); + break; + case T_SubqueryScan: + retval = _copySubqueryScan(from); + break; + case T_FunctionScan: + retval = _copyFunctionScan(from); + break; + case T_TableFuncScan: + retval = _copyTableFuncScan(from); + break; + case T_ValuesScan: + retval = _copyValuesScan(from); + break; + case T_CteScan: + retval = _copyCteScan(from); + break; + case T_NamedTuplestoreScan: + retval = _copyNamedTuplestoreScan(from); + break; + case T_WorkTableScan: + retval = _copyWorkTableScan(from); + break; + case T_ForeignScan: + retval = _copyForeignScan(from); + break; + case T_CustomScan: + retval = _copyCustomScan(from); + break; + case T_Join: + retval = _copyJoin(from); + break; + case T_NestLoop: + retval = _copyNestLoop(from); + break; + case T_MergeJoin: + retval = _copyMergeJoin(from); + break; + case T_HashJoin: + retval = _copyHashJoin(from); + break; + case T_Material: + retval = _copyMaterial(from); + break; + case T_Memoize: + retval = _copyMemoize(from); + break; + case T_Sort: + retval = _copySort(from); + break; + case T_IncrementalSort: + retval = _copyIncrementalSort(from); + break; + case T_Group: + retval = _copyGroup(from); + break; + case T_Agg: + retval = _copyAgg(from); + break; + case T_WindowAgg: + retval = _copyWindowAgg(from); + break; + case T_Unique: + retval = _copyUnique(from); + break; + case T_Hash: + retval = _copyHash(from); + break; + case T_SetOp: + retval = _copySetOp(from); + break; + case T_LockRows: + retval = _copyLockRows(from); + break; + case T_Limit: + retval = _copyLimit(from); + break; + case T_NestLoopParam: + retval = _copyNestLoopParam(from); + break; + case T_PlanRowMark: + retval = _copyPlanRowMark(from); + break; + case T_PartitionPruneInfo: + retval = _copyPartitionPruneInfo(from); + break; + case T_PartitionedRelPruneInfo: + retval = _copyPartitionedRelPruneInfo(from); + break; + case T_PartitionPruneStepOp: + retval = _copyPartitionPruneStepOp(from); + break; + case T_PartitionPruneStepCombine: + retval = _copyPartitionPruneStepCombine(from); + break; + case T_PlanInvalItem: + retval = _copyPlanInvalItem(from); + break; + + /* + * PRIMITIVE NODES + */ + case T_Alias: + retval = _copyAlias(from); + break; + case T_RangeVar: + retval = _copyRangeVar(from); + break; + case T_TableFunc: + retval = _copyTableFunc(from); + break; + case T_IntoClause: + retval = _copyIntoClause(from); + break; + case T_Var: + retval = _copyVar(from); + break; + case T_Const: + retval = _copyConst(from); + break; + case T_Param: + retval = _copyParam(from); + break; + case T_Aggref: + retval = _copyAggref(from); + break; + case T_GroupingFunc: + retval = _copyGroupingFunc(from); + break; + case T_WindowFunc: + retval = _copyWindowFunc(from); + break; + case T_SubscriptingRef: + retval = _copySubscriptingRef(from); + break; + case T_FuncExpr: + retval = _copyFuncExpr(from); + break; + case T_NamedArgExpr: + retval = _copyNamedArgExpr(from); + break; + case T_OpExpr: + retval = _copyOpExpr(from); + break; + case T_DistinctExpr: + retval = _copyDistinctExpr(from); + break; + case T_NullIfExpr: + retval = _copyNullIfExpr(from); + break; + case T_ScalarArrayOpExpr: + retval = _copyScalarArrayOpExpr(from); + break; + case T_BoolExpr: + retval = _copyBoolExpr(from); + break; + case T_SubLink: + retval = _copySubLink(from); + break; + case T_SubPlan: + retval = _copySubPlan(from); + break; + case T_AlternativeSubPlan: + retval = _copyAlternativeSubPlan(from); + break; + case T_FieldSelect: + retval = _copyFieldSelect(from); + break; + case T_FieldStore: + retval = _copyFieldStore(from); + break; + case T_RelabelType: + retval = _copyRelabelType(from); + break; + case T_CoerceViaIO: + retval = _copyCoerceViaIO(from); + break; + case T_ArrayCoerceExpr: + retval = _copyArrayCoerceExpr(from); + break; + case T_ConvertRowtypeExpr: + retval = _copyConvertRowtypeExpr(from); + break; + case T_CollateExpr: + retval = _copyCollateExpr(from); + break; + case T_CaseExpr: + retval = _copyCaseExpr(from); + break; + case T_CaseWhen: + retval = _copyCaseWhen(from); + break; + case T_CaseTestExpr: + retval = _copyCaseTestExpr(from); + break; + case T_ArrayExpr: + retval = _copyArrayExpr(from); + break; + case T_RowExpr: + retval = _copyRowExpr(from); + break; + case T_RowCompareExpr: + retval = _copyRowCompareExpr(from); + break; + case T_CoalesceExpr: + retval = _copyCoalesceExpr(from); + break; + case T_MinMaxExpr: + retval = _copyMinMaxExpr(from); + break; + case T_SQLValueFunction: + retval = _copySQLValueFunction(from); + break; + case T_XmlExpr: + retval = _copyXmlExpr(from); + break; + case T_NullTest: + retval = _copyNullTest(from); + break; + case T_BooleanTest: + retval = _copyBooleanTest(from); + break; + case T_CoerceToDomain: + retval = _copyCoerceToDomain(from); + break; + case T_CoerceToDomainValue: + retval = _copyCoerceToDomainValue(from); + break; + case T_SetToDefault: + retval = _copySetToDefault(from); + break; + case T_CurrentOfExpr: + retval = _copyCurrentOfExpr(from); + break; + case T_NextValueExpr: + retval = _copyNextValueExpr(from); + break; + case T_InferenceElem: + retval = _copyInferenceElem(from); + break; + case T_TargetEntry: + retval = _copyTargetEntry(from); + break; + case T_RangeTblRef: + retval = _copyRangeTblRef(from); + break; + case T_JoinExpr: + retval = _copyJoinExpr(from); + break; + case T_FromExpr: + retval = _copyFromExpr(from); + break; + case T_OnConflictExpr: + retval = _copyOnConflictExpr(from); + break; + + /* + * RELATION NODES + */ + case T_PathKey: + retval = _copyPathKey(from); + break; + case T_RestrictInfo: + retval = _copyRestrictInfo(from); + break; + case T_PlaceHolderVar: + retval = _copyPlaceHolderVar(from); + break; + case T_SpecialJoinInfo: + retval = _copySpecialJoinInfo(from); + break; + case T_AppendRelInfo: + retval = _copyAppendRelInfo(from); + break; + case T_PlaceHolderInfo: + retval = _copyPlaceHolderInfo(from); + break; + + /* + * VALUE NODES + */ + case T_Integer: + retval = _copyInteger(from); + break; + case T_Float: + retval = _copyFloat(from); + break; + case T_Boolean: + retval = _copyBoolean(from); + break; + case T_String: + retval = _copyString(from); + break; + case T_BitString: + retval = _copyBitString(from); + break; + + /* + * LIST NODES + */ + case T_List: + retval = list_copy_deep(from); + break; + + /* + * Lists of integers and OIDs don't need to be deep-copied, so we + * perform a shallow copy via list_copy() + */ + case T_IntList: + case T_OidList: + retval = list_copy(from); + break; + + /* + * EXTENSIBLE NODES + */ + case T_ExtensibleNode: + retval = _copyExtensibleNode(from); + break; + + /* + * PARSE NODES + */ + case T_Query: + retval = _copyQuery(from); + break; + case T_RawStmt: + retval = _copyRawStmt(from); + break; + case T_InsertStmt: + retval = _copyInsertStmt(from); + break; + case T_DeleteStmt: + retval = _copyDeleteStmt(from); + break; + case T_UpdateStmt: + retval = _copyUpdateStmt(from); + break; + case T_MergeStmt: + retval = _copyMergeStmt(from); + break; + case T_SelectStmt: + retval = _copySelectStmt(from); + break; + case T_SetOperationStmt: + retval = _copySetOperationStmt(from); + break; + case T_ReturnStmt: + retval = _copyReturnStmt(from); + break; + case T_PLAssignStmt: + retval = _copyPLAssignStmt(from); + break; + case T_AlterTableStmt: + retval = _copyAlterTableStmt(from); + break; + case T_AlterTableCmd: + retval = _copyAlterTableCmd(from); + break; + case T_AlterCollationStmt: + retval = _copyAlterCollationStmt(from); + break; + case T_AlterDomainStmt: + retval = _copyAlterDomainStmt(from); + break; + case T_GrantStmt: + retval = _copyGrantStmt(from); + break; + case T_GrantRoleStmt: + retval = _copyGrantRoleStmt(from); + break; + case T_AlterDefaultPrivilegesStmt: + retval = _copyAlterDefaultPrivilegesStmt(from); + break; + case T_DeclareCursorStmt: + retval = _copyDeclareCursorStmt(from); + break; + case T_ClosePortalStmt: + retval = _copyClosePortalStmt(from); + break; + case T_CallStmt: + retval = _copyCallStmt(from); + break; + case T_ClusterStmt: + retval = _copyClusterStmt(from); + break; + case T_CopyStmt: + retval = _copyCopyStmt(from); + break; + case T_CreateStmt: + retval = _copyCreateStmt(from); + break; + case T_TableLikeClause: + retval = _copyTableLikeClause(from); + break; + case T_DefineStmt: + retval = _copyDefineStmt(from); + break; + case T_DropStmt: + retval = _copyDropStmt(from); + break; + case T_TruncateStmt: + retval = _copyTruncateStmt(from); + break; + case T_CommentStmt: + retval = _copyCommentStmt(from); + break; + case T_SecLabelStmt: + retval = _copySecLabelStmt(from); + break; + case T_FetchStmt: + retval = _copyFetchStmt(from); + break; + case T_IndexStmt: + retval = _copyIndexStmt(from); + break; + case T_CreateStatsStmt: + retval = _copyCreateStatsStmt(from); + break; + case T_AlterStatsStmt: + retval = _copyAlterStatsStmt(from); + break; + case T_CreateFunctionStmt: + retval = _copyCreateFunctionStmt(from); + break; + case T_FunctionParameter: + retval = _copyFunctionParameter(from); + break; + case T_AlterFunctionStmt: + retval = _copyAlterFunctionStmt(from); + break; + case T_DoStmt: + retval = _copyDoStmt(from); + break; + case T_RenameStmt: + retval = _copyRenameStmt(from); + break; + case T_AlterObjectDependsStmt: + retval = _copyAlterObjectDependsStmt(from); + break; + case T_AlterObjectSchemaStmt: + retval = _copyAlterObjectSchemaStmt(from); + break; + case T_AlterOwnerStmt: + retval = _copyAlterOwnerStmt(from); + break; + case T_AlterOperatorStmt: + retval = _copyAlterOperatorStmt(from); + break; + case T_AlterTypeStmt: + retval = _copyAlterTypeStmt(from); + break; + case T_RuleStmt: + retval = _copyRuleStmt(from); + break; + case T_NotifyStmt: + retval = _copyNotifyStmt(from); + break; + case T_ListenStmt: + retval = _copyListenStmt(from); + break; + case T_UnlistenStmt: + retval = _copyUnlistenStmt(from); + break; + case T_TransactionStmt: + retval = _copyTransactionStmt(from); + break; + case T_CompositeTypeStmt: + retval = _copyCompositeTypeStmt(from); + break; + case T_CreateEnumStmt: + retval = _copyCreateEnumStmt(from); + break; + case T_CreateRangeStmt: + retval = _copyCreateRangeStmt(from); + break; + case T_AlterEnumStmt: + retval = _copyAlterEnumStmt(from); + break; + case T_ViewStmt: + retval = _copyViewStmt(from); + break; + case T_LoadStmt: + retval = _copyLoadStmt(from); + break; + case T_CreateDomainStmt: + retval = _copyCreateDomainStmt(from); + break; + case T_CreateOpClassStmt: + retval = _copyCreateOpClassStmt(from); + break; + case T_CreateOpClassItem: + retval = _copyCreateOpClassItem(from); + break; + case T_CreateOpFamilyStmt: + retval = _copyCreateOpFamilyStmt(from); + break; + case T_AlterOpFamilyStmt: + retval = _copyAlterOpFamilyStmt(from); + break; + case T_CreatedbStmt: + retval = _copyCreatedbStmt(from); + break; + case T_AlterDatabaseStmt: + retval = _copyAlterDatabaseStmt(from); + break; + case T_AlterDatabaseRefreshCollStmt: + retval = _copyAlterDatabaseRefreshCollStmt(from); + break; + case T_AlterDatabaseSetStmt: + retval = _copyAlterDatabaseSetStmt(from); + break; + case T_DropdbStmt: + retval = _copyDropdbStmt(from); + break; + case T_VacuumStmt: + retval = _copyVacuumStmt(from); + break; + case T_VacuumRelation: + retval = _copyVacuumRelation(from); + break; + case T_ExplainStmt: + retval = _copyExplainStmt(from); + break; + case T_CreateTableAsStmt: + retval = _copyCreateTableAsStmt(from); + break; + case T_RefreshMatViewStmt: + retval = _copyRefreshMatViewStmt(from); + break; + case T_ReplicaIdentityStmt: + retval = _copyReplicaIdentityStmt(from); + break; + case T_AlterSystemStmt: + retval = _copyAlterSystemStmt(from); + break; + case T_CreateSeqStmt: + retval = _copyCreateSeqStmt(from); + break; + case T_AlterSeqStmt: + retval = _copyAlterSeqStmt(from); + break; + case T_VariableSetStmt: + retval = _copyVariableSetStmt(from); + break; + case T_VariableShowStmt: + retval = _copyVariableShowStmt(from); + break; + case T_DiscardStmt: + retval = _copyDiscardStmt(from); + break; + case T_CreateTableSpaceStmt: + retval = _copyCreateTableSpaceStmt(from); + break; + case T_DropTableSpaceStmt: + retval = _copyDropTableSpaceStmt(from); + break; + case T_AlterTableSpaceOptionsStmt: + retval = _copyAlterTableSpaceOptionsStmt(from); + break; + case T_AlterTableMoveAllStmt: + retval = _copyAlterTableMoveAllStmt(from); + break; + case T_CreateExtensionStmt: + retval = _copyCreateExtensionStmt(from); + break; + case T_AlterExtensionStmt: + retval = _copyAlterExtensionStmt(from); + break; + case T_AlterExtensionContentsStmt: + retval = _copyAlterExtensionContentsStmt(from); + break; + case T_CreateFdwStmt: + retval = _copyCreateFdwStmt(from); + break; + case T_AlterFdwStmt: + retval = _copyAlterFdwStmt(from); + break; + case T_CreateForeignServerStmt: + retval = _copyCreateForeignServerStmt(from); + break; + case T_AlterForeignServerStmt: + retval = _copyAlterForeignServerStmt(from); + break; + case T_CreateUserMappingStmt: + retval = _copyCreateUserMappingStmt(from); + break; + case T_AlterUserMappingStmt: + retval = _copyAlterUserMappingStmt(from); + break; + case T_DropUserMappingStmt: + retval = _copyDropUserMappingStmt(from); + break; + case T_CreateForeignTableStmt: + retval = _copyCreateForeignTableStmt(from); + break; + case T_ImportForeignSchemaStmt: + retval = _copyImportForeignSchemaStmt(from); + break; + case T_CreateTransformStmt: + retval = _copyCreateTransformStmt(from); + break; + case T_CreateAmStmt: + retval = _copyCreateAmStmt(from); + break; + case T_CreateTrigStmt: + retval = _copyCreateTrigStmt(from); + break; + case T_CreateEventTrigStmt: + retval = _copyCreateEventTrigStmt(from); + break; + case T_AlterEventTrigStmt: + retval = _copyAlterEventTrigStmt(from); + break; + case T_CreatePLangStmt: + retval = _copyCreatePLangStmt(from); + break; + case T_CreateRoleStmt: + retval = _copyCreateRoleStmt(from); + break; + case T_AlterRoleStmt: + retval = _copyAlterRoleStmt(from); + break; + case T_AlterRoleSetStmt: + retval = _copyAlterRoleSetStmt(from); + break; + case T_DropRoleStmt: + retval = _copyDropRoleStmt(from); + break; + case T_LockStmt: + retval = _copyLockStmt(from); + break; + case T_ConstraintsSetStmt: + retval = _copyConstraintsSetStmt(from); + break; + case T_ReindexStmt: + retval = _copyReindexStmt(from); + break; + case T_CheckPointStmt: + retval = (void *) makeNode(CheckPointStmt); + break; + case T_CreateSchemaStmt: + retval = _copyCreateSchemaStmt(from); + break; + case T_CreateConversionStmt: + retval = _copyCreateConversionStmt(from); + break; + case T_CreateCastStmt: + retval = _copyCreateCastStmt(from); + break; + case T_PrepareStmt: + retval = _copyPrepareStmt(from); + break; + case T_ExecuteStmt: + retval = _copyExecuteStmt(from); + break; + case T_DeallocateStmt: + retval = _copyDeallocateStmt(from); + break; + case T_DropOwnedStmt: + retval = _copyDropOwnedStmt(from); + break; + case T_ReassignOwnedStmt: + retval = _copyReassignOwnedStmt(from); + break; + case T_AlterTSDictionaryStmt: + retval = _copyAlterTSDictionaryStmt(from); + break; + case T_AlterTSConfigurationStmt: + retval = _copyAlterTSConfigurationStmt(from); + break; + case T_CreatePolicyStmt: + retval = _copyCreatePolicyStmt(from); + break; + case T_AlterPolicyStmt: + retval = _copyAlterPolicyStmt(from); + break; + case T_CreatePublicationStmt: + retval = _copyCreatePublicationStmt(from); + break; + case T_AlterPublicationStmt: + retval = _copyAlterPublicationStmt(from); + break; + case T_CreateSubscriptionStmt: + retval = _copyCreateSubscriptionStmt(from); + break; + case T_AlterSubscriptionStmt: + retval = _copyAlterSubscriptionStmt(from); + break; + case T_DropSubscriptionStmt: + retval = _copyDropSubscriptionStmt(from); + break; + case T_A_Expr: + retval = _copyA_Expr(from); + break; + case T_ColumnRef: + retval = _copyColumnRef(from); + break; + case T_ParamRef: + retval = _copyParamRef(from); + break; + case T_A_Const: + retval = _copyA_Const(from); + break; + case T_FuncCall: + retval = _copyFuncCall(from); + break; + case T_A_Star: + retval = _copyA_Star(from); + break; + case T_A_Indices: + retval = _copyA_Indices(from); + break; + case T_A_Indirection: + retval = _copyA_Indirection(from); + break; + case T_A_ArrayExpr: + retval = _copyA_ArrayExpr(from); + break; + case T_ResTarget: + retval = _copyResTarget(from); + break; + case T_MultiAssignRef: + retval = _copyMultiAssignRef(from); + break; + case T_TypeCast: + retval = _copyTypeCast(from); + break; + case T_CollateClause: + retval = _copyCollateClause(from); + break; + case T_SortBy: + retval = _copySortBy(from); + break; + case T_WindowDef: + retval = _copyWindowDef(from); + break; + case T_RangeSubselect: + retval = _copyRangeSubselect(from); + break; + case T_RangeFunction: + retval = _copyRangeFunction(from); + break; + case T_RangeTableSample: + retval = _copyRangeTableSample(from); + break; + case T_RangeTableFunc: + retval = _copyRangeTableFunc(from); + break; + case T_RangeTableFuncCol: + retval = _copyRangeTableFuncCol(from); + break; + case T_TypeName: + retval = _copyTypeName(from); + break; + case T_IndexElem: + retval = _copyIndexElem(from); + break; + case T_StatsElem: + retval = _copyStatsElem(from); + break; + case T_ColumnDef: + retval = _copyColumnDef(from); + break; + case T_Constraint: + retval = _copyConstraint(from); + break; + case T_DefElem: + retval = _copyDefElem(from); + break; + case T_LockingClause: + retval = _copyLockingClause(from); + break; + case T_RangeTblEntry: + retval = _copyRangeTblEntry(from); + break; + case T_RangeTblFunction: + retval = _copyRangeTblFunction(from); + break; + case T_TableSampleClause: + retval = _copyTableSampleClause(from); + break; + case T_WithCheckOption: + retval = _copyWithCheckOption(from); + break; + case T_SortGroupClause: + retval = _copySortGroupClause(from); + break; + case T_GroupingSet: + retval = _copyGroupingSet(from); + break; + case T_WindowClause: + retval = _copyWindowClause(from); + break; + case T_RowMarkClause: + retval = _copyRowMarkClause(from); + break; + case T_WithClause: + retval = _copyWithClause(from); + break; + case T_InferClause: + retval = _copyInferClause(from); + break; + case T_OnConflictClause: + retval = _copyOnConflictClause(from); + break; + case T_CTESearchClause: + retval = _copyCTESearchClause(from); + break; + case T_CTECycleClause: + retval = _copyCTECycleClause(from); + break; + case T_CommonTableExpr: + retval = _copyCommonTableExpr(from); + break; + case T_MergeWhenClause: + retval = _copyMergeWhenClause(from); + break; + case T_MergeAction: + retval = _copyMergeAction(from); + break; + case T_ObjectWithArgs: + retval = _copyObjectWithArgs(from); + break; + case T_AccessPriv: + retval = _copyAccessPriv(from); + break; + case T_XmlSerialize: + retval = _copyXmlSerialize(from); + break; + case T_RoleSpec: + retval = _copyRoleSpec(from); + break; + case T_TriggerTransition: + retval = _copyTriggerTransition(from); + break; + case T_PartitionElem: + retval = _copyPartitionElem(from); + break; + case T_PartitionSpec: + retval = _copyPartitionSpec(from); + break; + case T_PartitionBoundSpec: + retval = _copyPartitionBoundSpec(from); + break; + case T_PartitionRangeDatum: + retval = _copyPartitionRangeDatum(from); + break; + case T_PartitionCmd: + retval = _copyPartitionCmd(from); + break; + case T_PublicationObjSpec: + retval = _copyPublicationObject(from); + break; + case T_PublicationTable: + retval = _copyPublicationTable(from); + break; + + /* + * MISCELLANEOUS NODES + */ + case T_ForeignKeyCacheInfo: + retval = _copyForeignKeyCacheInfo(from); + break; + + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); + retval = 0; /* keep compiler quiet */ + break; + } + + return retval; +} diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c new file mode 100644 index 0000000..e8b6976 --- /dev/null +++ b/src/backend/nodes/equalfuncs.c @@ -0,0 +1,4001 @@ +/*------------------------------------------------------------------------- + * + * equalfuncs.c + * Equality functions to compare node trees. + * + * NOTE: we currently support comparing all node types found in parse + * trees. We do not support comparing executor state trees; there + * is no need for that, and no point in maintaining all the code that + * would be needed. We also do not support comparing Path trees, mainly + * because the circular linkages between RelOptInfo and Path nodes can't + * be handled easily in a simple depth-first traversal. + * + * Currently, in fact, equal() doesn't know how to compare Plan trees + * either. This might need to be fixed someday. + * + * NOTE: it is intentional that parse location fields (in nodes that have + * one) are not compared. This is because we want, for example, a variable + * "x" to be considered equal() to another reference to "x" in the query. + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/nodes/equalfuncs.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "miscadmin.h" +#include "nodes/extensible.h" +#include "nodes/pathnodes.h" +#include "utils/datum.h" + + +/* + * Macros to simplify comparison of different kinds of fields. Use these + * wherever possible to reduce the chance for silly typos. Note that these + * hard-wire the convention that the local variables in an Equal routine are + * named 'a' and 'b'. + */ + +/* Compare a simple scalar field (int, float, bool, enum, etc) */ +#define COMPARE_SCALAR_FIELD(fldname) \ + do { \ + if (a->fldname != b->fldname) \ + return false; \ + } while (0) + +/* Compare a field that is a pointer to some kind of Node or Node tree */ +#define COMPARE_NODE_FIELD(fldname) \ + do { \ + if (!equal(a->fldname, b->fldname)) \ + return false; \ + } while (0) + +/* Compare a field that is a pointer to a Bitmapset */ +#define COMPARE_BITMAPSET_FIELD(fldname) \ + do { \ + if (!bms_equal(a->fldname, b->fldname)) \ + return false; \ + } while (0) + +/* Compare a field that is a pointer to a C string, or perhaps NULL */ +#define COMPARE_STRING_FIELD(fldname) \ + do { \ + if (!equalstr(a->fldname, b->fldname)) \ + return false; \ + } while (0) + +/* Macro for comparing string fields that might be NULL */ +#define equalstr(a, b) \ + (((a) != NULL && (b) != NULL) ? (strcmp(a, b) == 0) : (a) == (b)) + +/* Compare a field that is an inline array */ +#define COMPARE_ARRAY_FIELD(fldname) \ + do { \ + if (memcmp(a->fldname, b->fldname, sizeof(a->fldname)) != 0) \ + return false; \ + } while (0) + +/* Compare a field that is a pointer to a simple palloc'd object of size sz */ +#define COMPARE_POINTER_FIELD(fldname, sz) \ + do { \ + if (memcmp(a->fldname, b->fldname, (sz)) != 0) \ + return false; \ + } while (0) + +/* Compare a parse location field (this is a no-op, per note above) */ +#define COMPARE_LOCATION_FIELD(fldname) \ + ((void) 0) + +/* Compare a CoercionForm field (also a no-op, per comment in primnodes.h) */ +#define COMPARE_COERCIONFORM_FIELD(fldname) \ + ((void) 0) + + +/* + * Stuff from primnodes.h + */ + +static bool +_equalAlias(const Alias *a, const Alias *b) +{ + COMPARE_STRING_FIELD(aliasname); + COMPARE_NODE_FIELD(colnames); + + return true; +} + +static bool +_equalRangeVar(const RangeVar *a, const RangeVar *b) +{ + COMPARE_STRING_FIELD(catalogname); + COMPARE_STRING_FIELD(schemaname); + COMPARE_STRING_FIELD(relname); + COMPARE_SCALAR_FIELD(inh); + COMPARE_SCALAR_FIELD(relpersistence); + COMPARE_NODE_FIELD(alias); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalTableFunc(const TableFunc *a, const TableFunc *b) +{ + COMPARE_NODE_FIELD(ns_uris); + COMPARE_NODE_FIELD(ns_names); + COMPARE_NODE_FIELD(docexpr); + COMPARE_NODE_FIELD(rowexpr); + COMPARE_NODE_FIELD(colnames); + COMPARE_NODE_FIELD(coltypes); + COMPARE_NODE_FIELD(coltypmods); + COMPARE_NODE_FIELD(colcollations); + COMPARE_NODE_FIELD(colexprs); + COMPARE_NODE_FIELD(coldefexprs); + COMPARE_BITMAPSET_FIELD(notnulls); + COMPARE_SCALAR_FIELD(ordinalitycol); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalIntoClause(const IntoClause *a, const IntoClause *b) +{ + COMPARE_NODE_FIELD(rel); + COMPARE_NODE_FIELD(colNames); + COMPARE_STRING_FIELD(accessMethod); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(onCommit); + COMPARE_STRING_FIELD(tableSpaceName); + COMPARE_NODE_FIELD(viewQuery); + COMPARE_SCALAR_FIELD(skipData); + + return true; +} + +/* + * We don't need an _equalExpr because Expr is an abstract supertype which + * should never actually get instantiated. Also, since it has no common + * fields except NodeTag, there's no need for a helper routine to factor + * out comparing the common fields... + */ + +static bool +_equalVar(const Var *a, const Var *b) +{ + COMPARE_SCALAR_FIELD(varno); + COMPARE_SCALAR_FIELD(varattno); + COMPARE_SCALAR_FIELD(vartype); + COMPARE_SCALAR_FIELD(vartypmod); + COMPARE_SCALAR_FIELD(varcollid); + COMPARE_SCALAR_FIELD(varlevelsup); + + /* + * varnosyn/varattnosyn are intentionally ignored here, because Vars with + * different syntactic identifiers are semantically the same as long as + * their varno/varattno match. + */ + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalConst(const Const *a, const Const *b) +{ + COMPARE_SCALAR_FIELD(consttype); + COMPARE_SCALAR_FIELD(consttypmod); + COMPARE_SCALAR_FIELD(constcollid); + COMPARE_SCALAR_FIELD(constlen); + COMPARE_SCALAR_FIELD(constisnull); + COMPARE_SCALAR_FIELD(constbyval); + COMPARE_LOCATION_FIELD(location); + + /* + * We treat all NULL constants of the same type as equal. Someday this + * might need to change? But datumIsEqual doesn't work on nulls, so... + */ + if (a->constisnull) + return true; + return datumIsEqual(a->constvalue, b->constvalue, + a->constbyval, a->constlen); +} + +static bool +_equalParam(const Param *a, const Param *b) +{ + COMPARE_SCALAR_FIELD(paramkind); + COMPARE_SCALAR_FIELD(paramid); + COMPARE_SCALAR_FIELD(paramtype); + COMPARE_SCALAR_FIELD(paramtypmod); + COMPARE_SCALAR_FIELD(paramcollid); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalAggref(const Aggref *a, const Aggref *b) +{ + COMPARE_SCALAR_FIELD(aggfnoid); + COMPARE_SCALAR_FIELD(aggtype); + COMPARE_SCALAR_FIELD(aggcollid); + COMPARE_SCALAR_FIELD(inputcollid); + /* ignore aggtranstype since it might not be set yet */ + COMPARE_NODE_FIELD(aggargtypes); + COMPARE_NODE_FIELD(aggdirectargs); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(aggorder); + COMPARE_NODE_FIELD(aggdistinct); + COMPARE_NODE_FIELD(aggfilter); + COMPARE_SCALAR_FIELD(aggstar); + COMPARE_SCALAR_FIELD(aggvariadic); + COMPARE_SCALAR_FIELD(aggkind); + COMPARE_SCALAR_FIELD(agglevelsup); + COMPARE_SCALAR_FIELD(aggsplit); + COMPARE_SCALAR_FIELD(aggno); + COMPARE_SCALAR_FIELD(aggtransno); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalGroupingFunc(const GroupingFunc *a, const GroupingFunc *b) +{ + COMPARE_NODE_FIELD(args); + + /* + * We must not compare the refs or cols field + */ + + COMPARE_SCALAR_FIELD(agglevelsup); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalWindowFunc(const WindowFunc *a, const WindowFunc *b) +{ + COMPARE_SCALAR_FIELD(winfnoid); + COMPARE_SCALAR_FIELD(wintype); + COMPARE_SCALAR_FIELD(wincollid); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(aggfilter); + COMPARE_SCALAR_FIELD(winref); + COMPARE_SCALAR_FIELD(winstar); + COMPARE_SCALAR_FIELD(winagg); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b) +{ + COMPARE_SCALAR_FIELD(refcontainertype); + COMPARE_SCALAR_FIELD(refelemtype); + COMPARE_SCALAR_FIELD(refrestype); + COMPARE_SCALAR_FIELD(reftypmod); + COMPARE_SCALAR_FIELD(refcollid); + COMPARE_NODE_FIELD(refupperindexpr); + COMPARE_NODE_FIELD(reflowerindexpr); + COMPARE_NODE_FIELD(refexpr); + COMPARE_NODE_FIELD(refassgnexpr); + + return true; +} + +static bool +_equalFuncExpr(const FuncExpr *a, const FuncExpr *b) +{ + COMPARE_SCALAR_FIELD(funcid); + COMPARE_SCALAR_FIELD(funcresulttype); + COMPARE_SCALAR_FIELD(funcretset); + COMPARE_SCALAR_FIELD(funcvariadic); + COMPARE_COERCIONFORM_FIELD(funcformat); + COMPARE_SCALAR_FIELD(funccollid); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalNamedArgExpr(const NamedArgExpr *a, const NamedArgExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_STRING_FIELD(name); + COMPARE_SCALAR_FIELD(argnumber); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalOpExpr(const OpExpr *a, const OpExpr *b) +{ + COMPARE_SCALAR_FIELD(opno); + + /* + * Special-case opfuncid: it is allowable for it to differ if one node + * contains zero and the other doesn't. This just means that the one node + * isn't as far along in the parse/plan pipeline and hasn't had the + * opfuncid cache filled yet. + */ + if (a->opfuncid != b->opfuncid && + a->opfuncid != 0 && + b->opfuncid != 0) + return false; + + COMPARE_SCALAR_FIELD(opresulttype); + COMPARE_SCALAR_FIELD(opretset); + COMPARE_SCALAR_FIELD(opcollid); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalDistinctExpr(const DistinctExpr *a, const DistinctExpr *b) +{ + COMPARE_SCALAR_FIELD(opno); + + /* + * Special-case opfuncid: it is allowable for it to differ if one node + * contains zero and the other doesn't. This just means that the one node + * isn't as far along in the parse/plan pipeline and hasn't had the + * opfuncid cache filled yet. + */ + if (a->opfuncid != b->opfuncid && + a->opfuncid != 0 && + b->opfuncid != 0) + return false; + + COMPARE_SCALAR_FIELD(opresulttype); + COMPARE_SCALAR_FIELD(opretset); + COMPARE_SCALAR_FIELD(opcollid); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalNullIfExpr(const NullIfExpr *a, const NullIfExpr *b) +{ + COMPARE_SCALAR_FIELD(opno); + + /* + * Special-case opfuncid: it is allowable for it to differ if one node + * contains zero and the other doesn't. This just means that the one node + * isn't as far along in the parse/plan pipeline and hasn't had the + * opfuncid cache filled yet. + */ + if (a->opfuncid != b->opfuncid && + a->opfuncid != 0 && + b->opfuncid != 0) + return false; + + COMPARE_SCALAR_FIELD(opresulttype); + COMPARE_SCALAR_FIELD(opretset); + COMPARE_SCALAR_FIELD(opcollid); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalScalarArrayOpExpr(const ScalarArrayOpExpr *a, const ScalarArrayOpExpr *b) +{ + COMPARE_SCALAR_FIELD(opno); + + /* + * Special-case opfuncid: it is allowable for it to differ if one node + * contains zero and the other doesn't. This just means that the one node + * isn't as far along in the parse/plan pipeline and hasn't had the + * opfuncid cache filled yet. + */ + if (a->opfuncid != b->opfuncid && + a->opfuncid != 0 && + b->opfuncid != 0) + return false; + + /* As above, hashfuncid may differ too */ + if (a->hashfuncid != b->hashfuncid && + a->hashfuncid != 0 && + b->hashfuncid != 0) + return false; + + /* Likewise for the negfuncid */ + if (a->negfuncid != b->negfuncid && + a->negfuncid != 0 && + b->negfuncid != 0) + return false; + + COMPARE_SCALAR_FIELD(useOr); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalBoolExpr(const BoolExpr *a, const BoolExpr *b) +{ + COMPARE_SCALAR_FIELD(boolop); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalSubLink(const SubLink *a, const SubLink *b) +{ + COMPARE_SCALAR_FIELD(subLinkType); + COMPARE_SCALAR_FIELD(subLinkId); + COMPARE_NODE_FIELD(testexpr); + COMPARE_NODE_FIELD(operName); + COMPARE_NODE_FIELD(subselect); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalSubPlan(const SubPlan *a, const SubPlan *b) +{ + COMPARE_SCALAR_FIELD(subLinkType); + COMPARE_NODE_FIELD(testexpr); + COMPARE_NODE_FIELD(paramIds); + COMPARE_SCALAR_FIELD(plan_id); + COMPARE_STRING_FIELD(plan_name); + COMPARE_SCALAR_FIELD(firstColType); + COMPARE_SCALAR_FIELD(firstColTypmod); + COMPARE_SCALAR_FIELD(firstColCollation); + COMPARE_SCALAR_FIELD(useHashTable); + COMPARE_SCALAR_FIELD(unknownEqFalse); + COMPARE_SCALAR_FIELD(parallel_safe); + COMPARE_NODE_FIELD(setParam); + COMPARE_NODE_FIELD(parParam); + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(startup_cost); + COMPARE_SCALAR_FIELD(per_call_cost); + + return true; +} + +static bool +_equalAlternativeSubPlan(const AlternativeSubPlan *a, const AlternativeSubPlan *b) +{ + COMPARE_NODE_FIELD(subplans); + + return true; +} + +static bool +_equalFieldSelect(const FieldSelect *a, const FieldSelect *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(fieldnum); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_SCALAR_FIELD(resulttypmod); + COMPARE_SCALAR_FIELD(resultcollid); + + return true; +} + +static bool +_equalFieldStore(const FieldStore *a, const FieldStore *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(newvals); + COMPARE_NODE_FIELD(fieldnums); + COMPARE_SCALAR_FIELD(resulttype); + + return true; +} + +static bool +_equalRelabelType(const RelabelType *a, const RelabelType *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_SCALAR_FIELD(resulttypmod); + COMPARE_SCALAR_FIELD(resultcollid); + COMPARE_COERCIONFORM_FIELD(relabelformat); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCoerceViaIO(const CoerceViaIO *a, const CoerceViaIO *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_SCALAR_FIELD(resultcollid); + COMPARE_COERCIONFORM_FIELD(coerceformat); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalArrayCoerceExpr(const ArrayCoerceExpr *a, const ArrayCoerceExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(elemexpr); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_SCALAR_FIELD(resulttypmod); + COMPARE_SCALAR_FIELD(resultcollid); + COMPARE_COERCIONFORM_FIELD(coerceformat); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalConvertRowtypeExpr(const ConvertRowtypeExpr *a, const ConvertRowtypeExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_COERCIONFORM_FIELD(convertformat); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCollateExpr(const CollateExpr *a, const CollateExpr *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(collOid); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCaseExpr(const CaseExpr *a, const CaseExpr *b) +{ + COMPARE_SCALAR_FIELD(casetype); + COMPARE_SCALAR_FIELD(casecollid); + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(defresult); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCaseWhen(const CaseWhen *a, const CaseWhen *b) +{ + COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(result); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCaseTestExpr(const CaseTestExpr *a, const CaseTestExpr *b) +{ + COMPARE_SCALAR_FIELD(typeId); + COMPARE_SCALAR_FIELD(typeMod); + COMPARE_SCALAR_FIELD(collation); + + return true; +} + +static bool +_equalArrayExpr(const ArrayExpr *a, const ArrayExpr *b) +{ + COMPARE_SCALAR_FIELD(array_typeid); + COMPARE_SCALAR_FIELD(array_collid); + COMPARE_SCALAR_FIELD(element_typeid); + COMPARE_NODE_FIELD(elements); + COMPARE_SCALAR_FIELD(multidims); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalRowExpr(const RowExpr *a, const RowExpr *b) +{ + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(row_typeid); + COMPARE_COERCIONFORM_FIELD(row_format); + COMPARE_NODE_FIELD(colnames); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalRowCompareExpr(const RowCompareExpr *a, const RowCompareExpr *b) +{ + COMPARE_SCALAR_FIELD(rctype); + COMPARE_NODE_FIELD(opnos); + COMPARE_NODE_FIELD(opfamilies); + COMPARE_NODE_FIELD(inputcollids); + COMPARE_NODE_FIELD(largs); + COMPARE_NODE_FIELD(rargs); + + return true; +} + +static bool +_equalCoalesceExpr(const CoalesceExpr *a, const CoalesceExpr *b) +{ + COMPARE_SCALAR_FIELD(coalescetype); + COMPARE_SCALAR_FIELD(coalescecollid); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalMinMaxExpr(const MinMaxExpr *a, const MinMaxExpr *b) +{ + COMPARE_SCALAR_FIELD(minmaxtype); + COMPARE_SCALAR_FIELD(minmaxcollid); + COMPARE_SCALAR_FIELD(inputcollid); + COMPARE_SCALAR_FIELD(op); + COMPARE_NODE_FIELD(args); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalSQLValueFunction(const SQLValueFunction *a, const SQLValueFunction *b) +{ + COMPARE_SCALAR_FIELD(op); + COMPARE_SCALAR_FIELD(type); + COMPARE_SCALAR_FIELD(typmod); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalXmlExpr(const XmlExpr *a, const XmlExpr *b) +{ + COMPARE_SCALAR_FIELD(op); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(named_args); + COMPARE_NODE_FIELD(arg_names); + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(xmloption); + COMPARE_SCALAR_FIELD(type); + COMPARE_SCALAR_FIELD(typmod); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalNullTest(const NullTest *a, const NullTest *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(nulltesttype); + COMPARE_SCALAR_FIELD(argisrow); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalBooleanTest(const BooleanTest *a, const BooleanTest *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(booltesttype); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCoerceToDomain(const CoerceToDomain *a, const CoerceToDomain *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(resulttype); + COMPARE_SCALAR_FIELD(resulttypmod); + COMPARE_SCALAR_FIELD(resultcollid); + COMPARE_COERCIONFORM_FIELD(coercionformat); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCoerceToDomainValue(const CoerceToDomainValue *a, const CoerceToDomainValue *b) +{ + COMPARE_SCALAR_FIELD(typeId); + COMPARE_SCALAR_FIELD(typeMod); + COMPARE_SCALAR_FIELD(collation); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalSetToDefault(const SetToDefault *a, const SetToDefault *b) +{ + COMPARE_SCALAR_FIELD(typeId); + COMPARE_SCALAR_FIELD(typeMod); + COMPARE_SCALAR_FIELD(collation); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCurrentOfExpr(const CurrentOfExpr *a, const CurrentOfExpr *b) +{ + COMPARE_SCALAR_FIELD(cvarno); + COMPARE_STRING_FIELD(cursor_name); + COMPARE_SCALAR_FIELD(cursor_param); + + return true; +} + +static bool +_equalNextValueExpr(const NextValueExpr *a, const NextValueExpr *b) +{ + COMPARE_SCALAR_FIELD(seqid); + COMPARE_SCALAR_FIELD(typeId); + + return true; +} + +static bool +_equalInferenceElem(const InferenceElem *a, const InferenceElem *b) +{ + COMPARE_NODE_FIELD(expr); + COMPARE_SCALAR_FIELD(infercollid); + COMPARE_SCALAR_FIELD(inferopclass); + + return true; +} + +static bool +_equalTargetEntry(const TargetEntry *a, const TargetEntry *b) +{ + COMPARE_NODE_FIELD(expr); + COMPARE_SCALAR_FIELD(resno); + COMPARE_STRING_FIELD(resname); + COMPARE_SCALAR_FIELD(ressortgroupref); + COMPARE_SCALAR_FIELD(resorigtbl); + COMPARE_SCALAR_FIELD(resorigcol); + COMPARE_SCALAR_FIELD(resjunk); + + return true; +} + +static bool +_equalRangeTblRef(const RangeTblRef *a, const RangeTblRef *b) +{ + COMPARE_SCALAR_FIELD(rtindex); + + return true; +} + +static bool +_equalJoinExpr(const JoinExpr *a, const JoinExpr *b) +{ + COMPARE_SCALAR_FIELD(jointype); + COMPARE_SCALAR_FIELD(isNatural); + COMPARE_NODE_FIELD(larg); + COMPARE_NODE_FIELD(rarg); + COMPARE_NODE_FIELD(usingClause); + COMPARE_NODE_FIELD(join_using_alias); + COMPARE_NODE_FIELD(quals); + COMPARE_NODE_FIELD(alias); + COMPARE_SCALAR_FIELD(rtindex); + + return true; +} + +static bool +_equalFromExpr(const FromExpr *a, const FromExpr *b) +{ + COMPARE_NODE_FIELD(fromlist); + COMPARE_NODE_FIELD(quals); + + return true; +} + +static bool +_equalOnConflictExpr(const OnConflictExpr *a, const OnConflictExpr *b) +{ + COMPARE_SCALAR_FIELD(action); + COMPARE_NODE_FIELD(arbiterElems); + COMPARE_NODE_FIELD(arbiterWhere); + COMPARE_SCALAR_FIELD(constraint); + COMPARE_NODE_FIELD(onConflictSet); + COMPARE_NODE_FIELD(onConflictWhere); + COMPARE_SCALAR_FIELD(exclRelIndex); + COMPARE_NODE_FIELD(exclRelTlist); + + return true; +} + +/* + * Stuff from pathnodes.h + */ + +static bool +_equalPathKey(const PathKey *a, const PathKey *b) +{ + /* We assume pointer equality is sufficient to compare the eclasses */ + COMPARE_SCALAR_FIELD(pk_eclass); + COMPARE_SCALAR_FIELD(pk_opfamily); + COMPARE_SCALAR_FIELD(pk_strategy); + COMPARE_SCALAR_FIELD(pk_nulls_first); + + return true; +} + +static bool +_equalRestrictInfo(const RestrictInfo *a, const RestrictInfo *b) +{ + COMPARE_NODE_FIELD(clause); + COMPARE_SCALAR_FIELD(is_pushed_down); + COMPARE_SCALAR_FIELD(outerjoin_delayed); + COMPARE_SCALAR_FIELD(security_level); + COMPARE_BITMAPSET_FIELD(required_relids); + COMPARE_BITMAPSET_FIELD(outer_relids); + COMPARE_BITMAPSET_FIELD(nullable_relids); + + /* + * We ignore all the remaining fields, since they may not be set yet, and + * should be derivable from the clause anyway. + */ + + return true; +} + +static bool +_equalPlaceHolderVar(const PlaceHolderVar *a, const PlaceHolderVar *b) +{ + /* + * We intentionally do not compare phexpr. Two PlaceHolderVars with the + * same ID and levelsup should be considered equal even if the contained + * expressions have managed to mutate to different states. This will + * happen during final plan construction when there are nested PHVs, since + * the inner PHV will get replaced by a Param in some copies of the outer + * PHV. Another way in which it can happen is that initplan sublinks + * could get replaced by differently-numbered Params when sublink folding + * is done. (The end result of such a situation would be some + * unreferenced initplans, which is annoying but not really a problem.) On + * the same reasoning, there is no need to examine phrels. + * + * COMPARE_NODE_FIELD(phexpr); + * + * COMPARE_BITMAPSET_FIELD(phrels); + */ + COMPARE_SCALAR_FIELD(phid); + COMPARE_SCALAR_FIELD(phlevelsup); + + return true; +} + +static bool +_equalSpecialJoinInfo(const SpecialJoinInfo *a, const SpecialJoinInfo *b) +{ + COMPARE_BITMAPSET_FIELD(min_lefthand); + COMPARE_BITMAPSET_FIELD(min_righthand); + COMPARE_BITMAPSET_FIELD(syn_lefthand); + COMPARE_BITMAPSET_FIELD(syn_righthand); + COMPARE_SCALAR_FIELD(jointype); + COMPARE_SCALAR_FIELD(lhs_strict); + COMPARE_SCALAR_FIELD(delay_upper_joins); + COMPARE_SCALAR_FIELD(semi_can_btree); + COMPARE_SCALAR_FIELD(semi_can_hash); + COMPARE_NODE_FIELD(semi_operators); + COMPARE_NODE_FIELD(semi_rhs_exprs); + + return true; +} + +static bool +_equalAppendRelInfo(const AppendRelInfo *a, const AppendRelInfo *b) +{ + COMPARE_SCALAR_FIELD(parent_relid); + COMPARE_SCALAR_FIELD(child_relid); + COMPARE_SCALAR_FIELD(parent_reltype); + COMPARE_SCALAR_FIELD(child_reltype); + COMPARE_NODE_FIELD(translated_vars); + COMPARE_SCALAR_FIELD(num_child_cols); + COMPARE_POINTER_FIELD(parent_colnos, a->num_child_cols * sizeof(AttrNumber)); + COMPARE_SCALAR_FIELD(parent_reloid); + + return true; +} + +static bool +_equalPlaceHolderInfo(const PlaceHolderInfo *a, const PlaceHolderInfo *b) +{ + COMPARE_SCALAR_FIELD(phid); + COMPARE_NODE_FIELD(ph_var); /* should be redundant */ + COMPARE_BITMAPSET_FIELD(ph_eval_at); + COMPARE_BITMAPSET_FIELD(ph_lateral); + COMPARE_BITMAPSET_FIELD(ph_needed); + COMPARE_SCALAR_FIELD(ph_width); + + return true; +} + +/* + * Stuff from extensible.h + */ +static bool +_equalExtensibleNode(const ExtensibleNode *a, const ExtensibleNode *b) +{ + const ExtensibleNodeMethods *methods; + + COMPARE_STRING_FIELD(extnodename); + + /* At this point, we know extnodename is the same for both nodes. */ + methods = GetExtensibleNodeMethods(a->extnodename, false); + + /* compare the private fields */ + if (!methods->nodeEqual(a, b)) + return false; + + return true; +} + +/* + * Stuff from parsenodes.h + */ + +static bool +_equalQuery(const Query *a, const Query *b) +{ + COMPARE_SCALAR_FIELD(commandType); + COMPARE_SCALAR_FIELD(querySource); + /* we intentionally ignore queryId, since it might not be set */ + COMPARE_SCALAR_FIELD(canSetTag); + COMPARE_NODE_FIELD(utilityStmt); + COMPARE_SCALAR_FIELD(resultRelation); + COMPARE_SCALAR_FIELD(hasAggs); + COMPARE_SCALAR_FIELD(hasWindowFuncs); + COMPARE_SCALAR_FIELD(hasTargetSRFs); + COMPARE_SCALAR_FIELD(hasSubLinks); + COMPARE_SCALAR_FIELD(hasDistinctOn); + COMPARE_SCALAR_FIELD(hasRecursive); + COMPARE_SCALAR_FIELD(hasModifyingCTE); + COMPARE_SCALAR_FIELD(hasForUpdate); + COMPARE_SCALAR_FIELD(hasRowSecurity); + COMPARE_SCALAR_FIELD(isReturn); + COMPARE_NODE_FIELD(cteList); + COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(jointree); + COMPARE_NODE_FIELD(targetList); + COMPARE_SCALAR_FIELD(override); + COMPARE_NODE_FIELD(onConflict); + COMPARE_NODE_FIELD(returningList); + COMPARE_NODE_FIELD(groupClause); + COMPARE_SCALAR_FIELD(groupDistinct); + COMPARE_NODE_FIELD(groupingSets); + COMPARE_NODE_FIELD(havingQual); + COMPARE_NODE_FIELD(windowClause); + COMPARE_NODE_FIELD(distinctClause); + COMPARE_NODE_FIELD(sortClause); + COMPARE_NODE_FIELD(limitOffset); + COMPARE_NODE_FIELD(limitCount); + COMPARE_SCALAR_FIELD(limitOption); + COMPARE_NODE_FIELD(rowMarks); + COMPARE_NODE_FIELD(setOperations); + COMPARE_NODE_FIELD(constraintDeps); + COMPARE_NODE_FIELD(withCheckOptions); + COMPARE_NODE_FIELD(mergeActionList); + COMPARE_SCALAR_FIELD(mergeUseOuterJoin); + COMPARE_LOCATION_FIELD(stmt_location); + COMPARE_SCALAR_FIELD(stmt_len); + + return true; +} + +static bool +_equalRawStmt(const RawStmt *a, const RawStmt *b) +{ + COMPARE_NODE_FIELD(stmt); + COMPARE_LOCATION_FIELD(stmt_location); + COMPARE_SCALAR_FIELD(stmt_len); + + return true; +} + +static bool +_equalInsertStmt(const InsertStmt *a, const InsertStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(cols); + COMPARE_NODE_FIELD(selectStmt); + COMPARE_NODE_FIELD(onConflictClause); + COMPARE_NODE_FIELD(returningList); + COMPARE_NODE_FIELD(withClause); + COMPARE_SCALAR_FIELD(override); + + return true; +} + +static bool +_equalDeleteStmt(const DeleteStmt *a, const DeleteStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(usingClause); + COMPARE_NODE_FIELD(whereClause); + COMPARE_NODE_FIELD(returningList); + COMPARE_NODE_FIELD(withClause); + + return true; +} + +static bool +_equalUpdateStmt(const UpdateStmt *a, const UpdateStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(targetList); + COMPARE_NODE_FIELD(whereClause); + COMPARE_NODE_FIELD(fromClause); + COMPARE_NODE_FIELD(returningList); + COMPARE_NODE_FIELD(withClause); + + return true; +} + +static bool +_equalMergeStmt(const MergeStmt *a, const MergeStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(sourceRelation); + COMPARE_NODE_FIELD(joinCondition); + COMPARE_NODE_FIELD(mergeWhenClauses); + COMPARE_NODE_FIELD(withClause); + + return true; +} + +static bool +_equalSelectStmt(const SelectStmt *a, const SelectStmt *b) +{ + COMPARE_NODE_FIELD(distinctClause); + COMPARE_NODE_FIELD(intoClause); + COMPARE_NODE_FIELD(targetList); + COMPARE_NODE_FIELD(fromClause); + COMPARE_NODE_FIELD(whereClause); + COMPARE_NODE_FIELD(groupClause); + COMPARE_SCALAR_FIELD(groupDistinct); + COMPARE_NODE_FIELD(havingClause); + COMPARE_NODE_FIELD(windowClause); + COMPARE_NODE_FIELD(valuesLists); + COMPARE_NODE_FIELD(sortClause); + COMPARE_NODE_FIELD(limitOffset); + COMPARE_NODE_FIELD(limitCount); + COMPARE_SCALAR_FIELD(limitOption); + COMPARE_NODE_FIELD(lockingClause); + COMPARE_NODE_FIELD(withClause); + COMPARE_SCALAR_FIELD(op); + COMPARE_SCALAR_FIELD(all); + COMPARE_NODE_FIELD(larg); + COMPARE_NODE_FIELD(rarg); + + return true; +} + +static bool +_equalSetOperationStmt(const SetOperationStmt *a, const SetOperationStmt *b) +{ + COMPARE_SCALAR_FIELD(op); + COMPARE_SCALAR_FIELD(all); + COMPARE_NODE_FIELD(larg); + COMPARE_NODE_FIELD(rarg); + COMPARE_NODE_FIELD(colTypes); + COMPARE_NODE_FIELD(colTypmods); + COMPARE_NODE_FIELD(colCollations); + COMPARE_NODE_FIELD(groupClauses); + + return true; +} + +static bool +_equalReturnStmt(const ReturnStmt *a, const ReturnStmt *b) +{ + COMPARE_NODE_FIELD(returnval); + + return true; +} + +static bool +_equalPLAssignStmt(const PLAssignStmt *a, const PLAssignStmt *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(indirection); + COMPARE_SCALAR_FIELD(nnames); + COMPARE_NODE_FIELD(val); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalAlterTableStmt(const AlterTableStmt *a, const AlterTableStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(cmds); + COMPARE_SCALAR_FIELD(objtype); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalAlterTableCmd(const AlterTableCmd *a, const AlterTableCmd *b) +{ + COMPARE_SCALAR_FIELD(subtype); + COMPARE_STRING_FIELD(name); + COMPARE_SCALAR_FIELD(num); + COMPARE_NODE_FIELD(newowner); + COMPARE_NODE_FIELD(def); + COMPARE_SCALAR_FIELD(behavior); + COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_SCALAR_FIELD(recurse); + + return true; +} + +static bool +_equalAlterCollationStmt(const AlterCollationStmt *a, const AlterCollationStmt *b) +{ + COMPARE_NODE_FIELD(collname); + + return true; +} + +static bool +_equalAlterDomainStmt(const AlterDomainStmt *a, const AlterDomainStmt *b) +{ + COMPARE_SCALAR_FIELD(subtype); + COMPARE_NODE_FIELD(typeName); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(def); + COMPARE_SCALAR_FIELD(behavior); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalGrantStmt(const GrantStmt *a, const GrantStmt *b) +{ + COMPARE_SCALAR_FIELD(is_grant); + COMPARE_SCALAR_FIELD(targtype); + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(objects); + COMPARE_NODE_FIELD(privileges); + COMPARE_NODE_FIELD(grantees); + COMPARE_SCALAR_FIELD(grant_option); + COMPARE_NODE_FIELD(grantor); + COMPARE_SCALAR_FIELD(behavior); + + return true; +} + +static bool +_equalObjectWithArgs(const ObjectWithArgs *a, const ObjectWithArgs *b) +{ + COMPARE_NODE_FIELD(objname); + COMPARE_NODE_FIELD(objargs); + COMPARE_NODE_FIELD(objfuncargs); + COMPARE_SCALAR_FIELD(args_unspecified); + + return true; +} + +static bool +_equalAccessPriv(const AccessPriv *a, const AccessPriv *b) +{ + COMPARE_STRING_FIELD(priv_name); + COMPARE_NODE_FIELD(cols); + + return true; +} + +static bool +_equalGrantRoleStmt(const GrantRoleStmt *a, const GrantRoleStmt *b) +{ + COMPARE_NODE_FIELD(granted_roles); + COMPARE_NODE_FIELD(grantee_roles); + COMPARE_SCALAR_FIELD(is_grant); + COMPARE_SCALAR_FIELD(admin_opt); + COMPARE_NODE_FIELD(grantor); + COMPARE_SCALAR_FIELD(behavior); + + return true; +} + +static bool +_equalAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *a, const AlterDefaultPrivilegesStmt *b) +{ + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(action); + + return true; +} + +static bool +_equalDeclareCursorStmt(const DeclareCursorStmt *a, const DeclareCursorStmt *b) +{ + COMPARE_STRING_FIELD(portalname); + COMPARE_SCALAR_FIELD(options); + COMPARE_NODE_FIELD(query); + + return true; +} + +static bool +_equalClosePortalStmt(const ClosePortalStmt *a, const ClosePortalStmt *b) +{ + COMPARE_STRING_FIELD(portalname); + + return true; +} + +static bool +_equalCallStmt(const CallStmt *a, const CallStmt *b) +{ + COMPARE_NODE_FIELD(funccall); + COMPARE_NODE_FIELD(funcexpr); + COMPARE_NODE_FIELD(outargs); + + return true; +} + +static bool +_equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(indexname); + COMPARE_NODE_FIELD(params); + + return true; +} + +static bool +_equalCopyStmt(const CopyStmt *a, const CopyStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(query); + COMPARE_NODE_FIELD(attlist); + COMPARE_SCALAR_FIELD(is_from); + COMPARE_SCALAR_FIELD(is_program); + COMPARE_STRING_FIELD(filename); + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(whereClause); + + return true; +} + +static bool +_equalCreateStmt(const CreateStmt *a, const CreateStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(tableElts); + COMPARE_NODE_FIELD(inhRelations); + COMPARE_NODE_FIELD(partbound); + COMPARE_NODE_FIELD(partspec); + COMPARE_NODE_FIELD(ofTypename); + COMPARE_NODE_FIELD(constraints); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(oncommit); + COMPARE_STRING_FIELD(tablespacename); + COMPARE_STRING_FIELD(accessMethod); + COMPARE_SCALAR_FIELD(if_not_exists); + + return true; +} + +static bool +_equalTableLikeClause(const TableLikeClause *a, const TableLikeClause *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_SCALAR_FIELD(options); + COMPARE_SCALAR_FIELD(relationOid); + + return true; +} + +static bool +_equalDefineStmt(const DefineStmt *a, const DefineStmt *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_SCALAR_FIELD(oldstyle); + COMPARE_NODE_FIELD(defnames); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(definition); + COMPARE_SCALAR_FIELD(if_not_exists); + COMPARE_SCALAR_FIELD(replace); + + return true; +} + +static bool +_equalDropStmt(const DropStmt *a, const DropStmt *b) +{ + COMPARE_NODE_FIELD(objects); + COMPARE_SCALAR_FIELD(removeType); + COMPARE_SCALAR_FIELD(behavior); + COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_SCALAR_FIELD(concurrent); + + return true; +} + +static bool +_equalTruncateStmt(const TruncateStmt *a, const TruncateStmt *b) +{ + COMPARE_NODE_FIELD(relations); + COMPARE_SCALAR_FIELD(restart_seqs); + COMPARE_SCALAR_FIELD(behavior); + + return true; +} + +static bool +_equalCommentStmt(const CommentStmt *a, const CommentStmt *b) +{ + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(object); + COMPARE_STRING_FIELD(comment); + + return true; +} + +static bool +_equalSecLabelStmt(const SecLabelStmt *a, const SecLabelStmt *b) +{ + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(object); + COMPARE_STRING_FIELD(provider); + COMPARE_STRING_FIELD(label); + + return true; +} + +static bool +_equalFetchStmt(const FetchStmt *a, const FetchStmt *b) +{ + COMPARE_SCALAR_FIELD(direction); + COMPARE_SCALAR_FIELD(howMany); + COMPARE_STRING_FIELD(portalname); + COMPARE_SCALAR_FIELD(ismove); + + return true; +} + +static bool +_equalIndexStmt(const IndexStmt *a, const IndexStmt *b) +{ + COMPARE_STRING_FIELD(idxname); + COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(accessMethod); + COMPARE_STRING_FIELD(tableSpace); + COMPARE_NODE_FIELD(indexParams); + COMPARE_NODE_FIELD(indexIncludingParams); + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(whereClause); + COMPARE_NODE_FIELD(excludeOpNames); + COMPARE_STRING_FIELD(idxcomment); + COMPARE_SCALAR_FIELD(indexOid); + COMPARE_SCALAR_FIELD(oldNode); + COMPARE_SCALAR_FIELD(oldCreateSubid); + COMPARE_SCALAR_FIELD(oldFirstRelfilenodeSubid); + COMPARE_SCALAR_FIELD(unique); + COMPARE_SCALAR_FIELD(nulls_not_distinct); + COMPARE_SCALAR_FIELD(primary); + COMPARE_SCALAR_FIELD(isconstraint); + COMPARE_SCALAR_FIELD(deferrable); + COMPARE_SCALAR_FIELD(initdeferred); + COMPARE_SCALAR_FIELD(transformed); + COMPARE_SCALAR_FIELD(concurrent); + COMPARE_SCALAR_FIELD(if_not_exists); + COMPARE_SCALAR_FIELD(reset_default_tblspc); + + return true; +} + +static bool +_equalCreateStatsStmt(const CreateStatsStmt *a, const CreateStatsStmt *b) +{ + COMPARE_NODE_FIELD(defnames); + COMPARE_NODE_FIELD(stat_types); + COMPARE_NODE_FIELD(exprs); + COMPARE_NODE_FIELD(relations); + COMPARE_STRING_FIELD(stxcomment); + COMPARE_SCALAR_FIELD(transformed); + COMPARE_SCALAR_FIELD(if_not_exists); + + return true; +} + +static bool +_equalAlterStatsStmt(const AlterStatsStmt *a, const AlterStatsStmt *b) +{ + COMPARE_NODE_FIELD(defnames); + COMPARE_SCALAR_FIELD(stxstattarget); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalCreateFunctionStmt(const CreateFunctionStmt *a, const CreateFunctionStmt *b) +{ + COMPARE_SCALAR_FIELD(is_procedure); + COMPARE_SCALAR_FIELD(replace); + COMPARE_NODE_FIELD(funcname); + COMPARE_NODE_FIELD(parameters); + COMPARE_NODE_FIELD(returnType); + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(sql_body); + + return true; +} + +static bool +_equalFunctionParameter(const FunctionParameter *a, const FunctionParameter *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(argType); + COMPARE_SCALAR_FIELD(mode); + COMPARE_NODE_FIELD(defexpr); + + return true; +} + +static bool +_equalAlterFunctionStmt(const AlterFunctionStmt *a, const AlterFunctionStmt *b) +{ + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(func); + COMPARE_NODE_FIELD(actions); + + return true; +} + +static bool +_equalDoStmt(const DoStmt *a, const DoStmt *b) +{ + COMPARE_NODE_FIELD(args); + + return true; +} + +static bool +_equalRenameStmt(const RenameStmt *a, const RenameStmt *b) +{ + COMPARE_SCALAR_FIELD(renameType); + COMPARE_SCALAR_FIELD(relationType); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(object); + COMPARE_STRING_FIELD(subname); + COMPARE_STRING_FIELD(newname); + COMPARE_SCALAR_FIELD(behavior); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalAlterObjectDependsStmt(const AlterObjectDependsStmt *a, const AlterObjectDependsStmt *b) +{ + COMPARE_SCALAR_FIELD(objectType); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(object); + COMPARE_NODE_FIELD(extname); + COMPARE_SCALAR_FIELD(remove); + + return true; +} + +static bool +_equalAlterObjectSchemaStmt(const AlterObjectSchemaStmt *a, const AlterObjectSchemaStmt *b) +{ + COMPARE_SCALAR_FIELD(objectType); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(object); + COMPARE_STRING_FIELD(newschema); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalAlterOwnerStmt(const AlterOwnerStmt *a, const AlterOwnerStmt *b) +{ + COMPARE_SCALAR_FIELD(objectType); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(object); + COMPARE_NODE_FIELD(newowner); + + return true; +} + +static bool +_equalAlterOperatorStmt(const AlterOperatorStmt *a, const AlterOperatorStmt *b) +{ + COMPARE_NODE_FIELD(opername); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterTypeStmt(const AlterTypeStmt *a, const AlterTypeStmt *b) +{ + COMPARE_NODE_FIELD(typeName); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalRuleStmt(const RuleStmt *a, const RuleStmt *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(rulename); + COMPARE_NODE_FIELD(whereClause); + COMPARE_SCALAR_FIELD(event); + COMPARE_SCALAR_FIELD(instead); + COMPARE_NODE_FIELD(actions); + COMPARE_SCALAR_FIELD(replace); + + return true; +} + +static bool +_equalNotifyStmt(const NotifyStmt *a, const NotifyStmt *b) +{ + COMPARE_STRING_FIELD(conditionname); + COMPARE_STRING_FIELD(payload); + + return true; +} + +static bool +_equalListenStmt(const ListenStmt *a, const ListenStmt *b) +{ + COMPARE_STRING_FIELD(conditionname); + + return true; +} + +static bool +_equalUnlistenStmt(const UnlistenStmt *a, const UnlistenStmt *b) +{ + COMPARE_STRING_FIELD(conditionname); + + return true; +} + +static bool +_equalTransactionStmt(const TransactionStmt *a, const TransactionStmt *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(options); + COMPARE_STRING_FIELD(savepoint_name); + COMPARE_STRING_FIELD(gid); + COMPARE_SCALAR_FIELD(chain); + + return true; +} + +static bool +_equalCompositeTypeStmt(const CompositeTypeStmt *a, const CompositeTypeStmt *b) +{ + COMPARE_NODE_FIELD(typevar); + COMPARE_NODE_FIELD(coldeflist); + + return true; +} + +static bool +_equalCreateEnumStmt(const CreateEnumStmt *a, const CreateEnumStmt *b) +{ + COMPARE_NODE_FIELD(typeName); + COMPARE_NODE_FIELD(vals); + + return true; +} + +static bool +_equalCreateRangeStmt(const CreateRangeStmt *a, const CreateRangeStmt *b) +{ + COMPARE_NODE_FIELD(typeName); + COMPARE_NODE_FIELD(params); + + return true; +} + +static bool +_equalAlterEnumStmt(const AlterEnumStmt *a, const AlterEnumStmt *b) +{ + COMPARE_NODE_FIELD(typeName); + COMPARE_STRING_FIELD(oldVal); + COMPARE_STRING_FIELD(newVal); + COMPARE_STRING_FIELD(newValNeighbor); + COMPARE_SCALAR_FIELD(newValIsAfter); + COMPARE_SCALAR_FIELD(skipIfNewValExists); + + return true; +} + +static bool +_equalViewStmt(const ViewStmt *a, const ViewStmt *b) +{ + COMPARE_NODE_FIELD(view); + COMPARE_NODE_FIELD(aliases); + COMPARE_NODE_FIELD(query); + COMPARE_SCALAR_FIELD(replace); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(withCheckOption); + + return true; +} + +static bool +_equalLoadStmt(const LoadStmt *a, const LoadStmt *b) +{ + COMPARE_STRING_FIELD(filename); + + return true; +} + +static bool +_equalCreateDomainStmt(const CreateDomainStmt *a, const CreateDomainStmt *b) +{ + COMPARE_NODE_FIELD(domainname); + COMPARE_NODE_FIELD(typeName); + COMPARE_NODE_FIELD(collClause); + COMPARE_NODE_FIELD(constraints); + + return true; +} + +static bool +_equalCreateOpClassStmt(const CreateOpClassStmt *a, const CreateOpClassStmt *b) +{ + COMPARE_NODE_FIELD(opclassname); + COMPARE_NODE_FIELD(opfamilyname); + COMPARE_STRING_FIELD(amname); + COMPARE_NODE_FIELD(datatype); + COMPARE_NODE_FIELD(items); + COMPARE_SCALAR_FIELD(isDefault); + + return true; +} + +static bool +_equalCreateOpClassItem(const CreateOpClassItem *a, const CreateOpClassItem *b) +{ + COMPARE_SCALAR_FIELD(itemtype); + COMPARE_NODE_FIELD(name); + COMPARE_SCALAR_FIELD(number); + COMPARE_NODE_FIELD(order_family); + COMPARE_NODE_FIELD(class_args); + COMPARE_NODE_FIELD(storedtype); + + return true; +} + +static bool +_equalCreateOpFamilyStmt(const CreateOpFamilyStmt *a, const CreateOpFamilyStmt *b) +{ + COMPARE_NODE_FIELD(opfamilyname); + COMPARE_STRING_FIELD(amname); + + return true; +} + +static bool +_equalAlterOpFamilyStmt(const AlterOpFamilyStmt *a, const AlterOpFamilyStmt *b) +{ + COMPARE_NODE_FIELD(opfamilyname); + COMPARE_STRING_FIELD(amname); + COMPARE_SCALAR_FIELD(isDrop); + COMPARE_NODE_FIELD(items); + + return true; +} + +static bool +_equalCreatedbStmt(const CreatedbStmt *a, const CreatedbStmt *b) +{ + COMPARE_STRING_FIELD(dbname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterDatabaseStmt(const AlterDatabaseStmt *a, const AlterDatabaseStmt *b) +{ + COMPARE_STRING_FIELD(dbname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterDatabaseRefreshCollStmt(const AlterDatabaseRefreshCollStmt *a, const AlterDatabaseRefreshCollStmt *b) +{ + COMPARE_STRING_FIELD(dbname); + + return true; +} + +static bool +_equalAlterDatabaseSetStmt(const AlterDatabaseSetStmt *a, const AlterDatabaseSetStmt *b) +{ + COMPARE_STRING_FIELD(dbname); + COMPARE_NODE_FIELD(setstmt); + + return true; +} + +static bool +_equalDropdbStmt(const DropdbStmt *a, const DropdbStmt *b) +{ + COMPARE_STRING_FIELD(dbname); + COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b) +{ + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(rels); + COMPARE_SCALAR_FIELD(is_vacuumcmd); + + return true; +} + +static bool +_equalVacuumRelation(const VacuumRelation *a, const VacuumRelation *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_SCALAR_FIELD(oid); + COMPARE_NODE_FIELD(va_cols); + + return true; +} + +static bool +_equalExplainStmt(const ExplainStmt *a, const ExplainStmt *b) +{ + COMPARE_NODE_FIELD(query); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalCreateTableAsStmt(const CreateTableAsStmt *a, const CreateTableAsStmt *b) +{ + COMPARE_NODE_FIELD(query); + COMPARE_NODE_FIELD(into); + COMPARE_SCALAR_FIELD(objtype); + COMPARE_SCALAR_FIELD(is_select_into); + COMPARE_SCALAR_FIELD(if_not_exists); + + return true; +} + +static bool +_equalRefreshMatViewStmt(const RefreshMatViewStmt *a, const RefreshMatViewStmt *b) +{ + COMPARE_SCALAR_FIELD(concurrent); + COMPARE_SCALAR_FIELD(skipData); + COMPARE_NODE_FIELD(relation); + + return true; +} + +static bool +_equalReplicaIdentityStmt(const ReplicaIdentityStmt *a, const ReplicaIdentityStmt *b) +{ + COMPARE_SCALAR_FIELD(identity_type); + COMPARE_STRING_FIELD(name); + + return true; +} + +static bool +_equalAlterSystemStmt(const AlterSystemStmt *a, const AlterSystemStmt *b) +{ + COMPARE_NODE_FIELD(setstmt); + + return true; +} + + +static bool +_equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b) +{ + COMPARE_NODE_FIELD(sequence); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(ownerId); + COMPARE_SCALAR_FIELD(for_identity); + COMPARE_SCALAR_FIELD(if_not_exists); + + return true; +} + +static bool +_equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b) +{ + COMPARE_NODE_FIELD(sequence); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(for_identity); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalVariableSetStmt(const VariableSetStmt *a, const VariableSetStmt *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(is_local); + + return true; +} + +static bool +_equalVariableShowStmt(const VariableShowStmt *a, const VariableShowStmt *b) +{ + COMPARE_STRING_FIELD(name); + + return true; +} + +static bool +_equalDiscardStmt(const DiscardStmt *a, const DiscardStmt *b) +{ + COMPARE_SCALAR_FIELD(target); + + return true; +} + +static bool +_equalCreateTableSpaceStmt(const CreateTableSpaceStmt *a, const CreateTableSpaceStmt *b) +{ + COMPARE_STRING_FIELD(tablespacename); + COMPARE_NODE_FIELD(owner); + COMPARE_STRING_FIELD(location); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalDropTableSpaceStmt(const DropTableSpaceStmt *a, const DropTableSpaceStmt *b) +{ + COMPARE_STRING_FIELD(tablespacename); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalAlterTableSpaceOptionsStmt(const AlterTableSpaceOptionsStmt *a, + const AlterTableSpaceOptionsStmt *b) +{ + COMPARE_STRING_FIELD(tablespacename); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(isReset); + + return true; +} + +static bool +_equalAlterTableMoveAllStmt(const AlterTableMoveAllStmt *a, + const AlterTableMoveAllStmt *b) +{ + COMPARE_STRING_FIELD(orig_tablespacename); + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(roles); + COMPARE_STRING_FIELD(new_tablespacename); + COMPARE_SCALAR_FIELD(nowait); + + return true; +} + +static bool +_equalCreateExtensionStmt(const CreateExtensionStmt *a, const CreateExtensionStmt *b) +{ + COMPARE_STRING_FIELD(extname); + COMPARE_SCALAR_FIELD(if_not_exists); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterExtensionStmt(const AlterExtensionStmt *a, const AlterExtensionStmt *b) +{ + COMPARE_STRING_FIELD(extname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterExtensionContentsStmt(const AlterExtensionContentsStmt *a, const AlterExtensionContentsStmt *b) +{ + COMPARE_STRING_FIELD(extname); + COMPARE_SCALAR_FIELD(action); + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(object); + + return true; +} + +static bool +_equalCreateFdwStmt(const CreateFdwStmt *a, const CreateFdwStmt *b) +{ + COMPARE_STRING_FIELD(fdwname); + COMPARE_NODE_FIELD(func_options); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterFdwStmt(const AlterFdwStmt *a, const AlterFdwStmt *b) +{ + COMPARE_STRING_FIELD(fdwname); + COMPARE_NODE_FIELD(func_options); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalCreateForeignServerStmt(const CreateForeignServerStmt *a, const CreateForeignServerStmt *b) +{ + COMPARE_STRING_FIELD(servername); + COMPARE_STRING_FIELD(servertype); + COMPARE_STRING_FIELD(version); + COMPARE_STRING_FIELD(fdwname); + COMPARE_SCALAR_FIELD(if_not_exists); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterForeignServerStmt(const AlterForeignServerStmt *a, const AlterForeignServerStmt *b) +{ + COMPARE_STRING_FIELD(servername); + COMPARE_STRING_FIELD(version); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(has_version); + + return true; +} + +static bool +_equalCreateUserMappingStmt(const CreateUserMappingStmt *a, const CreateUserMappingStmt *b) +{ + COMPARE_NODE_FIELD(user); + COMPARE_STRING_FIELD(servername); + COMPARE_SCALAR_FIELD(if_not_exists); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterUserMappingStmt(const AlterUserMappingStmt *a, const AlterUserMappingStmt *b) +{ + COMPARE_NODE_FIELD(user); + COMPARE_STRING_FIELD(servername); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalDropUserMappingStmt(const DropUserMappingStmt *a, const DropUserMappingStmt *b) +{ + COMPARE_NODE_FIELD(user); + COMPARE_STRING_FIELD(servername); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalCreateForeignTableStmt(const CreateForeignTableStmt *a, const CreateForeignTableStmt *b) +{ + if (!_equalCreateStmt(&a->base, &b->base)) + return false; + + COMPARE_STRING_FIELD(servername); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalImportForeignSchemaStmt(const ImportForeignSchemaStmt *a, const ImportForeignSchemaStmt *b) +{ + COMPARE_STRING_FIELD(server_name); + COMPARE_STRING_FIELD(remote_schema); + COMPARE_STRING_FIELD(local_schema); + COMPARE_SCALAR_FIELD(list_type); + COMPARE_NODE_FIELD(table_list); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStmt *b) +{ + COMPARE_SCALAR_FIELD(replace); + COMPARE_NODE_FIELD(type_name); + COMPARE_STRING_FIELD(lang); + COMPARE_NODE_FIELD(fromsql); + COMPARE_NODE_FIELD(tosql); + + return true; +} + +static bool +_equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b) +{ + COMPARE_STRING_FIELD(amname); + COMPARE_NODE_FIELD(handler_name); + COMPARE_SCALAR_FIELD(amtype); + + return true; +} + +static bool +_equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b) +{ + COMPARE_SCALAR_FIELD(replace); + COMPARE_SCALAR_FIELD(isconstraint); + COMPARE_STRING_FIELD(trigname); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(funcname); + COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(row); + COMPARE_SCALAR_FIELD(timing); + COMPARE_SCALAR_FIELD(events); + COMPARE_NODE_FIELD(columns); + COMPARE_NODE_FIELD(whenClause); + COMPARE_NODE_FIELD(transitionRels); + COMPARE_SCALAR_FIELD(deferrable); + COMPARE_SCALAR_FIELD(initdeferred); + COMPARE_NODE_FIELD(constrrel); + + return true; +} + +static bool +_equalCreateEventTrigStmt(const CreateEventTrigStmt *a, const CreateEventTrigStmt *b) +{ + COMPARE_STRING_FIELD(trigname); + COMPARE_STRING_FIELD(eventname); + COMPARE_NODE_FIELD(whenclause); + COMPARE_NODE_FIELD(funcname); + + return true; +} + +static bool +_equalAlterEventTrigStmt(const AlterEventTrigStmt *a, const AlterEventTrigStmt *b) +{ + COMPARE_STRING_FIELD(trigname); + COMPARE_SCALAR_FIELD(tgenabled); + + return true; +} + +static bool +_equalCreatePLangStmt(const CreatePLangStmt *a, const CreatePLangStmt *b) +{ + COMPARE_SCALAR_FIELD(replace); + COMPARE_STRING_FIELD(plname); + COMPARE_NODE_FIELD(plhandler); + COMPARE_NODE_FIELD(plinline); + COMPARE_NODE_FIELD(plvalidator); + COMPARE_SCALAR_FIELD(pltrusted); + + return true; +} + +static bool +_equalCreateRoleStmt(const CreateRoleStmt *a, const CreateRoleStmt *b) +{ + COMPARE_SCALAR_FIELD(stmt_type); + COMPARE_STRING_FIELD(role); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterRoleStmt(const AlterRoleStmt *a, const AlterRoleStmt *b) +{ + COMPARE_NODE_FIELD(role); + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(action); + + return true; +} + +static bool +_equalAlterRoleSetStmt(const AlterRoleSetStmt *a, const AlterRoleSetStmt *b) +{ + COMPARE_NODE_FIELD(role); + COMPARE_STRING_FIELD(database); + COMPARE_NODE_FIELD(setstmt); + + return true; +} + +static bool +_equalDropRoleStmt(const DropRoleStmt *a, const DropRoleStmt *b) +{ + COMPARE_NODE_FIELD(roles); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalLockStmt(const LockStmt *a, const LockStmt *b) +{ + COMPARE_NODE_FIELD(relations); + COMPARE_SCALAR_FIELD(mode); + COMPARE_SCALAR_FIELD(nowait); + + return true; +} + +static bool +_equalConstraintsSetStmt(const ConstraintsSetStmt *a, const ConstraintsSetStmt *b) +{ + COMPARE_NODE_FIELD(constraints); + COMPARE_SCALAR_FIELD(deferred); + + return true; +} + +static bool +_equalReindexStmt(const ReindexStmt *a, const ReindexStmt *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(params); + + return true; +} + +static bool +_equalCreateSchemaStmt(const CreateSchemaStmt *a, const CreateSchemaStmt *b) +{ + COMPARE_STRING_FIELD(schemaname); + COMPARE_NODE_FIELD(authrole); + COMPARE_NODE_FIELD(schemaElts); + COMPARE_SCALAR_FIELD(if_not_exists); + + return true; +} + +static bool +_equalCreateConversionStmt(const CreateConversionStmt *a, const CreateConversionStmt *b) +{ + COMPARE_NODE_FIELD(conversion_name); + COMPARE_STRING_FIELD(for_encoding_name); + COMPARE_STRING_FIELD(to_encoding_name); + COMPARE_NODE_FIELD(func_name); + COMPARE_SCALAR_FIELD(def); + + return true; +} + +static bool +_equalCreateCastStmt(const CreateCastStmt *a, const CreateCastStmt *b) +{ + COMPARE_NODE_FIELD(sourcetype); + COMPARE_NODE_FIELD(targettype); + COMPARE_NODE_FIELD(func); + COMPARE_SCALAR_FIELD(context); + COMPARE_SCALAR_FIELD(inout); + + return true; +} + +static bool +_equalPrepareStmt(const PrepareStmt *a, const PrepareStmt *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(argtypes); + COMPARE_NODE_FIELD(query); + + return true; +} + +static bool +_equalExecuteStmt(const ExecuteStmt *a, const ExecuteStmt *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(params); + + return true; +} + +static bool +_equalDeallocateStmt(const DeallocateStmt *a, const DeallocateStmt *b) +{ + COMPARE_STRING_FIELD(name); + + return true; +} + +static bool +_equalDropOwnedStmt(const DropOwnedStmt *a, const DropOwnedStmt *b) +{ + COMPARE_NODE_FIELD(roles); + COMPARE_SCALAR_FIELD(behavior); + + return true; +} + +static bool +_equalReassignOwnedStmt(const ReassignOwnedStmt *a, const ReassignOwnedStmt *b) +{ + COMPARE_NODE_FIELD(roles); + COMPARE_NODE_FIELD(newrole); + + return true; +} + +static bool +_equalAlterTSDictionaryStmt(const AlterTSDictionaryStmt *a, const AlterTSDictionaryStmt *b) +{ + COMPARE_NODE_FIELD(dictname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterTSConfigurationStmt(const AlterTSConfigurationStmt *a, + const AlterTSConfigurationStmt *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(cfgname); + COMPARE_NODE_FIELD(tokentype); + COMPARE_NODE_FIELD(dicts); + COMPARE_SCALAR_FIELD(override); + COMPARE_SCALAR_FIELD(replace); + COMPARE_SCALAR_FIELD(missing_ok); + + return true; +} + +static bool +_equalPublicationObject(const PublicationObjSpec *a, + const PublicationObjSpec *b) +{ + COMPARE_SCALAR_FIELD(pubobjtype); + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(pubtable); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalPublicationTable(const PublicationTable *a, const PublicationTable *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(whereClause); + COMPARE_NODE_FIELD(columns); + + return true; +} + +static bool +_equalCreatePublicationStmt(const CreatePublicationStmt *a, + const CreatePublicationStmt *b) +{ + COMPARE_STRING_FIELD(pubname); + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(pubobjects); + COMPARE_SCALAR_FIELD(for_all_tables); + + return true; +} + +static bool +_equalAlterPublicationStmt(const AlterPublicationStmt *a, + const AlterPublicationStmt *b) +{ + COMPARE_STRING_FIELD(pubname); + COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(pubobjects); + COMPARE_SCALAR_FIELD(for_all_tables); + COMPARE_SCALAR_FIELD(action); + + return true; +} + +static bool +_equalCreateSubscriptionStmt(const CreateSubscriptionStmt *a, + const CreateSubscriptionStmt *b) +{ + COMPARE_STRING_FIELD(subname); + COMPARE_STRING_FIELD(conninfo); + COMPARE_NODE_FIELD(publication); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterSubscriptionStmt(const AlterSubscriptionStmt *a, + const AlterSubscriptionStmt *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_STRING_FIELD(subname); + COMPARE_STRING_FIELD(conninfo); + COMPARE_NODE_FIELD(publication); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalDropSubscriptionStmt(const DropSubscriptionStmt *a, + const DropSubscriptionStmt *b) +{ + COMPARE_STRING_FIELD(subname); + COMPARE_SCALAR_FIELD(missing_ok); + COMPARE_SCALAR_FIELD(behavior); + + return true; +} + +static bool +_equalCreatePolicyStmt(const CreatePolicyStmt *a, const CreatePolicyStmt *b) +{ + COMPARE_STRING_FIELD(policy_name); + COMPARE_NODE_FIELD(table); + COMPARE_STRING_FIELD(cmd_name); + COMPARE_SCALAR_FIELD(permissive); + COMPARE_NODE_FIELD(roles); + COMPARE_NODE_FIELD(qual); + COMPARE_NODE_FIELD(with_check); + + return true; +} + +static bool +_equalAlterPolicyStmt(const AlterPolicyStmt *a, const AlterPolicyStmt *b) +{ + COMPARE_STRING_FIELD(policy_name); + COMPARE_NODE_FIELD(table); + COMPARE_NODE_FIELD(roles); + COMPARE_NODE_FIELD(qual); + COMPARE_NODE_FIELD(with_check); + + return true; +} + +static bool +_equalA_Expr(const A_Expr *a, const A_Expr *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(name); + COMPARE_NODE_FIELD(lexpr); + COMPARE_NODE_FIELD(rexpr); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalColumnRef(const ColumnRef *a, const ColumnRef *b) +{ + COMPARE_NODE_FIELD(fields); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalParamRef(const ParamRef *a, const ParamRef *b) +{ + COMPARE_SCALAR_FIELD(number); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalA_Const(const A_Const *a, const A_Const *b) +{ + /* + * Hack for in-line val field. Also val is not valid is isnull is true. + */ + if (!a->isnull && !b->isnull && + !equal(&a->val, &b->val)) + return false; + COMPARE_SCALAR_FIELD(isnull); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalFuncCall(const FuncCall *a, const FuncCall *b) +{ + COMPARE_NODE_FIELD(funcname); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(agg_order); + COMPARE_NODE_FIELD(agg_filter); + COMPARE_NODE_FIELD(over); + COMPARE_SCALAR_FIELD(agg_within_group); + COMPARE_SCALAR_FIELD(agg_star); + COMPARE_SCALAR_FIELD(agg_distinct); + COMPARE_SCALAR_FIELD(func_variadic); + COMPARE_COERCIONFORM_FIELD(funcformat); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalA_Star(const A_Star *a, const A_Star *b) +{ + return true; +} + +static bool +_equalA_Indices(const A_Indices *a, const A_Indices *b) +{ + COMPARE_SCALAR_FIELD(is_slice); + COMPARE_NODE_FIELD(lidx); + COMPARE_NODE_FIELD(uidx); + + return true; +} + +static bool +_equalA_Indirection(const A_Indirection *a, const A_Indirection *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(indirection); + + return true; +} + +static bool +_equalA_ArrayExpr(const A_ArrayExpr *a, const A_ArrayExpr *b) +{ + COMPARE_NODE_FIELD(elements); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalResTarget(const ResTarget *a, const ResTarget *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(indirection); + COMPARE_NODE_FIELD(val); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalMultiAssignRef(const MultiAssignRef *a, const MultiAssignRef *b) +{ + COMPARE_NODE_FIELD(source); + COMPARE_SCALAR_FIELD(colno); + COMPARE_SCALAR_FIELD(ncolumns); + + return true; +} + +static bool +_equalTypeName(const TypeName *a, const TypeName *b) +{ + COMPARE_NODE_FIELD(names); + COMPARE_SCALAR_FIELD(typeOid); + COMPARE_SCALAR_FIELD(setof); + COMPARE_SCALAR_FIELD(pct_type); + COMPARE_NODE_FIELD(typmods); + COMPARE_SCALAR_FIELD(typemod); + COMPARE_NODE_FIELD(arrayBounds); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalTypeCast(const TypeCast *a, const TypeCast *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(typeName); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCollateClause(const CollateClause *a, const CollateClause *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(collname); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalSortBy(const SortBy *a, const SortBy *b) +{ + COMPARE_NODE_FIELD(node); + COMPARE_SCALAR_FIELD(sortby_dir); + COMPARE_SCALAR_FIELD(sortby_nulls); + COMPARE_NODE_FIELD(useOp); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalWindowDef(const WindowDef *a, const WindowDef *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_STRING_FIELD(refname); + COMPARE_NODE_FIELD(partitionClause); + COMPARE_NODE_FIELD(orderClause); + COMPARE_SCALAR_FIELD(frameOptions); + COMPARE_NODE_FIELD(startOffset); + COMPARE_NODE_FIELD(endOffset); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalRangeSubselect(const RangeSubselect *a, const RangeSubselect *b) +{ + COMPARE_SCALAR_FIELD(lateral); + COMPARE_NODE_FIELD(subquery); + COMPARE_NODE_FIELD(alias); + + return true; +} + +static bool +_equalRangeFunction(const RangeFunction *a, const RangeFunction *b) +{ + COMPARE_SCALAR_FIELD(lateral); + COMPARE_SCALAR_FIELD(ordinality); + COMPARE_SCALAR_FIELD(is_rowsfrom); + COMPARE_NODE_FIELD(functions); + COMPARE_NODE_FIELD(alias); + COMPARE_NODE_FIELD(coldeflist); + + return true; +} + +static bool +_equalRangeTableSample(const RangeTableSample *a, const RangeTableSample *b) +{ + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(method); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(repeatable); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalRangeTableFunc(const RangeTableFunc *a, const RangeTableFunc *b) +{ + COMPARE_SCALAR_FIELD(lateral); + COMPARE_NODE_FIELD(docexpr); + COMPARE_NODE_FIELD(rowexpr); + COMPARE_NODE_FIELD(namespaces); + COMPARE_NODE_FIELD(columns); + COMPARE_NODE_FIELD(alias); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalRangeTableFuncCol(const RangeTableFuncCol *a, const RangeTableFuncCol *b) +{ + COMPARE_STRING_FIELD(colname); + COMPARE_NODE_FIELD(typeName); + COMPARE_SCALAR_FIELD(for_ordinality); + COMPARE_SCALAR_FIELD(is_not_null); + COMPARE_NODE_FIELD(colexpr); + COMPARE_NODE_FIELD(coldefexpr); + COMPARE_LOCATION_FIELD(location); + + return true; +} + + +static bool +_equalIndexElem(const IndexElem *a, const IndexElem *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(expr); + COMPARE_STRING_FIELD(indexcolname); + COMPARE_NODE_FIELD(collation); + COMPARE_NODE_FIELD(opclass); + COMPARE_NODE_FIELD(opclassopts); + COMPARE_SCALAR_FIELD(ordering); + COMPARE_SCALAR_FIELD(nulls_ordering); + + return true; +} + + +static bool +_equalStatsElem(const StatsElem *a, const StatsElem *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(expr); + + return true; +} + +static bool +_equalColumnDef(const ColumnDef *a, const ColumnDef *b) +{ + COMPARE_STRING_FIELD(colname); + COMPARE_NODE_FIELD(typeName); + COMPARE_STRING_FIELD(compression); + COMPARE_SCALAR_FIELD(inhcount); + COMPARE_SCALAR_FIELD(is_local); + COMPARE_SCALAR_FIELD(is_not_null); + COMPARE_SCALAR_FIELD(is_from_type); + COMPARE_SCALAR_FIELD(storage); + COMPARE_NODE_FIELD(raw_default); + COMPARE_NODE_FIELD(cooked_default); + COMPARE_SCALAR_FIELD(identity); + COMPARE_NODE_FIELD(identitySequence); + COMPARE_SCALAR_FIELD(generated); + COMPARE_NODE_FIELD(collClause); + COMPARE_SCALAR_FIELD(collOid); + COMPARE_NODE_FIELD(constraints); + COMPARE_NODE_FIELD(fdwoptions); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalConstraint(const Constraint *a, const Constraint *b) +{ + COMPARE_SCALAR_FIELD(contype); + COMPARE_STRING_FIELD(conname); + COMPARE_SCALAR_FIELD(deferrable); + COMPARE_SCALAR_FIELD(initdeferred); + COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(is_no_inherit); + COMPARE_NODE_FIELD(raw_expr); + COMPARE_STRING_FIELD(cooked_expr); + COMPARE_SCALAR_FIELD(generated_when); + COMPARE_SCALAR_FIELD(nulls_not_distinct); + COMPARE_NODE_FIELD(keys); + COMPARE_NODE_FIELD(including); + COMPARE_NODE_FIELD(exclusions); + COMPARE_NODE_FIELD(options); + COMPARE_STRING_FIELD(indexname); + COMPARE_STRING_FIELD(indexspace); + COMPARE_SCALAR_FIELD(reset_default_tblspc); + COMPARE_STRING_FIELD(access_method); + COMPARE_NODE_FIELD(where_clause); + COMPARE_NODE_FIELD(pktable); + COMPARE_NODE_FIELD(fk_attrs); + COMPARE_NODE_FIELD(pk_attrs); + COMPARE_SCALAR_FIELD(fk_matchtype); + COMPARE_SCALAR_FIELD(fk_upd_action); + COMPARE_SCALAR_FIELD(fk_del_action); + COMPARE_NODE_FIELD(fk_del_set_cols); + COMPARE_NODE_FIELD(old_conpfeqop); + COMPARE_SCALAR_FIELD(old_pktable_oid); + COMPARE_SCALAR_FIELD(skip_validation); + COMPARE_SCALAR_FIELD(initially_valid); + + return true; +} + +static bool +_equalDefElem(const DefElem *a, const DefElem *b) +{ + COMPARE_STRING_FIELD(defnamespace); + COMPARE_STRING_FIELD(defname); + COMPARE_NODE_FIELD(arg); + COMPARE_SCALAR_FIELD(defaction); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalLockingClause(const LockingClause *a, const LockingClause *b) +{ + COMPARE_NODE_FIELD(lockedRels); + COMPARE_SCALAR_FIELD(strength); + COMPARE_SCALAR_FIELD(waitPolicy); + + return true; +} + +static bool +_equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) +{ + COMPARE_SCALAR_FIELD(rtekind); + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(relkind); + COMPARE_SCALAR_FIELD(rellockmode); + COMPARE_NODE_FIELD(tablesample); + COMPARE_NODE_FIELD(subquery); + COMPARE_SCALAR_FIELD(security_barrier); + COMPARE_SCALAR_FIELD(jointype); + COMPARE_SCALAR_FIELD(joinmergedcols); + COMPARE_NODE_FIELD(joinaliasvars); + COMPARE_NODE_FIELD(joinleftcols); + COMPARE_NODE_FIELD(joinrightcols); + COMPARE_NODE_FIELD(join_using_alias); + COMPARE_NODE_FIELD(functions); + COMPARE_SCALAR_FIELD(funcordinality); + COMPARE_NODE_FIELD(tablefunc); + COMPARE_NODE_FIELD(values_lists); + COMPARE_STRING_FIELD(ctename); + COMPARE_SCALAR_FIELD(ctelevelsup); + COMPARE_SCALAR_FIELD(self_reference); + COMPARE_NODE_FIELD(coltypes); + COMPARE_NODE_FIELD(coltypmods); + COMPARE_NODE_FIELD(colcollations); + COMPARE_STRING_FIELD(enrname); + COMPARE_SCALAR_FIELD(enrtuples); + COMPARE_NODE_FIELD(alias); + COMPARE_NODE_FIELD(eref); + COMPARE_SCALAR_FIELD(lateral); + COMPARE_SCALAR_FIELD(inh); + COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_SCALAR_FIELD(requiredPerms); + COMPARE_SCALAR_FIELD(checkAsUser); + COMPARE_BITMAPSET_FIELD(selectedCols); + COMPARE_BITMAPSET_FIELD(insertedCols); + COMPARE_BITMAPSET_FIELD(updatedCols); + COMPARE_BITMAPSET_FIELD(extraUpdatedCols); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) +{ + COMPARE_NODE_FIELD(funcexpr); + COMPARE_SCALAR_FIELD(funccolcount); + COMPARE_NODE_FIELD(funccolnames); + COMPARE_NODE_FIELD(funccoltypes); + COMPARE_NODE_FIELD(funccoltypmods); + COMPARE_NODE_FIELD(funccolcollations); + COMPARE_BITMAPSET_FIELD(funcparams); + + return true; +} + +static bool +_equalTableSampleClause(const TableSampleClause *a, const TableSampleClause *b) +{ + COMPARE_SCALAR_FIELD(tsmhandler); + COMPARE_NODE_FIELD(args); + COMPARE_NODE_FIELD(repeatable); + + return true; +} + +static bool +_equalWithCheckOption(const WithCheckOption *a, const WithCheckOption *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_STRING_FIELD(relname); + COMPARE_STRING_FIELD(polname); + COMPARE_NODE_FIELD(qual); + COMPARE_SCALAR_FIELD(cascaded); + + return true; +} + +static bool +_equalSortGroupClause(const SortGroupClause *a, const SortGroupClause *b) +{ + COMPARE_SCALAR_FIELD(tleSortGroupRef); + COMPARE_SCALAR_FIELD(eqop); + COMPARE_SCALAR_FIELD(sortop); + COMPARE_SCALAR_FIELD(nulls_first); + COMPARE_SCALAR_FIELD(hashable); + + return true; +} + +static bool +_equalGroupingSet(const GroupingSet *a, const GroupingSet *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(content); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalWindowClause(const WindowClause *a, const WindowClause *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_STRING_FIELD(refname); + COMPARE_NODE_FIELD(partitionClause); + COMPARE_NODE_FIELD(orderClause); + COMPARE_SCALAR_FIELD(frameOptions); + COMPARE_NODE_FIELD(startOffset); + COMPARE_NODE_FIELD(endOffset); + COMPARE_NODE_FIELD(runCondition); + COMPARE_SCALAR_FIELD(startInRangeFunc); + COMPARE_SCALAR_FIELD(endInRangeFunc); + COMPARE_SCALAR_FIELD(inRangeColl); + COMPARE_SCALAR_FIELD(inRangeAsc); + COMPARE_SCALAR_FIELD(inRangeNullsFirst); + COMPARE_SCALAR_FIELD(winref); + COMPARE_SCALAR_FIELD(copiedOrder); + + return true; +} + +static bool +_equalRowMarkClause(const RowMarkClause *a, const RowMarkClause *b) +{ + COMPARE_SCALAR_FIELD(rti); + COMPARE_SCALAR_FIELD(strength); + COMPARE_SCALAR_FIELD(waitPolicy); + COMPARE_SCALAR_FIELD(pushedDown); + + return true; +} + +static bool +_equalWithClause(const WithClause *a, const WithClause *b) +{ + COMPARE_NODE_FIELD(ctes); + COMPARE_SCALAR_FIELD(recursive); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalInferClause(const InferClause *a, const InferClause *b) +{ + COMPARE_NODE_FIELD(indexElems); + COMPARE_NODE_FIELD(whereClause); + COMPARE_STRING_FIELD(conname); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalOnConflictClause(const OnConflictClause *a, const OnConflictClause *b) +{ + COMPARE_SCALAR_FIELD(action); + COMPARE_NODE_FIELD(infer); + COMPARE_NODE_FIELD(targetList); + COMPARE_NODE_FIELD(whereClause); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCTESearchClause(const CTESearchClause *a, const CTESearchClause *b) +{ + COMPARE_NODE_FIELD(search_col_list); + COMPARE_SCALAR_FIELD(search_breadth_first); + COMPARE_STRING_FIELD(search_seq_column); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalCTECycleClause(const CTECycleClause *a, const CTECycleClause *b) +{ + COMPARE_NODE_FIELD(cycle_col_list); + COMPARE_STRING_FIELD(cycle_mark_column); + COMPARE_NODE_FIELD(cycle_mark_value); + COMPARE_NODE_FIELD(cycle_mark_default); + COMPARE_STRING_FIELD(cycle_path_column); + COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(cycle_mark_type); + COMPARE_SCALAR_FIELD(cycle_mark_typmod); + COMPARE_SCALAR_FIELD(cycle_mark_collation); + COMPARE_SCALAR_FIELD(cycle_mark_neop); + + return true; +} + +static bool +_equalCommonTableExpr(const CommonTableExpr *a, const CommonTableExpr *b) +{ + COMPARE_STRING_FIELD(ctename); + COMPARE_NODE_FIELD(aliascolnames); + COMPARE_SCALAR_FIELD(ctematerialized); + COMPARE_NODE_FIELD(ctequery); + COMPARE_NODE_FIELD(search_clause); + COMPARE_NODE_FIELD(cycle_clause); + COMPARE_LOCATION_FIELD(location); + COMPARE_SCALAR_FIELD(cterecursive); + COMPARE_SCALAR_FIELD(cterefcount); + COMPARE_NODE_FIELD(ctecolnames); + COMPARE_NODE_FIELD(ctecoltypes); + COMPARE_NODE_FIELD(ctecoltypmods); + COMPARE_NODE_FIELD(ctecolcollations); + + return true; +} + +static bool +_equalMergeWhenClause(const MergeWhenClause *a, const MergeWhenClause *b) +{ + COMPARE_SCALAR_FIELD(matched); + COMPARE_SCALAR_FIELD(commandType); + COMPARE_SCALAR_FIELD(override); + COMPARE_NODE_FIELD(condition); + COMPARE_NODE_FIELD(targetList); + COMPARE_NODE_FIELD(values); + + return true; +} + +static bool +_equalMergeAction(const MergeAction *a, const MergeAction *b) +{ + COMPARE_SCALAR_FIELD(matched); + COMPARE_SCALAR_FIELD(commandType); + COMPARE_SCALAR_FIELD(override); + COMPARE_NODE_FIELD(qual); + COMPARE_NODE_FIELD(targetList); + COMPARE_NODE_FIELD(updateColnos); + + return true; +} + +static bool +_equalXmlSerialize(const XmlSerialize *a, const XmlSerialize *b) +{ + COMPARE_SCALAR_FIELD(xmloption); + COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(typeName); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalRoleSpec(const RoleSpec *a, const RoleSpec *b) +{ + COMPARE_SCALAR_FIELD(roletype); + COMPARE_STRING_FIELD(rolename); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalTriggerTransition(const TriggerTransition *a, const TriggerTransition *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_SCALAR_FIELD(isNew); + COMPARE_SCALAR_FIELD(isTable); + + return true; +} + +static bool +_equalPartitionElem(const PartitionElem *a, const PartitionElem *b) +{ + COMPARE_STRING_FIELD(name); + COMPARE_NODE_FIELD(expr); + COMPARE_NODE_FIELD(collation); + COMPARE_NODE_FIELD(opclass); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b) +{ + COMPARE_STRING_FIELD(strategy); + COMPARE_NODE_FIELD(partParams); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalPartitionBoundSpec(const PartitionBoundSpec *a, const PartitionBoundSpec *b) +{ + COMPARE_SCALAR_FIELD(strategy); + COMPARE_SCALAR_FIELD(is_default); + COMPARE_SCALAR_FIELD(modulus); + COMPARE_SCALAR_FIELD(remainder); + COMPARE_NODE_FIELD(listdatums); + COMPARE_NODE_FIELD(lowerdatums); + COMPARE_NODE_FIELD(upperdatums); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalPartitionRangeDatum(const PartitionRangeDatum *a, const PartitionRangeDatum *b) +{ + COMPARE_SCALAR_FIELD(kind); + COMPARE_NODE_FIELD(value); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool +_equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b) +{ + COMPARE_NODE_FIELD(name); + COMPARE_NODE_FIELD(bound); + COMPARE_SCALAR_FIELD(concurrent); + + return true; +} + +/* + * Stuff from pg_list.h + */ + +static bool +_equalList(const List *a, const List *b) +{ + const ListCell *item_a; + const ListCell *item_b; + + /* + * Try to reject by simple scalar checks before grovelling through all the + * list elements... + */ + COMPARE_SCALAR_FIELD(type); + COMPARE_SCALAR_FIELD(length); + + /* + * We place the switch outside the loop for the sake of efficiency; this + * may not be worth doing... + */ + switch (a->type) + { + case T_List: + forboth(item_a, a, item_b, b) + { + if (!equal(lfirst(item_a), lfirst(item_b))) + return false; + } + break; + case T_IntList: + forboth(item_a, a, item_b, b) + { + if (lfirst_int(item_a) != lfirst_int(item_b)) + return false; + } + break; + case T_OidList: + forboth(item_a, a, item_b, b) + { + if (lfirst_oid(item_a) != lfirst_oid(item_b)) + return false; + } + break; + default: + elog(ERROR, "unrecognized list node type: %d", + (int) a->type); + return false; /* keep compiler quiet */ + } + + /* + * If we got here, we should have run out of elements of both lists + */ + Assert(item_a == NULL); + Assert(item_b == NULL); + + return true; +} + +/* + * Stuff from value.h + */ + +static bool +_equalInteger(const Integer *a, const Integer *b) +{ + COMPARE_SCALAR_FIELD(ival); + + return true; +} + +static bool +_equalFloat(const Float *a, const Float *b) +{ + COMPARE_STRING_FIELD(fval); + + return true; +} + +static bool +_equalBoolean(const Boolean *a, const Boolean *b) +{ + COMPARE_SCALAR_FIELD(boolval); + + return true; +} + +static bool +_equalString(const String *a, const String *b) +{ + COMPARE_STRING_FIELD(sval); + + return true; +} + +static bool +_equalBitString(const BitString *a, const BitString *b) +{ + COMPARE_STRING_FIELD(bsval); + + return true; +} + +/* + * equal + * returns whether two nodes are equal + */ +bool +equal(const void *a, const void *b) +{ + bool retval; + + if (a == b) + return true; + + /* + * note that a!=b, so only one of them can be NULL + */ + if (a == NULL || b == NULL) + return false; + + /* + * are they the same type of nodes? + */ + if (nodeTag(a) != nodeTag(b)) + return false; + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + switch (nodeTag(a)) + { + /* + * PRIMITIVE NODES + */ + case T_Alias: + retval = _equalAlias(a, b); + break; + case T_RangeVar: + retval = _equalRangeVar(a, b); + break; + case T_TableFunc: + retval = _equalTableFunc(a, b); + break; + case T_IntoClause: + retval = _equalIntoClause(a, b); + break; + case T_Var: + retval = _equalVar(a, b); + break; + case T_Const: + retval = _equalConst(a, b); + break; + case T_Param: + retval = _equalParam(a, b); + break; + case T_Aggref: + retval = _equalAggref(a, b); + break; + case T_GroupingFunc: + retval = _equalGroupingFunc(a, b); + break; + case T_WindowFunc: + retval = _equalWindowFunc(a, b); + break; + case T_SubscriptingRef: + retval = _equalSubscriptingRef(a, b); + break; + case T_FuncExpr: + retval = _equalFuncExpr(a, b); + break; + case T_NamedArgExpr: + retval = _equalNamedArgExpr(a, b); + break; + case T_OpExpr: + retval = _equalOpExpr(a, b); + break; + case T_DistinctExpr: + retval = _equalDistinctExpr(a, b); + break; + case T_NullIfExpr: + retval = _equalNullIfExpr(a, b); + break; + case T_ScalarArrayOpExpr: + retval = _equalScalarArrayOpExpr(a, b); + break; + case T_BoolExpr: + retval = _equalBoolExpr(a, b); + break; + case T_SubLink: + retval = _equalSubLink(a, b); + break; + case T_SubPlan: + retval = _equalSubPlan(a, b); + break; + case T_AlternativeSubPlan: + retval = _equalAlternativeSubPlan(a, b); + break; + case T_FieldSelect: + retval = _equalFieldSelect(a, b); + break; + case T_FieldStore: + retval = _equalFieldStore(a, b); + break; + case T_RelabelType: + retval = _equalRelabelType(a, b); + break; + case T_CoerceViaIO: + retval = _equalCoerceViaIO(a, b); + break; + case T_ArrayCoerceExpr: + retval = _equalArrayCoerceExpr(a, b); + break; + case T_ConvertRowtypeExpr: + retval = _equalConvertRowtypeExpr(a, b); + break; + case T_CollateExpr: + retval = _equalCollateExpr(a, b); + break; + case T_CaseExpr: + retval = _equalCaseExpr(a, b); + break; + case T_CaseWhen: + retval = _equalCaseWhen(a, b); + break; + case T_CaseTestExpr: + retval = _equalCaseTestExpr(a, b); + break; + case T_ArrayExpr: + retval = _equalArrayExpr(a, b); + break; + case T_RowExpr: + retval = _equalRowExpr(a, b); + break; + case T_RowCompareExpr: + retval = _equalRowCompareExpr(a, b); + break; + case T_CoalesceExpr: + retval = _equalCoalesceExpr(a, b); + break; + case T_MinMaxExpr: + retval = _equalMinMaxExpr(a, b); + break; + case T_SQLValueFunction: + retval = _equalSQLValueFunction(a, b); + break; + case T_XmlExpr: + retval = _equalXmlExpr(a, b); + break; + case T_NullTest: + retval = _equalNullTest(a, b); + break; + case T_BooleanTest: + retval = _equalBooleanTest(a, b); + break; + case T_CoerceToDomain: + retval = _equalCoerceToDomain(a, b); + break; + case T_CoerceToDomainValue: + retval = _equalCoerceToDomainValue(a, b); + break; + case T_SetToDefault: + retval = _equalSetToDefault(a, b); + break; + case T_CurrentOfExpr: + retval = _equalCurrentOfExpr(a, b); + break; + case T_NextValueExpr: + retval = _equalNextValueExpr(a, b); + break; + case T_InferenceElem: + retval = _equalInferenceElem(a, b); + break; + case T_TargetEntry: + retval = _equalTargetEntry(a, b); + break; + case T_RangeTblRef: + retval = _equalRangeTblRef(a, b); + break; + case T_FromExpr: + retval = _equalFromExpr(a, b); + break; + case T_OnConflictExpr: + retval = _equalOnConflictExpr(a, b); + break; + case T_JoinExpr: + retval = _equalJoinExpr(a, b); + break; + + /* + * RELATION NODES + */ + case T_PathKey: + retval = _equalPathKey(a, b); + break; + case T_RestrictInfo: + retval = _equalRestrictInfo(a, b); + break; + case T_PlaceHolderVar: + retval = _equalPlaceHolderVar(a, b); + break; + case T_SpecialJoinInfo: + retval = _equalSpecialJoinInfo(a, b); + break; + case T_AppendRelInfo: + retval = _equalAppendRelInfo(a, b); + break; + case T_PlaceHolderInfo: + retval = _equalPlaceHolderInfo(a, b); + break; + + case T_List: + case T_IntList: + case T_OidList: + retval = _equalList(a, b); + break; + + case T_Integer: + retval = _equalInteger(a, b); + break; + case T_Float: + retval = _equalFloat(a, b); + break; + case T_Boolean: + retval = _equalBoolean(a, b); + break; + case T_String: + retval = _equalString(a, b); + break; + case T_BitString: + retval = _equalBitString(a, b); + break; + + /* + * EXTENSIBLE NODES + */ + case T_ExtensibleNode: + retval = _equalExtensibleNode(a, b); + break; + + /* + * PARSE NODES + */ + case T_Query: + retval = _equalQuery(a, b); + break; + case T_RawStmt: + retval = _equalRawStmt(a, b); + break; + case T_InsertStmt: + retval = _equalInsertStmt(a, b); + break; + case T_DeleteStmt: + retval = _equalDeleteStmt(a, b); + break; + case T_UpdateStmt: + retval = _equalUpdateStmt(a, b); + break; + case T_MergeStmt: + retval = _equalMergeStmt(a, b); + break; + case T_SelectStmt: + retval = _equalSelectStmt(a, b); + break; + case T_SetOperationStmt: + retval = _equalSetOperationStmt(a, b); + break; + case T_ReturnStmt: + retval = _equalReturnStmt(a, b); + break; + case T_PLAssignStmt: + retval = _equalPLAssignStmt(a, b); + break; + case T_AlterTableStmt: + retval = _equalAlterTableStmt(a, b); + break; + case T_AlterTableCmd: + retval = _equalAlterTableCmd(a, b); + break; + case T_AlterCollationStmt: + retval = _equalAlterCollationStmt(a, b); + break; + case T_AlterDomainStmt: + retval = _equalAlterDomainStmt(a, b); + break; + case T_GrantStmt: + retval = _equalGrantStmt(a, b); + break; + case T_GrantRoleStmt: + retval = _equalGrantRoleStmt(a, b); + break; + case T_AlterDefaultPrivilegesStmt: + retval = _equalAlterDefaultPrivilegesStmt(a, b); + break; + case T_DeclareCursorStmt: + retval = _equalDeclareCursorStmt(a, b); + break; + case T_ClosePortalStmt: + retval = _equalClosePortalStmt(a, b); + break; + case T_CallStmt: + retval = _equalCallStmt(a, b); + break; + case T_ClusterStmt: + retval = _equalClusterStmt(a, b); + break; + case T_CopyStmt: + retval = _equalCopyStmt(a, b); + break; + case T_CreateStmt: + retval = _equalCreateStmt(a, b); + break; + case T_TableLikeClause: + retval = _equalTableLikeClause(a, b); + break; + case T_DefineStmt: + retval = _equalDefineStmt(a, b); + break; + case T_DropStmt: + retval = _equalDropStmt(a, b); + break; + case T_TruncateStmt: + retval = _equalTruncateStmt(a, b); + break; + case T_CommentStmt: + retval = _equalCommentStmt(a, b); + break; + case T_SecLabelStmt: + retval = _equalSecLabelStmt(a, b); + break; + case T_FetchStmt: + retval = _equalFetchStmt(a, b); + break; + case T_IndexStmt: + retval = _equalIndexStmt(a, b); + break; + case T_CreateStatsStmt: + retval = _equalCreateStatsStmt(a, b); + break; + case T_AlterStatsStmt: + retval = _equalAlterStatsStmt(a, b); + break; + case T_CreateFunctionStmt: + retval = _equalCreateFunctionStmt(a, b); + break; + case T_FunctionParameter: + retval = _equalFunctionParameter(a, b); + break; + case T_AlterFunctionStmt: + retval = _equalAlterFunctionStmt(a, b); + break; + case T_DoStmt: + retval = _equalDoStmt(a, b); + break; + case T_RenameStmt: + retval = _equalRenameStmt(a, b); + break; + case T_AlterObjectDependsStmt: + retval = _equalAlterObjectDependsStmt(a, b); + break; + case T_AlterObjectSchemaStmt: + retval = _equalAlterObjectSchemaStmt(a, b); + break; + case T_AlterOwnerStmt: + retval = _equalAlterOwnerStmt(a, b); + break; + case T_AlterOperatorStmt: + retval = _equalAlterOperatorStmt(a, b); + break; + case T_AlterTypeStmt: + retval = _equalAlterTypeStmt(a, b); + break; + case T_RuleStmt: + retval = _equalRuleStmt(a, b); + break; + case T_NotifyStmt: + retval = _equalNotifyStmt(a, b); + break; + case T_ListenStmt: + retval = _equalListenStmt(a, b); + break; + case T_UnlistenStmt: + retval = _equalUnlistenStmt(a, b); + break; + case T_TransactionStmt: + retval = _equalTransactionStmt(a, b); + break; + case T_CompositeTypeStmt: + retval = _equalCompositeTypeStmt(a, b); + break; + case T_CreateEnumStmt: + retval = _equalCreateEnumStmt(a, b); + break; + case T_CreateRangeStmt: + retval = _equalCreateRangeStmt(a, b); + break; + case T_AlterEnumStmt: + retval = _equalAlterEnumStmt(a, b); + break; + case T_ViewStmt: + retval = _equalViewStmt(a, b); + break; + case T_LoadStmt: + retval = _equalLoadStmt(a, b); + break; + case T_CreateDomainStmt: + retval = _equalCreateDomainStmt(a, b); + break; + case T_CreateOpClassStmt: + retval = _equalCreateOpClassStmt(a, b); + break; + case T_CreateOpClassItem: + retval = _equalCreateOpClassItem(a, b); + break; + case T_CreateOpFamilyStmt: + retval = _equalCreateOpFamilyStmt(a, b); + break; + case T_AlterOpFamilyStmt: + retval = _equalAlterOpFamilyStmt(a, b); + break; + case T_CreatedbStmt: + retval = _equalCreatedbStmt(a, b); + break; + case T_AlterDatabaseStmt: + retval = _equalAlterDatabaseStmt(a, b); + break; + case T_AlterDatabaseRefreshCollStmt: + retval = _equalAlterDatabaseRefreshCollStmt(a, b); + break; + case T_AlterDatabaseSetStmt: + retval = _equalAlterDatabaseSetStmt(a, b); + break; + case T_DropdbStmt: + retval = _equalDropdbStmt(a, b); + break; + case T_VacuumStmt: + retval = _equalVacuumStmt(a, b); + break; + case T_VacuumRelation: + retval = _equalVacuumRelation(a, b); + break; + case T_ExplainStmt: + retval = _equalExplainStmt(a, b); + break; + case T_CreateTableAsStmt: + retval = _equalCreateTableAsStmt(a, b); + break; + case T_RefreshMatViewStmt: + retval = _equalRefreshMatViewStmt(a, b); + break; + case T_ReplicaIdentityStmt: + retval = _equalReplicaIdentityStmt(a, b); + break; + case T_AlterSystemStmt: + retval = _equalAlterSystemStmt(a, b); + break; + case T_CreateSeqStmt: + retval = _equalCreateSeqStmt(a, b); + break; + case T_AlterSeqStmt: + retval = _equalAlterSeqStmt(a, b); + break; + case T_VariableSetStmt: + retval = _equalVariableSetStmt(a, b); + break; + case T_VariableShowStmt: + retval = _equalVariableShowStmt(a, b); + break; + case T_DiscardStmt: + retval = _equalDiscardStmt(a, b); + break; + case T_CreateTableSpaceStmt: + retval = _equalCreateTableSpaceStmt(a, b); + break; + case T_DropTableSpaceStmt: + retval = _equalDropTableSpaceStmt(a, b); + break; + case T_AlterTableSpaceOptionsStmt: + retval = _equalAlterTableSpaceOptionsStmt(a, b); + break; + case T_AlterTableMoveAllStmt: + retval = _equalAlterTableMoveAllStmt(a, b); + break; + case T_CreateExtensionStmt: + retval = _equalCreateExtensionStmt(a, b); + break; + case T_AlterExtensionStmt: + retval = _equalAlterExtensionStmt(a, b); + break; + case T_AlterExtensionContentsStmt: + retval = _equalAlterExtensionContentsStmt(a, b); + break; + case T_CreateFdwStmt: + retval = _equalCreateFdwStmt(a, b); + break; + case T_AlterFdwStmt: + retval = _equalAlterFdwStmt(a, b); + break; + case T_CreateForeignServerStmt: + retval = _equalCreateForeignServerStmt(a, b); + break; + case T_AlterForeignServerStmt: + retval = _equalAlterForeignServerStmt(a, b); + break; + case T_CreateUserMappingStmt: + retval = _equalCreateUserMappingStmt(a, b); + break; + case T_AlterUserMappingStmt: + retval = _equalAlterUserMappingStmt(a, b); + break; + case T_DropUserMappingStmt: + retval = _equalDropUserMappingStmt(a, b); + break; + case T_CreateForeignTableStmt: + retval = _equalCreateForeignTableStmt(a, b); + break; + case T_ImportForeignSchemaStmt: + retval = _equalImportForeignSchemaStmt(a, b); + break; + case T_CreateTransformStmt: + retval = _equalCreateTransformStmt(a, b); + break; + case T_CreateAmStmt: + retval = _equalCreateAmStmt(a, b); + break; + case T_CreateTrigStmt: + retval = _equalCreateTrigStmt(a, b); + break; + case T_CreateEventTrigStmt: + retval = _equalCreateEventTrigStmt(a, b); + break; + case T_AlterEventTrigStmt: + retval = _equalAlterEventTrigStmt(a, b); + break; + case T_CreatePLangStmt: + retval = _equalCreatePLangStmt(a, b); + break; + case T_CreateRoleStmt: + retval = _equalCreateRoleStmt(a, b); + break; + case T_AlterRoleStmt: + retval = _equalAlterRoleStmt(a, b); + break; + case T_AlterRoleSetStmt: + retval = _equalAlterRoleSetStmt(a, b); + break; + case T_DropRoleStmt: + retval = _equalDropRoleStmt(a, b); + break; + case T_LockStmt: + retval = _equalLockStmt(a, b); + break; + case T_ConstraintsSetStmt: + retval = _equalConstraintsSetStmt(a, b); + break; + case T_ReindexStmt: + retval = _equalReindexStmt(a, b); + break; + case T_CheckPointStmt: + retval = true; + break; + case T_CreateSchemaStmt: + retval = _equalCreateSchemaStmt(a, b); + break; + case T_CreateConversionStmt: + retval = _equalCreateConversionStmt(a, b); + break; + case T_CreateCastStmt: + retval = _equalCreateCastStmt(a, b); + break; + case T_PrepareStmt: + retval = _equalPrepareStmt(a, b); + break; + case T_ExecuteStmt: + retval = _equalExecuteStmt(a, b); + break; + case T_DeallocateStmt: + retval = _equalDeallocateStmt(a, b); + break; + case T_DropOwnedStmt: + retval = _equalDropOwnedStmt(a, b); + break; + case T_ReassignOwnedStmt: + retval = _equalReassignOwnedStmt(a, b); + break; + case T_AlterTSDictionaryStmt: + retval = _equalAlterTSDictionaryStmt(a, b); + break; + case T_AlterTSConfigurationStmt: + retval = _equalAlterTSConfigurationStmt(a, b); + break; + case T_CreatePolicyStmt: + retval = _equalCreatePolicyStmt(a, b); + break; + case T_AlterPolicyStmt: + retval = _equalAlterPolicyStmt(a, b); + break; + case T_CreatePublicationStmt: + retval = _equalCreatePublicationStmt(a, b); + break; + case T_AlterPublicationStmt: + retval = _equalAlterPublicationStmt(a, b); + break; + case T_CreateSubscriptionStmt: + retval = _equalCreateSubscriptionStmt(a, b); + break; + case T_AlterSubscriptionStmt: + retval = _equalAlterSubscriptionStmt(a, b); + break; + case T_DropSubscriptionStmt: + retval = _equalDropSubscriptionStmt(a, b); + break; + case T_A_Expr: + retval = _equalA_Expr(a, b); + break; + case T_ColumnRef: + retval = _equalColumnRef(a, b); + break; + case T_ParamRef: + retval = _equalParamRef(a, b); + break; + case T_A_Const: + retval = _equalA_Const(a, b); + break; + case T_FuncCall: + retval = _equalFuncCall(a, b); + break; + case T_A_Star: + retval = _equalA_Star(a, b); + break; + case T_A_Indices: + retval = _equalA_Indices(a, b); + break; + case T_A_Indirection: + retval = _equalA_Indirection(a, b); + break; + case T_A_ArrayExpr: + retval = _equalA_ArrayExpr(a, b); + break; + case T_ResTarget: + retval = _equalResTarget(a, b); + break; + case T_MultiAssignRef: + retval = _equalMultiAssignRef(a, b); + break; + case T_TypeCast: + retval = _equalTypeCast(a, b); + break; + case T_CollateClause: + retval = _equalCollateClause(a, b); + break; + case T_SortBy: + retval = _equalSortBy(a, b); + break; + case T_WindowDef: + retval = _equalWindowDef(a, b); + break; + case T_RangeSubselect: + retval = _equalRangeSubselect(a, b); + break; + case T_RangeFunction: + retval = _equalRangeFunction(a, b); + break; + case T_RangeTableSample: + retval = _equalRangeTableSample(a, b); + break; + case T_RangeTableFunc: + retval = _equalRangeTableFunc(a, b); + break; + case T_RangeTableFuncCol: + retval = _equalRangeTableFuncCol(a, b); + break; + case T_TypeName: + retval = _equalTypeName(a, b); + break; + case T_IndexElem: + retval = _equalIndexElem(a, b); + break; + case T_StatsElem: + retval = _equalStatsElem(a, b); + break; + case T_ColumnDef: + retval = _equalColumnDef(a, b); + break; + case T_Constraint: + retval = _equalConstraint(a, b); + break; + case T_DefElem: + retval = _equalDefElem(a, b); + break; + case T_LockingClause: + retval = _equalLockingClause(a, b); + break; + case T_RangeTblEntry: + retval = _equalRangeTblEntry(a, b); + break; + case T_RangeTblFunction: + retval = _equalRangeTblFunction(a, b); + break; + case T_TableSampleClause: + retval = _equalTableSampleClause(a, b); + break; + case T_WithCheckOption: + retval = _equalWithCheckOption(a, b); + break; + case T_SortGroupClause: + retval = _equalSortGroupClause(a, b); + break; + case T_GroupingSet: + retval = _equalGroupingSet(a, b); + break; + case T_WindowClause: + retval = _equalWindowClause(a, b); + break; + case T_RowMarkClause: + retval = _equalRowMarkClause(a, b); + break; + case T_WithClause: + retval = _equalWithClause(a, b); + break; + case T_InferClause: + retval = _equalInferClause(a, b); + break; + case T_OnConflictClause: + retval = _equalOnConflictClause(a, b); + break; + case T_CTESearchClause: + retval = _equalCTESearchClause(a, b); + break; + case T_CTECycleClause: + retval = _equalCTECycleClause(a, b); + break; + case T_CommonTableExpr: + retval = _equalCommonTableExpr(a, b); + break; + case T_MergeWhenClause: + retval = _equalMergeWhenClause(a, b); + break; + case T_MergeAction: + retval = _equalMergeAction(a, b); + break; + case T_ObjectWithArgs: + retval = _equalObjectWithArgs(a, b); + break; + case T_AccessPriv: + retval = _equalAccessPriv(a, b); + break; + case T_XmlSerialize: + retval = _equalXmlSerialize(a, b); + break; + case T_RoleSpec: + retval = _equalRoleSpec(a, b); + break; + case T_TriggerTransition: + retval = _equalTriggerTransition(a, b); + break; + case T_PartitionElem: + retval = _equalPartitionElem(a, b); + break; + case T_PartitionSpec: + retval = _equalPartitionSpec(a, b); + break; + case T_PartitionBoundSpec: + retval = _equalPartitionBoundSpec(a, b); + break; + case T_PartitionRangeDatum: + retval = _equalPartitionRangeDatum(a, b); + break; + case T_PartitionCmd: + retval = _equalPartitionCmd(a, b); + break; + case T_PublicationObjSpec: + retval = _equalPublicationObject(a, b); + break; + case T_PublicationTable: + retval = _equalPublicationTable(a, b); + break; + + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(a)); + retval = false; /* keep compiler quiet */ + break; + } + + return retval; +} diff --git a/src/backend/nodes/extensible.c b/src/backend/nodes/extensible.c new file mode 100644 index 0000000..50db4c1 --- /dev/null +++ b/src/backend/nodes/extensible.c @@ -0,0 +1,143 @@ +/*------------------------------------------------------------------------- + * + * extensible.c + * Support for extensible node types + * + * Loadable modules can define what are in effect new types of nodes using + * the routines in this file. All such nodes are flagged T_ExtensibleNode, + * with the extnodename field distinguishing the specific type. Use + * RegisterExtensibleNodeMethods to register a new type of extensible node, + * and GetExtensibleNodeMethods to get information about a previously + * registered type of extensible node. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/nodes/extensible.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/extensible.h" +#include "utils/hsearch.h" + +static HTAB *extensible_node_methods = NULL; +static HTAB *custom_scan_methods = NULL; + +typedef struct +{ + char extnodename[EXTNODENAME_MAX_LEN]; + const void *extnodemethods; +} ExtensibleNodeEntry; + +/* + * An internal function to register a new callback structure + */ +static void +RegisterExtensibleNodeEntry(HTAB **p_htable, const char *htable_label, + const char *extnodename, + const void *extnodemethods) +{ + ExtensibleNodeEntry *entry; + bool found; + + if (*p_htable == NULL) + { + HASHCTL ctl; + + ctl.keysize = EXTNODENAME_MAX_LEN; + ctl.entrysize = sizeof(ExtensibleNodeEntry); + + *p_htable = hash_create(htable_label, 100, &ctl, + HASH_ELEM | HASH_STRINGS); + } + + if (strlen(extnodename) >= EXTNODENAME_MAX_LEN) + elog(ERROR, "extensible node name is too long"); + + entry = (ExtensibleNodeEntry *) hash_search(*p_htable, + extnodename, + HASH_ENTER, &found); + if (found) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("extensible node type \"%s\" already exists", + extnodename))); + + entry->extnodemethods = extnodemethods; +} + +/* + * Register a new type of extensible node. + */ +void +RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *methods) +{ + RegisterExtensibleNodeEntry(&extensible_node_methods, + "Extensible Node Methods", + methods->extnodename, + methods); +} + +/* + * Register a new type of custom scan node + */ +void +RegisterCustomScanMethods(const CustomScanMethods *methods) +{ + RegisterExtensibleNodeEntry(&custom_scan_methods, + "Custom Scan Methods", + methods->CustomName, + methods); +} + +/* + * An internal routine to get an ExtensibleNodeEntry by the given identifier + */ +static const void * +GetExtensibleNodeEntry(HTAB *htable, const char *extnodename, bool missing_ok) +{ + ExtensibleNodeEntry *entry = NULL; + + if (htable != NULL) + entry = (ExtensibleNodeEntry *) hash_search(htable, + extnodename, + HASH_FIND, NULL); + if (!entry) + { + if (missing_ok) + return NULL; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("ExtensibleNodeMethods \"%s\" was not registered", + extnodename))); + } + + return entry->extnodemethods; +} + +/* + * Get the methods for a given type of extensible node. + */ +const ExtensibleNodeMethods * +GetExtensibleNodeMethods(const char *extnodename, bool missing_ok) +{ + return (const ExtensibleNodeMethods *) + GetExtensibleNodeEntry(extensible_node_methods, + extnodename, + missing_ok); +} + +/* + * Get the methods for a given name of CustomScanMethods + */ +const CustomScanMethods * +GetCustomScanMethods(const char *CustomName, bool missing_ok) +{ + return (const CustomScanMethods *) + GetExtensibleNodeEntry(custom_scan_methods, + CustomName, + missing_ok); +} diff --git a/src/backend/nodes/list.c b/src/backend/nodes/list.c new file mode 100644 index 0000000..90f93e8 --- /dev/null +++ b/src/backend/nodes/list.c @@ -0,0 +1,1676 @@ +/*------------------------------------------------------------------------- + * + * list.c + * implementation for PostgreSQL generic list package + * + * See comments in pg_list.h. + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/list.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "port/pg_bitutils.h" +#include "utils/memdebug.h" +#include "utils/memutils.h" + + +/* + * The previous List implementation, since it used a separate palloc chunk + * for each cons cell, had the property that adding or deleting list cells + * did not move the storage of other existing cells in the list. Quite a + * bit of existing code depended on that, by retaining ListCell pointers + * across such operations on a list. There is no such guarantee in this + * implementation, so instead we have debugging support that is meant to + * help flush out now-broken assumptions. Defining DEBUG_LIST_MEMORY_USAGE + * while building this file causes the List operations to forcibly move + * all cells in a list whenever a cell is added or deleted. In combination + * with MEMORY_CONTEXT_CHECKING and/or Valgrind, this can usually expose + * broken code. It's a bit expensive though, as there's many more palloc + * cycles and a lot more data-copying than in a default build. + * + * By default, we enable this when building for Valgrind. + */ +#ifdef USE_VALGRIND +#define DEBUG_LIST_MEMORY_USAGE +#endif + +/* Overhead for the fixed part of a List header, measured in ListCells */ +#define LIST_HEADER_OVERHEAD \ + ((int) ((offsetof(List, initial_elements) - 1) / sizeof(ListCell) + 1)) + +/* + * Macros to simplify writing assertions about the type of a list; a + * NIL list is considered to be an empty list of any type. + */ +#define IsPointerList(l) ((l) == NIL || IsA((l), List)) +#define IsIntegerList(l) ((l) == NIL || IsA((l), IntList)) +#define IsOidList(l) ((l) == NIL || IsA((l), OidList)) + +#ifdef USE_ASSERT_CHECKING +/* + * Check that the specified List is valid (so far as we can tell). + */ +static void +check_list_invariants(const List *list) +{ + if (list == NIL) + return; + + Assert(list->length > 0); + Assert(list->length <= list->max_length); + Assert(list->elements != NULL); + + Assert(list->type == T_List || + list->type == T_IntList || + list->type == T_OidList); +} +#else +#define check_list_invariants(l) ((void) 0) +#endif /* USE_ASSERT_CHECKING */ + +/* + * Return a freshly allocated List with room for at least min_size cells. + * + * Since empty non-NIL lists are invalid, new_list() sets the initial length + * to min_size, effectively marking that number of cells as valid; the caller + * is responsible for filling in their data. + */ +static List * +new_list(NodeTag type, int min_size) +{ + List *newlist; + int max_size; + + Assert(min_size > 0); + + /* + * We allocate all the requested cells, and possibly some more, as part of + * the same palloc request as the List header. This is a big win for the + * typical case of short fixed-length lists. It can lose if we allocate a + * moderately long list and then it gets extended; we'll be wasting more + * initial_elements[] space than if we'd made the header small. However, + * rounding up the request as we do in the normal code path provides some + * defense against small extensions. + */ + +#ifndef DEBUG_LIST_MEMORY_USAGE + + /* + * Normally, we set up a list with some extra cells, to allow it to grow + * without a repalloc. Prefer cell counts chosen to make the total + * allocation a power-of-2, since palloc would round it up to that anyway. + * (That stops being true for very large allocations, but very long lists + * are infrequent, so it doesn't seem worth special logic for such cases.) + * + * The minimum allocation is 8 ListCell units, providing either 4 or 5 + * available ListCells depending on the machine's word width. Counting + * palloc's overhead, this uses the same amount of space as a one-cell + * list did in the old implementation, and less space for any longer list. + * + * We needn't worry about integer overflow; no caller passes min_size + * that's more than twice the size of an existing list, so the size limits + * within palloc will ensure that we don't overflow here. + */ + max_size = pg_nextpower2_32(Max(8, min_size + LIST_HEADER_OVERHEAD)); + max_size -= LIST_HEADER_OVERHEAD; +#else + + /* + * For debugging, don't allow any extra space. This forces any cell + * addition to go through enlarge_list() and thus move the existing data. + */ + max_size = min_size; +#endif + + newlist = (List *) palloc(offsetof(List, initial_elements) + + max_size * sizeof(ListCell)); + newlist->type = type; + newlist->length = min_size; + newlist->max_length = max_size; + newlist->elements = newlist->initial_elements; + + return newlist; +} + +/* + * Enlarge an existing non-NIL List to have room for at least min_size cells. + * + * This does *not* update list->length, as some callers would find that + * inconvenient. (list->length had better be the correct number of existing + * valid cells, though.) + */ +static void +enlarge_list(List *list, int min_size) +{ + int new_max_len; + + Assert(min_size > list->max_length); /* else we shouldn't be here */ + +#ifndef DEBUG_LIST_MEMORY_USAGE + + /* + * As above, we prefer power-of-two total allocations; but here we need + * not account for list header overhead. + */ + + /* clamp the minimum value to 16, a semi-arbitrary small power of 2 */ + new_max_len = pg_nextpower2_32(Max(16, min_size)); + +#else + /* As above, don't allocate anything extra */ + new_max_len = min_size; +#endif + + if (list->elements == list->initial_elements) + { + /* + * Replace original in-line allocation with a separate palloc block. + * Ensure it is in the same memory context as the List header. (The + * previous List implementation did not offer any guarantees about + * keeping all list cells in the same context, but it seems reasonable + * to create such a guarantee now.) + */ + list->elements = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + new_max_len * sizeof(ListCell)); + memcpy(list->elements, list->initial_elements, + list->length * sizeof(ListCell)); + + /* + * We must not move the list header, so it's unsafe to try to reclaim + * the initial_elements[] space via repalloc. In debugging builds, + * however, we can clear that space and/or mark it inaccessible. + * (wipe_mem includes VALGRIND_MAKE_MEM_NOACCESS.) + */ +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(list->initial_elements, + list->max_length * sizeof(ListCell)); +#else + VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements, + list->max_length * sizeof(ListCell)); +#endif + } + else + { +#ifndef DEBUG_LIST_MEMORY_USAGE + /* Normally, let repalloc deal with enlargement */ + list->elements = (ListCell *) repalloc(list->elements, + new_max_len * sizeof(ListCell)); +#else + /* + * repalloc() might enlarge the space in-place, which we don't want + * for debugging purposes, so forcibly move the data somewhere else. + */ + ListCell *newelements; + + newelements = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + new_max_len * sizeof(ListCell)); + memcpy(newelements, list->elements, + list->length * sizeof(ListCell)); + pfree(list->elements); + list->elements = newelements; +#endif + } + + list->max_length = new_max_len; +} + +/* + * Convenience functions to construct short Lists from given values. + * (These are normally invoked via the list_makeN macros.) + */ +List * +list_make1_impl(NodeTag t, ListCell datum1) +{ + List *list = new_list(t, 1); + + list->elements[0] = datum1; + check_list_invariants(list); + return list; +} + +List * +list_make2_impl(NodeTag t, ListCell datum1, ListCell datum2) +{ + List *list = new_list(t, 2); + + list->elements[0] = datum1; + list->elements[1] = datum2; + check_list_invariants(list); + return list; +} + +List * +list_make3_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3) +{ + List *list = new_list(t, 3); + + list->elements[0] = datum1; + list->elements[1] = datum2; + list->elements[2] = datum3; + check_list_invariants(list); + return list; +} + +List * +list_make4_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3, ListCell datum4) +{ + List *list = new_list(t, 4); + + list->elements[0] = datum1; + list->elements[1] = datum2; + list->elements[2] = datum3; + list->elements[3] = datum4; + check_list_invariants(list); + return list; +} + +List * +list_make5_impl(NodeTag t, ListCell datum1, ListCell datum2, + ListCell datum3, ListCell datum4, ListCell datum5) +{ + List *list = new_list(t, 5); + + list->elements[0] = datum1; + list->elements[1] = datum2; + list->elements[2] = datum3; + list->elements[3] = datum4; + list->elements[4] = datum5; + check_list_invariants(list); + return list; +} + +/* + * Make room for a new head cell in the given (non-NIL) list. + * + * The data in the new head cell is undefined; the caller should be + * sure to fill it in + */ +static void +new_head_cell(List *list) +{ + /* Enlarge array if necessary */ + if (list->length >= list->max_length) + enlarge_list(list, list->length + 1); + /* Now shove the existing data over */ + memmove(&list->elements[1], &list->elements[0], + list->length * sizeof(ListCell)); + list->length++; +} + +/* + * Make room for a new tail cell in the given (non-NIL) list. + * + * The data in the new tail cell is undefined; the caller should be + * sure to fill it in + */ +static void +new_tail_cell(List *list) +{ + /* Enlarge array if necessary */ + if (list->length >= list->max_length) + enlarge_list(list, list->length + 1); + list->length++; +} + +/* + * Append a pointer to the list. A pointer to the modified list is + * returned. Note that this function may or may not destructively + * modify the list; callers should always use this function's return + * value, rather than continuing to use the pointer passed as the + * first argument. + */ +List * +lappend(List *list, void *datum) +{ + Assert(IsPointerList(list)); + + if (list == NIL) + list = new_list(T_List, 1); + else + new_tail_cell(list); + + llast(list) = datum; + check_list_invariants(list); + return list; +} + +/* + * Append an integer to the specified list. See lappend() + */ +List * +lappend_int(List *list, int datum) +{ + Assert(IsIntegerList(list)); + + if (list == NIL) + list = new_list(T_IntList, 1); + else + new_tail_cell(list); + + llast_int(list) = datum; + check_list_invariants(list); + return list; +} + +/* + * Append an OID to the specified list. See lappend() + */ +List * +lappend_oid(List *list, Oid datum) +{ + Assert(IsOidList(list)); + + if (list == NIL) + list = new_list(T_OidList, 1); + else + new_tail_cell(list); + + llast_oid(list) = datum; + check_list_invariants(list); + return list; +} + +/* + * Make room for a new cell at position 'pos' (measured from 0). + * The data in the cell is left undefined, and must be filled in by the + * caller. 'list' is assumed to be non-NIL, and 'pos' must be a valid + * list position, ie, 0 <= pos <= list's length. + * Returns address of the new cell. + */ +static ListCell * +insert_new_cell(List *list, int pos) +{ + Assert(pos >= 0 && pos <= list->length); + + /* Enlarge array if necessary */ + if (list->length >= list->max_length) + enlarge_list(list, list->length + 1); + /* Now shove the existing data over */ + if (pos < list->length) + memmove(&list->elements[pos + 1], &list->elements[pos], + (list->length - pos) * sizeof(ListCell)); + list->length++; + + return &list->elements[pos]; +} + +/* + * Insert the given datum at position 'pos' (measured from 0) in the list. + * 'pos' must be valid, ie, 0 <= pos <= list's length. + * + * Note that this takes time proportional to the distance to the end of the + * list, since the following entries must be moved. + */ +List * +list_insert_nth(List *list, int pos, void *datum) +{ + if (list == NIL) + { + Assert(pos == 0); + return list_make1(datum); + } + Assert(IsPointerList(list)); + lfirst(insert_new_cell(list, pos)) = datum; + check_list_invariants(list); + return list; +} + +List * +list_insert_nth_int(List *list, int pos, int datum) +{ + if (list == NIL) + { + Assert(pos == 0); + return list_make1_int(datum); + } + Assert(IsIntegerList(list)); + lfirst_int(insert_new_cell(list, pos)) = datum; + check_list_invariants(list); + return list; +} + +List * +list_insert_nth_oid(List *list, int pos, Oid datum) +{ + if (list == NIL) + { + Assert(pos == 0); + return list_make1_oid(datum); + } + Assert(IsOidList(list)); + lfirst_oid(insert_new_cell(list, pos)) = datum; + check_list_invariants(list); + return list; +} + +/* + * Prepend a new element to the list. A pointer to the modified list + * is returned. Note that this function may or may not destructively + * modify the list; callers should always use this function's return + * value, rather than continuing to use the pointer passed as the + * second argument. + * + * Note that this takes time proportional to the length of the list, + * since the existing entries must be moved. + * + * Caution: before Postgres 8.0, the original List was unmodified and + * could be considered to retain its separate identity. This is no longer + * the case. + */ +List * +lcons(void *datum, List *list) +{ + Assert(IsPointerList(list)); + + if (list == NIL) + list = new_list(T_List, 1); + else + new_head_cell(list); + + linitial(list) = datum; + check_list_invariants(list); + return list; +} + +/* + * Prepend an integer to the list. See lcons() + */ +List * +lcons_int(int datum, List *list) +{ + Assert(IsIntegerList(list)); + + if (list == NIL) + list = new_list(T_IntList, 1); + else + new_head_cell(list); + + linitial_int(list) = datum; + check_list_invariants(list); + return list; +} + +/* + * Prepend an OID to the list. See lcons() + */ +List * +lcons_oid(Oid datum, List *list) +{ + Assert(IsOidList(list)); + + if (list == NIL) + list = new_list(T_OidList, 1); + else + new_head_cell(list); + + linitial_oid(list) = datum; + check_list_invariants(list); + return list; +} + +/* + * Concatenate list2 to the end of list1, and return list1. + * + * This is equivalent to lappend'ing each element of list2, in order, to list1. + * list1 is destructively changed, list2 is not. (However, in the case of + * pointer lists, list1 and list2 will point to the same structures.) + * + * Callers should be sure to use the return value as the new pointer to the + * concatenated list: the 'list1' input pointer may or may not be the same + * as the returned pointer. + * + * Note that this takes at least time proportional to the length of list2. + * It'd typically be the case that we have to enlarge list1's storage, + * probably adding time proportional to the length of list1. + */ +List * +list_concat(List *list1, const List *list2) +{ + int new_len; + + if (list1 == NIL) + return list_copy(list2); + if (list2 == NIL) + return list1; + + Assert(list1->type == list2->type); + + new_len = list1->length + list2->length; + /* Enlarge array if necessary */ + if (new_len > list1->max_length) + enlarge_list(list1, new_len); + + /* Even if list1 == list2, using memcpy should be safe here */ + memcpy(&list1->elements[list1->length], &list2->elements[0], + list2->length * sizeof(ListCell)); + list1->length = new_len; + + check_list_invariants(list1); + return list1; +} + +/* + * Form a new list by concatenating the elements of list1 and list2. + * + * Neither input list is modified. (However, if they are pointer lists, + * the output list will point to the same structures.) + * + * This is equivalent to, but more efficient than, + * list_concat(list_copy(list1), list2). + * Note that some pre-v13 code might list_copy list2 as well, but that's + * pointless now. + */ +List * +list_concat_copy(const List *list1, const List *list2) +{ + List *result; + int new_len; + + if (list1 == NIL) + return list_copy(list2); + if (list2 == NIL) + return list_copy(list1); + + Assert(list1->type == list2->type); + + new_len = list1->length + list2->length; + result = new_list(list1->type, new_len); + memcpy(result->elements, list1->elements, + list1->length * sizeof(ListCell)); + memcpy(result->elements + list1->length, list2->elements, + list2->length * sizeof(ListCell)); + + check_list_invariants(result); + return result; +} + +/* + * Truncate 'list' to contain no more than 'new_size' elements. This + * modifies the list in-place! Despite this, callers should use the + * pointer returned by this function to refer to the newly truncated + * list -- it may or may not be the same as the pointer that was + * passed. + * + * Note that any cells removed by list_truncate() are NOT pfree'd. + */ +List * +list_truncate(List *list, int new_size) +{ + if (new_size <= 0) + return NIL; /* truncate to zero length */ + + /* If asked to effectively extend the list, do nothing */ + if (new_size < list_length(list)) + list->length = new_size; + + /* + * Note: unlike the individual-list-cell deletion functions, we don't move + * the list cells to new storage, even in DEBUG_LIST_MEMORY_USAGE mode. + * This is because none of them can move in this operation, so just like + * in the old cons-cell-based implementation, this function doesn't + * invalidate any pointers to cells of the list. This is also the reason + * for not wiping the memory of the deleted cells: the old code didn't + * free them either. Perhaps later we'll tighten this up. + */ + + return list; +} + +/* + * Return true iff 'datum' is a member of the list. Equality is + * determined via equal(), so callers should ensure that they pass a + * Node as 'datum'. + * + * This does a simple linear search --- avoid using it on long lists. + */ +bool +list_member(const List *list, const void *datum) +{ + const ListCell *cell; + + Assert(IsPointerList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (equal(lfirst(cell), datum)) + return true; + } + + return false; +} + +/* + * Return true iff 'datum' is a member of the list. Equality is + * determined by using simple pointer comparison. + */ +bool +list_member_ptr(const List *list, const void *datum) +{ + const ListCell *cell; + + Assert(IsPointerList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (lfirst(cell) == datum) + return true; + } + + return false; +} + +/* + * Return true iff the integer 'datum' is a member of the list. + */ +bool +list_member_int(const List *list, int datum) +{ + const ListCell *cell; + + Assert(IsIntegerList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (lfirst_int(cell) == datum) + return true; + } + + return false; +} + +/* + * Return true iff the OID 'datum' is a member of the list. + */ +bool +list_member_oid(const List *list, Oid datum) +{ + const ListCell *cell; + + Assert(IsOidList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (lfirst_oid(cell) == datum) + return true; + } + + return false; +} + +/* + * Delete the n'th cell (counting from 0) in list. + * + * The List is pfree'd if this was the last member. + * + * Note that this takes time proportional to the distance to the end of the + * list, since the following entries must be moved. + */ +List * +list_delete_nth_cell(List *list, int n) +{ + check_list_invariants(list); + + Assert(n >= 0 && n < list->length); + + /* + * If we're about to delete the last node from the list, free the whole + * list instead and return NIL, which is the only valid representation of + * a zero-length list. + */ + if (list->length == 1) + { + list_free(list); + return NIL; + } + + /* + * Otherwise, we normally just collapse out the removed element. But for + * debugging purposes, move the whole list contents someplace else. + * + * (Note that we *must* keep the contents in the same memory context.) + */ +#ifndef DEBUG_LIST_MEMORY_USAGE + memmove(&list->elements[n], &list->elements[n + 1], + (list->length - 1 - n) * sizeof(ListCell)); + list->length--; +#else + { + ListCell *newelems; + int newmaxlen = list->length - 1; + + newelems = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + newmaxlen * sizeof(ListCell)); + memcpy(newelems, list->elements, n * sizeof(ListCell)); + memcpy(&newelems[n], &list->elements[n + 1], + (list->length - 1 - n) * sizeof(ListCell)); + if (list->elements != list->initial_elements) + pfree(list->elements); + else + { + /* + * As in enlarge_list(), clear the initial_elements[] space and/or + * mark it inaccessible. + */ +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(list->initial_elements, + list->max_length * sizeof(ListCell)); +#else + VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements, + list->max_length * sizeof(ListCell)); +#endif + } + list->elements = newelems; + list->max_length = newmaxlen; + list->length--; + check_list_invariants(list); + } +#endif + + return list; +} + +/* + * Delete 'cell' from 'list'. + * + * The List is pfree'd if this was the last member. However, we do not + * touch any data the cell might've been pointing to. + * + * Note that this takes time proportional to the distance to the end of the + * list, since the following entries must be moved. + */ +List * +list_delete_cell(List *list, ListCell *cell) +{ + return list_delete_nth_cell(list, cell - list->elements); +} + +/* + * Delete the first cell in list that matches datum, if any. + * Equality is determined via equal(). + * + * This does a simple linear search --- avoid using it on long lists. + */ +List * +list_delete(List *list, void *datum) +{ + ListCell *cell; + + Assert(IsPointerList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (equal(lfirst(cell), datum)) + return list_delete_cell(list, cell); + } + + /* Didn't find a match: return the list unmodified */ + return list; +} + +/* As above, but use simple pointer equality */ +List * +list_delete_ptr(List *list, void *datum) +{ + ListCell *cell; + + Assert(IsPointerList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (lfirst(cell) == datum) + return list_delete_cell(list, cell); + } + + /* Didn't find a match: return the list unmodified */ + return list; +} + +/* As above, but for integers */ +List * +list_delete_int(List *list, int datum) +{ + ListCell *cell; + + Assert(IsIntegerList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (lfirst_int(cell) == datum) + return list_delete_cell(list, cell); + } + + /* Didn't find a match: return the list unmodified */ + return list; +} + +/* As above, but for OIDs */ +List * +list_delete_oid(List *list, Oid datum) +{ + ListCell *cell; + + Assert(IsOidList(list)); + check_list_invariants(list); + + foreach(cell, list) + { + if (lfirst_oid(cell) == datum) + return list_delete_cell(list, cell); + } + + /* Didn't find a match: return the list unmodified */ + return list; +} + +/* + * Delete the first element of the list. + * + * This is useful to replace the Lisp-y code "list = lnext(list);" in cases + * where the intent is to alter the list rather than just traverse it. + * Beware that the list is modified, whereas the Lisp-y coding leaves + * the original list head intact in case there's another pointer to it. + * + * Note that this takes time proportional to the length of the list, + * since the remaining entries must be moved. Consider reversing the + * list order so that you can use list_delete_last() instead. However, + * if that causes you to replace lappend() with lcons(), you haven't + * improved matters. (In short, you can make an efficient stack from + * a List, but not an efficient FIFO queue.) + */ +List * +list_delete_first(List *list) +{ + check_list_invariants(list); + + if (list == NIL) + return NIL; /* would an error be better? */ + + return list_delete_nth_cell(list, 0); +} + +/* + * Delete the last element of the list. + */ +List * +list_delete_last(List *list) +{ + check_list_invariants(list); + + if (list == NIL) + return NIL; /* would an error be better? */ + + /* list_truncate won't free list if it goes to empty, but this should */ + if (list_length(list) <= 1) + { + list_free(list); + return NIL; + } + + return list_truncate(list, list_length(list) - 1); +} + +/* + * Delete the first N cells of the list. + * + * The List is pfree'd if the request causes all cells to be deleted. + * + * Note that this takes time proportional to the distance to the end of the + * list, since the following entries must be moved. + */ +List * +list_delete_first_n(List *list, int n) +{ + check_list_invariants(list); + + /* No-op request? */ + if (n <= 0) + return list; + + /* Delete whole list? */ + if (n >= list_length(list)) + { + list_free(list); + return NIL; + } + + /* + * Otherwise, we normally just collapse out the removed elements. But for + * debugging purposes, move the whole list contents someplace else. + * + * (Note that we *must* keep the contents in the same memory context.) + */ +#ifndef DEBUG_LIST_MEMORY_USAGE + memmove(&list->elements[0], &list->elements[n], + (list->length - n) * sizeof(ListCell)); + list->length -= n; +#else + { + ListCell *newelems; + int newmaxlen = list->length - n; + + newelems = (ListCell *) + MemoryContextAlloc(GetMemoryChunkContext(list), + newmaxlen * sizeof(ListCell)); + memcpy(newelems, &list->elements[n], newmaxlen * sizeof(ListCell)); + if (list->elements != list->initial_elements) + pfree(list->elements); + else + { + /* + * As in enlarge_list(), clear the initial_elements[] space and/or + * mark it inaccessible. + */ +#ifdef CLOBBER_FREED_MEMORY + wipe_mem(list->initial_elements, + list->max_length * sizeof(ListCell)); +#else + VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements, + list->max_length * sizeof(ListCell)); +#endif + } + list->elements = newelems; + list->max_length = newmaxlen; + list->length = newmaxlen; + check_list_invariants(list); + } +#endif + + return list; +} + +/* + * Generate the union of two lists. This is calculated by copying + * list1 via list_copy(), then adding to it all the members of list2 + * that aren't already in list1. + * + * Whether an element is already a member of the list is determined + * via equal(). + * + * The returned list is newly-allocated, although the content of the + * cells is the same (i.e. any pointed-to objects are not copied). + * + * NB: this function will NOT remove any duplicates that are present + * in list1 (so it only performs a "union" if list1 is known unique to + * start with). Also, if you are about to write "x = list_union(x, y)" + * you probably want to use list_concat_unique() instead to avoid wasting + * the storage of the old x list. + * + * Note that this takes time proportional to the product of the list + * lengths, so beware of using it on long lists. (We could probably + * improve that, but really you should be using some other data structure + * if this'd be a performance bottleneck.) + */ +List * +list_union(const List *list1, const List *list2) +{ + List *result; + const ListCell *cell; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + result = list_copy(list1); + foreach(cell, list2) + { + if (!list_member(result, lfirst(cell))) + result = lappend(result, lfirst(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * This variant of list_union() determines duplicates via simple + * pointer comparison. + */ +List * +list_union_ptr(const List *list1, const List *list2) +{ + List *result; + const ListCell *cell; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + result = list_copy(list1); + foreach(cell, list2) + { + if (!list_member_ptr(result, lfirst(cell))) + result = lappend(result, lfirst(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * This variant of list_union() operates upon lists of integers. + */ +List * +list_union_int(const List *list1, const List *list2) +{ + List *result; + const ListCell *cell; + + Assert(IsIntegerList(list1)); + Assert(IsIntegerList(list2)); + + result = list_copy(list1); + foreach(cell, list2) + { + if (!list_member_int(result, lfirst_int(cell))) + result = lappend_int(result, lfirst_int(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * This variant of list_union() operates upon lists of OIDs. + */ +List * +list_union_oid(const List *list1, const List *list2) +{ + List *result; + const ListCell *cell; + + Assert(IsOidList(list1)); + Assert(IsOidList(list2)); + + result = list_copy(list1); + foreach(cell, list2) + { + if (!list_member_oid(result, lfirst_oid(cell))) + result = lappend_oid(result, lfirst_oid(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * Return a list that contains all the cells that are in both list1 and + * list2. The returned list is freshly allocated via palloc(), but the + * cells themselves point to the same objects as the cells of the + * input lists. + * + * Duplicate entries in list1 will not be suppressed, so it's only a true + * "intersection" if list1 is known unique beforehand. + * + * This variant works on lists of pointers, and determines list + * membership via equal(). Note that the list1 member will be pointed + * to in the result. + * + * Note that this takes time proportional to the product of the list + * lengths, so beware of using it on long lists. (We could probably + * improve that, but really you should be using some other data structure + * if this'd be a performance bottleneck.) + */ +List * +list_intersection(const List *list1, const List *list2) +{ + List *result; + const ListCell *cell; + + if (list1 == NIL || list2 == NIL) + return NIL; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + result = NIL; + foreach(cell, list1) + { + if (list_member(list2, lfirst(cell))) + result = lappend(result, lfirst(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * As list_intersection but operates on lists of integers. + */ +List * +list_intersection_int(const List *list1, const List *list2) +{ + List *result; + const ListCell *cell; + + if (list1 == NIL || list2 == NIL) + return NIL; + + Assert(IsIntegerList(list1)); + Assert(IsIntegerList(list2)); + + result = NIL; + foreach(cell, list1) + { + if (list_member_int(list2, lfirst_int(cell))) + result = lappend_int(result, lfirst_int(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * Return a list that contains all the cells in list1 that are not in + * list2. The returned list is freshly allocated via palloc(), but the + * cells themselves point to the same objects as the cells of the + * input lists. + * + * This variant works on lists of pointers, and determines list + * membership via equal() + * + * Note that this takes time proportional to the product of the list + * lengths, so beware of using it on long lists. (We could probably + * improve that, but really you should be using some other data structure + * if this'd be a performance bottleneck.) + */ +List * +list_difference(const List *list1, const List *list2) +{ + const ListCell *cell; + List *result = NIL; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + if (list2 == NIL) + return list_copy(list1); + + foreach(cell, list1) + { + if (!list_member(list2, lfirst(cell))) + result = lappend(result, lfirst(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * This variant of list_difference() determines list membership via + * simple pointer equality. + */ +List * +list_difference_ptr(const List *list1, const List *list2) +{ + const ListCell *cell; + List *result = NIL; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + if (list2 == NIL) + return list_copy(list1); + + foreach(cell, list1) + { + if (!list_member_ptr(list2, lfirst(cell))) + result = lappend(result, lfirst(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * This variant of list_difference() operates upon lists of integers. + */ +List * +list_difference_int(const List *list1, const List *list2) +{ + const ListCell *cell; + List *result = NIL; + + Assert(IsIntegerList(list1)); + Assert(IsIntegerList(list2)); + + if (list2 == NIL) + return list_copy(list1); + + foreach(cell, list1) + { + if (!list_member_int(list2, lfirst_int(cell))) + result = lappend_int(result, lfirst_int(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * This variant of list_difference() operates upon lists of OIDs. + */ +List * +list_difference_oid(const List *list1, const List *list2) +{ + const ListCell *cell; + List *result = NIL; + + Assert(IsOidList(list1)); + Assert(IsOidList(list2)); + + if (list2 == NIL) + return list_copy(list1); + + foreach(cell, list1) + { + if (!list_member_oid(list2, lfirst_oid(cell))) + result = lappend_oid(result, lfirst_oid(cell)); + } + + check_list_invariants(result); + return result; +} + +/* + * Append datum to list, but only if it isn't already in the list. + * + * Whether an element is already a member of the list is determined + * via equal(). + * + * This does a simple linear search --- avoid using it on long lists. + */ +List * +list_append_unique(List *list, void *datum) +{ + if (list_member(list, datum)) + return list; + else + return lappend(list, datum); +} + +/* + * This variant of list_append_unique() determines list membership via + * simple pointer equality. + */ +List * +list_append_unique_ptr(List *list, void *datum) +{ + if (list_member_ptr(list, datum)) + return list; + else + return lappend(list, datum); +} + +/* + * This variant of list_append_unique() operates upon lists of integers. + */ +List * +list_append_unique_int(List *list, int datum) +{ + if (list_member_int(list, datum)) + return list; + else + return lappend_int(list, datum); +} + +/* + * This variant of list_append_unique() operates upon lists of OIDs. + */ +List * +list_append_unique_oid(List *list, Oid datum) +{ + if (list_member_oid(list, datum)) + return list; + else + return lappend_oid(list, datum); +} + +/* + * Append to list1 each member of list2 that isn't already in list1. + * + * Whether an element is already a member of the list is determined + * via equal(). + * + * This is almost the same functionality as list_union(), but list1 is + * modified in-place rather than being copied. However, callers of this + * function may have strict ordering expectations -- i.e. that the relative + * order of those list2 elements that are not duplicates is preserved. + * + * Note that this takes time proportional to the product of the list + * lengths, so beware of using it on long lists. (We could probably + * improve that, but really you should be using some other data structure + * if this'd be a performance bottleneck.) + */ +List * +list_concat_unique(List *list1, const List *list2) +{ + ListCell *cell; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + foreach(cell, list2) + { + if (!list_member(list1, lfirst(cell))) + list1 = lappend(list1, lfirst(cell)); + } + + check_list_invariants(list1); + return list1; +} + +/* + * This variant of list_concat_unique() determines list membership via + * simple pointer equality. + */ +List * +list_concat_unique_ptr(List *list1, const List *list2) +{ + ListCell *cell; + + Assert(IsPointerList(list1)); + Assert(IsPointerList(list2)); + + foreach(cell, list2) + { + if (!list_member_ptr(list1, lfirst(cell))) + list1 = lappend(list1, lfirst(cell)); + } + + check_list_invariants(list1); + return list1; +} + +/* + * This variant of list_concat_unique() operates upon lists of integers. + */ +List * +list_concat_unique_int(List *list1, const List *list2) +{ + ListCell *cell; + + Assert(IsIntegerList(list1)); + Assert(IsIntegerList(list2)); + + foreach(cell, list2) + { + if (!list_member_int(list1, lfirst_int(cell))) + list1 = lappend_int(list1, lfirst_int(cell)); + } + + check_list_invariants(list1); + return list1; +} + +/* + * This variant of list_concat_unique() operates upon lists of OIDs. + */ +List * +list_concat_unique_oid(List *list1, const List *list2) +{ + ListCell *cell; + + Assert(IsOidList(list1)); + Assert(IsOidList(list2)); + + foreach(cell, list2) + { + if (!list_member_oid(list1, lfirst_oid(cell))) + list1 = lappend_oid(list1, lfirst_oid(cell)); + } + + check_list_invariants(list1); + return list1; +} + +/* + * Remove adjacent duplicates in a list of OIDs. + * + * It is caller's responsibility to have sorted the list to bring duplicates + * together, perhaps via list_sort(list, list_oid_cmp). + * + * Note that this takes time proportional to the length of the list. + */ +void +list_deduplicate_oid(List *list) +{ + int len; + + Assert(IsOidList(list)); + len = list_length(list); + if (len > 1) + { + ListCell *elements = list->elements; + int i = 0; + + for (int j = 1; j < len; j++) + { + if (elements[i].oid_value != elements[j].oid_value) + elements[++i].oid_value = elements[j].oid_value; + } + list->length = i + 1; + } + check_list_invariants(list); +} + +/* + * Free all storage in a list, and optionally the pointed-to elements + */ +static void +list_free_private(List *list, bool deep) +{ + if (list == NIL) + return; /* nothing to do */ + + check_list_invariants(list); + + if (deep) + { + for (int i = 0; i < list->length; i++) + pfree(lfirst(&list->elements[i])); + } + if (list->elements != list->initial_elements) + pfree(list->elements); + pfree(list); +} + +/* + * Free all the cells of the list, as well as the list itself. Any + * objects that are pointed-to by the cells of the list are NOT + * free'd. + * + * On return, the argument to this function has been freed, so the + * caller would be wise to set it to NIL for safety's sake. + */ +void +list_free(List *list) +{ + list_free_private(list, false); +} + +/* + * Free all the cells of the list, the list itself, and all the + * objects pointed-to by the cells of the list (each element in the + * list must contain a pointer to a palloc()'d region of memory!) + * + * On return, the argument to this function has been freed, so the + * caller would be wise to set it to NIL for safety's sake. + */ +void +list_free_deep(List *list) +{ + /* + * A "deep" free operation only makes sense on a list of pointers. + */ + Assert(IsPointerList(list)); + list_free_private(list, true); +} + +/* + * Return a shallow copy of the specified list. + */ +List * +list_copy(const List *oldlist) +{ + List *newlist; + + if (oldlist == NIL) + return NIL; + + newlist = new_list(oldlist->type, oldlist->length); + memcpy(newlist->elements, oldlist->elements, + newlist->length * sizeof(ListCell)); + + check_list_invariants(newlist); + return newlist; +} + +/* + * Return a shallow copy of the specified list containing only the first 'len' + * elements. If oldlist is shorter than 'len' then we copy the entire list. + */ +List * +list_copy_head(const List *oldlist, int len) +{ + List *newlist; + + if (oldlist == NIL || len <= 0) + return NIL; + + len = Min(oldlist->length, len); + + newlist = new_list(oldlist->type, len); + memcpy(newlist->elements, oldlist->elements, len * sizeof(ListCell)); + + check_list_invariants(newlist); + return newlist; +} + +/* + * Return a shallow copy of the specified list, without the first N elements. + */ +List * +list_copy_tail(const List *oldlist, int nskip) +{ + List *newlist; + + if (nskip < 0) + nskip = 0; /* would it be better to elog? */ + + if (oldlist == NIL || nskip >= oldlist->length) + return NIL; + + newlist = new_list(oldlist->type, oldlist->length - nskip); + memcpy(newlist->elements, &oldlist->elements[nskip], + newlist->length * sizeof(ListCell)); + + check_list_invariants(newlist); + return newlist; +} + +/* + * Return a deep copy of the specified list. + * + * The list elements are copied via copyObject(), so that this function's + * idea of a "deep" copy is considerably deeper than what list_free_deep() + * means by the same word. + */ +List * +list_copy_deep(const List *oldlist) +{ + List *newlist; + + if (oldlist == NIL) + return NIL; + + /* This is only sensible for pointer Lists */ + Assert(IsA(oldlist, List)); + + newlist = new_list(oldlist->type, oldlist->length); + for (int i = 0; i < newlist->length; i++) + lfirst(&newlist->elements[i]) = + copyObjectImpl(lfirst(&oldlist->elements[i])); + + check_list_invariants(newlist); + return newlist; +} + +/* + * Sort a list according to the specified comparator function. + * + * The list is sorted in-place. + * + * The comparator function is declared to receive arguments of type + * const ListCell *; this allows it to use lfirst() and variants + * without casting its arguments. Otherwise it behaves the same as + * the comparator function for standard qsort(). + * + * Like qsort(), this provides no guarantees about sort stability + * for equal keys. + * + * This is based on qsort(), so it likewise has O(N log N) runtime. + */ +void +list_sort(List *list, list_sort_comparator cmp) +{ + typedef int (*qsort_comparator) (const void *a, const void *b); + int len; + + check_list_invariants(list); + + /* Nothing to do if there's less than two elements */ + len = list_length(list); + if (len > 1) + qsort(list->elements, len, sizeof(ListCell), (qsort_comparator) cmp); +} + +/* + * list_sort comparator for sorting a list into ascending int order. + */ +int +list_int_cmp(const ListCell *p1, const ListCell *p2) +{ + int v1 = lfirst_int(p1); + int v2 = lfirst_int(p2); + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; +} + +/* + * list_sort comparator for sorting a list into ascending OID order. + */ +int +list_oid_cmp(const ListCell *p1, const ListCell *p2) +{ + Oid v1 = lfirst_oid(p1); + Oid v2 = lfirst_oid(p2); + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; +} diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c new file mode 100644 index 0000000..c85d8fe --- /dev/null +++ b/src/backend/nodes/makefuncs.c @@ -0,0 +1,820 @@ +/*------------------------------------------------------------------------- + * + * makefuncs.c + * creator functions for various nodes. The functions here are for the + * most frequently created nodes. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/makefuncs.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/pg_class.h" +#include "catalog/pg_type.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "utils/lsyscache.h" + + +/* + * makeA_Expr - + * makes an A_Expr node + */ +A_Expr * +makeA_Expr(A_Expr_Kind kind, List *name, + Node *lexpr, Node *rexpr, int location) +{ + A_Expr *a = makeNode(A_Expr); + + a->kind = kind; + a->name = name; + a->lexpr = lexpr; + a->rexpr = rexpr; + a->location = location; + return a; +} + +/* + * makeSimpleA_Expr - + * As above, given a simple (unqualified) operator name + */ +A_Expr * +makeSimpleA_Expr(A_Expr_Kind kind, char *name, + Node *lexpr, Node *rexpr, int location) +{ + A_Expr *a = makeNode(A_Expr); + + a->kind = kind; + a->name = list_make1(makeString((char *) name)); + a->lexpr = lexpr; + a->rexpr = rexpr; + a->location = location; + return a; +} + +/* + * makeVar - + * creates a Var node + */ +Var * +makeVar(int varno, + AttrNumber varattno, + Oid vartype, + int32 vartypmod, + Oid varcollid, + Index varlevelsup) +{ + Var *var = makeNode(Var); + + var->varno = varno; + var->varattno = varattno; + var->vartype = vartype; + var->vartypmod = vartypmod; + var->varcollid = varcollid; + var->varlevelsup = varlevelsup; + + /* + * Only a few callers need to make Var nodes with varnosyn/varattnosyn + * different from varno/varattno. We don't provide separate arguments for + * them, but just initialize them to the given varno/varattno. This + * reduces code clutter and chance of error for most callers. + */ + var->varnosyn = (Index) varno; + var->varattnosyn = varattno; + + /* Likewise, we just set location to "unknown" here */ + var->location = -1; + + return var; +} + +/* + * makeVarFromTargetEntry - + * convenience function to create a same-level Var node from a + * TargetEntry + */ +Var * +makeVarFromTargetEntry(int varno, + TargetEntry *tle) +{ + return makeVar(varno, + tle->resno, + exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr), + exprCollation((Node *) tle->expr), + 0); +} + +/* + * makeWholeRowVar - + * creates a Var node representing a whole row of the specified RTE + * + * A whole-row reference is a Var with varno set to the correct range + * table entry, and varattno == 0 to signal that it references the whole + * tuple. (Use of zero here is unclean, since it could easily be confused + * with error cases, but it's not worth changing now.) The vartype indicates + * a rowtype; either a named composite type, or a domain over a named + * composite type (only possible if the RTE is a function returning that), + * or RECORD. This function encapsulates the logic for determining the + * correct rowtype OID to use. + * + * If allowScalar is true, then for the case where the RTE is a single function + * returning a non-composite result type, we produce a normal Var referencing + * the function's result directly, instead of the single-column composite + * value that the whole-row notation might otherwise suggest. + */ +Var * +makeWholeRowVar(RangeTblEntry *rte, + int varno, + Index varlevelsup, + bool allowScalar) +{ + Var *result; + Oid toid; + Node *fexpr; + + switch (rte->rtekind) + { + case RTE_RELATION: + /* relation: the rowtype is a named composite type */ + toid = get_rel_type_id(rte->relid); + if (!OidIsValid(toid)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("relation \"%s\" does not have a composite type", + get_rel_name(rte->relid)))); + result = makeVar(varno, + InvalidAttrNumber, + toid, + -1, + InvalidOid, + varlevelsup); + break; + + case RTE_FUNCTION: + + /* + * If there's more than one function, or ordinality is requested, + * force a RECORD result, since there's certainly more than one + * column involved and it can't be a known named type. + */ + if (rte->funcordinality || list_length(rte->functions) != 1) + { + /* always produces an anonymous RECORD result */ + result = makeVar(varno, + InvalidAttrNumber, + RECORDOID, + -1, + InvalidOid, + varlevelsup); + break; + } + + fexpr = ((RangeTblFunction *) linitial(rte->functions))->funcexpr; + toid = exprType(fexpr); + if (type_is_rowtype(toid)) + { + /* func returns composite; same as relation case */ + result = makeVar(varno, + InvalidAttrNumber, + toid, + -1, + InvalidOid, + varlevelsup); + } + else if (allowScalar) + { + /* func returns scalar; just return its output as-is */ + result = makeVar(varno, + 1, + toid, + -1, + exprCollation(fexpr), + varlevelsup); + } + else + { + /* func returns scalar, but we want a composite result */ + result = makeVar(varno, + InvalidAttrNumber, + RECORDOID, + -1, + InvalidOid, + varlevelsup); + } + break; + + default: + + /* + * RTE is a join, subselect, tablefunc, or VALUES. We represent + * this as a whole-row Var of RECORD type. (Note that in most + * cases the Var will be expanded to a RowExpr during planning, + * but that is not our concern here.) + */ + result = makeVar(varno, + InvalidAttrNumber, + RECORDOID, + -1, + InvalidOid, + varlevelsup); + break; + } + + return result; +} + +/* + * makeTargetEntry - + * creates a TargetEntry node + */ +TargetEntry * +makeTargetEntry(Expr *expr, + AttrNumber resno, + char *resname, + bool resjunk) +{ + TargetEntry *tle = makeNode(TargetEntry); + + tle->expr = expr; + tle->resno = resno; + tle->resname = resname; + + /* + * We always set these fields to 0. If the caller wants to change them he + * must do so explicitly. Few callers do that, so omitting these + * arguments reduces the chance of error. + */ + tle->ressortgroupref = 0; + tle->resorigtbl = InvalidOid; + tle->resorigcol = 0; + + tle->resjunk = resjunk; + + return tle; +} + +/* + * flatCopyTargetEntry - + * duplicate a TargetEntry, but don't copy substructure + * + * This is commonly used when we just want to modify the resno or substitute + * a new expression. + */ +TargetEntry * +flatCopyTargetEntry(TargetEntry *src_tle) +{ + TargetEntry *tle = makeNode(TargetEntry); + + Assert(IsA(src_tle, TargetEntry)); + memcpy(tle, src_tle, sizeof(TargetEntry)); + return tle; +} + +/* + * makeFromExpr - + * creates a FromExpr node + */ +FromExpr * +makeFromExpr(List *fromlist, Node *quals) +{ + FromExpr *f = makeNode(FromExpr); + + f->fromlist = fromlist; + f->quals = quals; + return f; +} + +/* + * makeConst - + * creates a Const node + */ +Const * +makeConst(Oid consttype, + int32 consttypmod, + Oid constcollid, + int constlen, + Datum constvalue, + bool constisnull, + bool constbyval) +{ + Const *cnst = makeNode(Const); + + /* + * If it's a varlena value, force it to be in non-expanded (non-toasted) + * format; this avoids any possible dependency on external values and + * improves consistency of representation, which is important for equal(). + */ + if (!constisnull && constlen == -1) + constvalue = PointerGetDatum(PG_DETOAST_DATUM(constvalue)); + + cnst->consttype = consttype; + cnst->consttypmod = consttypmod; + cnst->constcollid = constcollid; + cnst->constlen = constlen; + cnst->constvalue = constvalue; + cnst->constisnull = constisnull; + cnst->constbyval = constbyval; + cnst->location = -1; /* "unknown" */ + + return cnst; +} + +/* + * makeNullConst - + * creates a Const node representing a NULL of the specified type/typmod + * + * This is a convenience routine that just saves a lookup of the type's + * storage properties. + */ +Const * +makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid) +{ + int16 typLen; + bool typByVal; + + get_typlenbyval(consttype, &typLen, &typByVal); + return makeConst(consttype, + consttypmod, + constcollid, + (int) typLen, + (Datum) 0, + true, + typByVal); +} + +/* + * makeBoolConst - + * creates a Const node representing a boolean value (can be NULL too) + */ +Node * +makeBoolConst(bool value, bool isnull) +{ + /* note that pg_type.h hardwires size of bool as 1 ... duplicate it */ + return (Node *) makeConst(BOOLOID, -1, InvalidOid, 1, + BoolGetDatum(value), isnull, true); +} + +/* + * makeBoolExpr - + * creates a BoolExpr node + */ +Expr * +makeBoolExpr(BoolExprType boolop, List *args, int location) +{ + BoolExpr *b = makeNode(BoolExpr); + + b->boolop = boolop; + b->args = args; + b->location = location; + + return (Expr *) b; +} + +/* + * makeAlias - + * creates an Alias node + * + * NOTE: the given name is copied, but the colnames list (if any) isn't. + */ +Alias * +makeAlias(const char *aliasname, List *colnames) +{ + Alias *a = makeNode(Alias); + + a->aliasname = pstrdup(aliasname); + a->colnames = colnames; + + return a; +} + +/* + * makeRelabelType - + * creates a RelabelType node + */ +RelabelType * +makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, Oid rcollid, + CoercionForm rformat) +{ + RelabelType *r = makeNode(RelabelType); + + r->arg = arg; + r->resulttype = rtype; + r->resulttypmod = rtypmod; + r->resultcollid = rcollid; + r->relabelformat = rformat; + r->location = -1; + + return r; +} + +/* + * makeRangeVar - + * creates a RangeVar node (rather oversimplified case) + */ +RangeVar * +makeRangeVar(char *schemaname, char *relname, int location) +{ + RangeVar *r = makeNode(RangeVar); + + r->catalogname = NULL; + r->schemaname = schemaname; + r->relname = relname; + r->inh = true; + r->relpersistence = RELPERSISTENCE_PERMANENT; + r->alias = NULL; + r->location = location; + + return r; +} + +/* + * makeTypeName - + * build a TypeName node for an unqualified name. + * + * typmod is defaulted, but can be changed later by caller. + */ +TypeName * +makeTypeName(char *typnam) +{ + return makeTypeNameFromNameList(list_make1(makeString(typnam))); +} + +/* + * makeTypeNameFromNameList - + * build a TypeName node for a String list representing a qualified name. + * + * typmod is defaulted, but can be changed later by caller. + */ +TypeName * +makeTypeNameFromNameList(List *names) +{ + TypeName *n = makeNode(TypeName); + + n->names = names; + n->typmods = NIL; + n->typemod = -1; + n->location = -1; + return n; +} + +/* + * makeTypeNameFromOid - + * build a TypeName node to represent a type already known by OID/typmod. + */ +TypeName * +makeTypeNameFromOid(Oid typeOid, int32 typmod) +{ + TypeName *n = makeNode(TypeName); + + n->typeOid = typeOid; + n->typemod = typmod; + n->location = -1; + return n; +} + +/* + * makeColumnDef - + * build a ColumnDef node to represent a simple column definition. + * + * Type and collation are specified by OID. + * Other properties are all basic to start with. + */ +ColumnDef * +makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid) +{ + ColumnDef *n = makeNode(ColumnDef); + + n->colname = pstrdup(colname); + n->typeName = makeTypeNameFromOid(typeOid, typmod); + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collClause = NULL; + n->collOid = collOid; + n->constraints = NIL; + n->fdwoptions = NIL; + n->location = -1; + + return n; +} + +/* + * makeFuncExpr - + * build an expression tree representing a function call. + * + * The argument expressions must have been transformed already. + */ +FuncExpr * +makeFuncExpr(Oid funcid, Oid rettype, List *args, + Oid funccollid, Oid inputcollid, CoercionForm fformat) +{ + FuncExpr *funcexpr; + + funcexpr = makeNode(FuncExpr); + funcexpr->funcid = funcid; + funcexpr->funcresulttype = rettype; + funcexpr->funcretset = false; /* only allowed case here */ + funcexpr->funcvariadic = false; /* only allowed case here */ + funcexpr->funcformat = fformat; + funcexpr->funccollid = funccollid; + funcexpr->inputcollid = inputcollid; + funcexpr->args = args; + funcexpr->location = -1; + + return funcexpr; +} + +/* + * makeDefElem - + * build a DefElem node + * + * This is sufficient for the "typical" case with an unqualified option name + * and no special action. + */ +DefElem * +makeDefElem(char *name, Node *arg, int location) +{ + DefElem *res = makeNode(DefElem); + + res->defnamespace = NULL; + res->defname = name; + res->arg = arg; + res->defaction = DEFELEM_UNSPEC; + res->location = location; + + return res; +} + +/* + * makeDefElemExtended - + * build a DefElem node with all fields available to be specified + */ +DefElem * +makeDefElemExtended(char *nameSpace, char *name, Node *arg, + DefElemAction defaction, int location) +{ + DefElem *res = makeNode(DefElem); + + res->defnamespace = nameSpace; + res->defname = name; + res->arg = arg; + res->defaction = defaction; + res->location = location; + + return res; +} + +/* + * makeFuncCall - + * + * Initialize a FuncCall struct with the information every caller must + * supply. Any non-default parameters have to be inserted by the caller. + */ +FuncCall * +makeFuncCall(List *name, List *args, CoercionForm funcformat, int location) +{ + FuncCall *n = makeNode(FuncCall); + + n->funcname = name; + n->args = args; + n->agg_order = NIL; + n->agg_filter = NULL; + n->over = NULL; + n->agg_within_group = false; + n->agg_star = false; + n->agg_distinct = false; + n->func_variadic = false; + n->funcformat = funcformat; + n->location = location; + return n; +} + +/* + * make_opclause + * Creates an operator clause given its operator info, left operand + * and right operand (pass NULL to create single-operand clause), + * and collation info. + */ +Expr * +make_opclause(Oid opno, Oid opresulttype, bool opretset, + Expr *leftop, Expr *rightop, + Oid opcollid, Oid inputcollid) +{ + OpExpr *expr = makeNode(OpExpr); + + expr->opno = opno; + expr->opfuncid = InvalidOid; + expr->opresulttype = opresulttype; + expr->opretset = opretset; + expr->opcollid = opcollid; + expr->inputcollid = inputcollid; + if (rightop) + expr->args = list_make2(leftop, rightop); + else + expr->args = list_make1(leftop); + expr->location = -1; + return (Expr *) expr; +} + +/* + * make_andclause + * + * Creates an 'and' clause given a list of its subclauses. + */ +Expr * +make_andclause(List *andclauses) +{ + BoolExpr *expr = makeNode(BoolExpr); + + expr->boolop = AND_EXPR; + expr->args = andclauses; + expr->location = -1; + return (Expr *) expr; +} + +/* + * make_orclause + * + * Creates an 'or' clause given a list of its subclauses. + */ +Expr * +make_orclause(List *orclauses) +{ + BoolExpr *expr = makeNode(BoolExpr); + + expr->boolop = OR_EXPR; + expr->args = orclauses; + expr->location = -1; + return (Expr *) expr; +} + +/* + * make_notclause + * + * Create a 'not' clause given the expression to be negated. + */ +Expr * +make_notclause(Expr *notclause) +{ + BoolExpr *expr = makeNode(BoolExpr); + + expr->boolop = NOT_EXPR; + expr->args = list_make1(notclause); + expr->location = -1; + return (Expr *) expr; +} + +/* + * make_and_qual + * + * Variant of make_andclause for ANDing two qual conditions together. + * Qual conditions have the property that a NULL nodetree is interpreted + * as 'true'. + * + * NB: this makes no attempt to preserve AND/OR flatness; so it should not + * be used on a qual that has already been run through prepqual.c. + */ +Node * +make_and_qual(Node *qual1, Node *qual2) +{ + if (qual1 == NULL) + return qual2; + if (qual2 == NULL) + return qual1; + return (Node *) make_andclause(list_make2(qual1, qual2)); +} + +/* + * The planner and executor usually represent qualification expressions + * as lists of boolean expressions with implicit AND semantics. + * + * These functions convert between an AND-semantics expression list and the + * ordinary representation of a boolean expression. + * + * Note that an empty list is considered equivalent to TRUE. + */ +Expr * +make_ands_explicit(List *andclauses) +{ + if (andclauses == NIL) + return (Expr *) makeBoolConst(true, false); + else if (list_length(andclauses) == 1) + return (Expr *) linitial(andclauses); + else + return make_andclause(andclauses); +} + +List * +make_ands_implicit(Expr *clause) +{ + /* + * NB: because the parser sets the qual field to NULL in a query that has + * no WHERE clause, we must consider a NULL input clause as TRUE, even + * though one might more reasonably think it FALSE. + */ + if (clause == NULL) + return NIL; /* NULL -> NIL list == TRUE */ + else if (is_andclause(clause)) + return ((BoolExpr *) clause)->args; + else if (IsA(clause, Const) && + !((Const *) clause)->constisnull && + DatumGetBool(((Const *) clause)->constvalue)) + return NIL; /* constant TRUE input -> NIL list */ + else + return list_make1(clause); +} + +/* + * makeIndexInfo + * create an IndexInfo node + */ +IndexInfo * +makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions, + List *predicates, bool unique, bool nulls_not_distinct, bool isready, bool concurrent) +{ + IndexInfo *n = makeNode(IndexInfo); + + n->ii_NumIndexAttrs = numattrs; + n->ii_NumIndexKeyAttrs = numkeyattrs; + Assert(n->ii_NumIndexKeyAttrs != 0); + Assert(n->ii_NumIndexKeyAttrs <= n->ii_NumIndexAttrs); + n->ii_Unique = unique; + n->ii_NullsNotDistinct = nulls_not_distinct; + n->ii_ReadyForInserts = isready; + n->ii_CheckedUnchanged = false; + n->ii_IndexUnchanged = false; + n->ii_Concurrent = concurrent; + + /* expressions */ + n->ii_Expressions = expressions; + n->ii_ExpressionsState = NIL; + + /* predicates */ + n->ii_Predicate = predicates; + n->ii_PredicateState = NULL; + + /* exclusion constraints */ + n->ii_ExclusionOps = NULL; + n->ii_ExclusionProcs = NULL; + n->ii_ExclusionStrats = NULL; + + /* opclass options */ + n->ii_OpclassOptions = NULL; + + /* speculative inserts */ + n->ii_UniqueOps = NULL; + n->ii_UniqueProcs = NULL; + n->ii_UniqueStrats = NULL; + + /* initialize index-build state to default */ + n->ii_BrokenHotChain = false; + n->ii_ParallelWorkers = 0; + + /* set up for possible use by index AM */ + n->ii_Am = amoid; + n->ii_AmCache = NULL; + n->ii_Context = CurrentMemoryContext; + + return n; +} + +/* + * makeGroupingSet + * + */ +GroupingSet * +makeGroupingSet(GroupingSetKind kind, List *content, int location) +{ + GroupingSet *n = makeNode(GroupingSet); + + n->kind = kind; + n->content = content; + n->location = location; + return n; +} + +/* + * makeVacuumRelation - + * create a VacuumRelation node + */ +VacuumRelation * +makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols) +{ + VacuumRelation *v = makeNode(VacuumRelation); + + v->relation = relation; + v->oid = oid; + v->va_cols = va_cols; + return v; +} diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c new file mode 100644 index 0000000..a7080f5 --- /dev/null +++ b/src/backend/nodes/nodeFuncs.c @@ -0,0 +1,4201 @@ +/*------------------------------------------------------------------------- + * + * nodeFuncs.c + * Various general-purpose manipulations of Node trees + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/nodeFuncs.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/pg_collation.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "nodes/execnodes.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "nodes/pathnodes.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" + +static bool expression_returns_set_walker(Node *node, void *context); +static int leftmostLoc(int loc1, int loc2); +static bool fix_opfuncids_walker(Node *node, void *context); +static bool planstate_walk_subplans(List *plans, bool (*walker) (), + void *context); +static bool planstate_walk_members(PlanState **planstates, int nplans, + bool (*walker) (), void *context); + + +/* + * exprType - + * returns the Oid of the type of the expression's result. + */ +Oid +exprType(const Node *expr) +{ + Oid type; + + if (!expr) + return InvalidOid; + + switch (nodeTag(expr)) + { + case T_Var: + type = ((const Var *) expr)->vartype; + break; + case T_Const: + type = ((const Const *) expr)->consttype; + break; + case T_Param: + type = ((const Param *) expr)->paramtype; + break; + case T_Aggref: + type = ((const Aggref *) expr)->aggtype; + break; + case T_GroupingFunc: + type = INT4OID; + break; + case T_WindowFunc: + type = ((const WindowFunc *) expr)->wintype; + break; + case T_SubscriptingRef: + type = ((const SubscriptingRef *) expr)->refrestype; + break; + case T_FuncExpr: + type = ((const FuncExpr *) expr)->funcresulttype; + break; + case T_NamedArgExpr: + type = exprType((Node *) ((const NamedArgExpr *) expr)->arg); + break; + case T_OpExpr: + type = ((const OpExpr *) expr)->opresulttype; + break; + case T_DistinctExpr: + type = ((const DistinctExpr *) expr)->opresulttype; + break; + case T_NullIfExpr: + type = ((const NullIfExpr *) expr)->opresulttype; + break; + case T_ScalarArrayOpExpr: + type = BOOLOID; + break; + case T_BoolExpr: + type = BOOLOID; + break; + case T_SubLink: + { + const SubLink *sublink = (const SubLink *) expr; + + if (sublink->subLinkType == EXPR_SUBLINK || + sublink->subLinkType == ARRAY_SUBLINK) + { + /* get the type of the subselect's first target column */ + Query *qtree = (Query *) sublink->subselect; + TargetEntry *tent; + + if (!qtree || !IsA(qtree, Query)) + elog(ERROR, "cannot get type for untransformed sublink"); + tent = linitial_node(TargetEntry, qtree->targetList); + Assert(!tent->resjunk); + type = exprType((Node *) tent->expr); + if (sublink->subLinkType == ARRAY_SUBLINK) + { + type = get_promoted_array_type(type); + if (!OidIsValid(type)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(exprType((Node *) tent->expr))))); + } + } + else if (sublink->subLinkType == MULTIEXPR_SUBLINK) + { + /* MULTIEXPR is always considered to return RECORD */ + type = RECORDOID; + } + else + { + /* for all other sublink types, result is boolean */ + type = BOOLOID; + } + } + break; + case T_SubPlan: + { + const SubPlan *subplan = (const SubPlan *) expr; + + if (subplan->subLinkType == EXPR_SUBLINK || + subplan->subLinkType == ARRAY_SUBLINK) + { + /* get the type of the subselect's first target column */ + type = subplan->firstColType; + if (subplan->subLinkType == ARRAY_SUBLINK) + { + type = get_promoted_array_type(type); + if (!OidIsValid(type)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(subplan->firstColType)))); + } + } + else if (subplan->subLinkType == MULTIEXPR_SUBLINK) + { + /* MULTIEXPR is always considered to return RECORD */ + type = RECORDOID; + } + else + { + /* for all other subplan types, result is boolean */ + type = BOOLOID; + } + } + break; + case T_AlternativeSubPlan: + { + const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr; + + /* subplans should all return the same thing */ + type = exprType((Node *) linitial(asplan->subplans)); + } + break; + case T_FieldSelect: + type = ((const FieldSelect *) expr)->resulttype; + break; + case T_FieldStore: + type = ((const FieldStore *) expr)->resulttype; + break; + case T_RelabelType: + type = ((const RelabelType *) expr)->resulttype; + break; + case T_CoerceViaIO: + type = ((const CoerceViaIO *) expr)->resulttype; + break; + case T_ArrayCoerceExpr: + type = ((const ArrayCoerceExpr *) expr)->resulttype; + break; + case T_ConvertRowtypeExpr: + type = ((const ConvertRowtypeExpr *) expr)->resulttype; + break; + case T_CollateExpr: + type = exprType((Node *) ((const CollateExpr *) expr)->arg); + break; + case T_CaseExpr: + type = ((const CaseExpr *) expr)->casetype; + break; + case T_CaseTestExpr: + type = ((const CaseTestExpr *) expr)->typeId; + break; + case T_ArrayExpr: + type = ((const ArrayExpr *) expr)->array_typeid; + break; + case T_RowExpr: + type = ((const RowExpr *) expr)->row_typeid; + break; + case T_RowCompareExpr: + type = BOOLOID; + break; + case T_CoalesceExpr: + type = ((const CoalesceExpr *) expr)->coalescetype; + break; + case T_MinMaxExpr: + type = ((const MinMaxExpr *) expr)->minmaxtype; + break; + case T_SQLValueFunction: + type = ((const SQLValueFunction *) expr)->type; + break; + case T_XmlExpr: + if (((const XmlExpr *) expr)->op == IS_DOCUMENT) + type = BOOLOID; + else if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE) + type = TEXTOID; + else + type = XMLOID; + break; + case T_NullTest: + type = BOOLOID; + break; + case T_BooleanTest: + type = BOOLOID; + break; + case T_CoerceToDomain: + type = ((const CoerceToDomain *) expr)->resulttype; + break; + case T_CoerceToDomainValue: + type = ((const CoerceToDomainValue *) expr)->typeId; + break; + case T_SetToDefault: + type = ((const SetToDefault *) expr)->typeId; + break; + case T_CurrentOfExpr: + type = BOOLOID; + break; + case T_NextValueExpr: + type = ((const NextValueExpr *) expr)->typeId; + break; + case T_InferenceElem: + { + const InferenceElem *n = (const InferenceElem *) expr; + + type = exprType((Node *) n->expr); + } + break; + case T_PlaceHolderVar: + type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr); + break; + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); + type = InvalidOid; /* keep compiler quiet */ + break; + } + return type; +} + +/* + * exprTypmod - + * returns the type-specific modifier of the expression's result type, + * if it can be determined. In many cases, it can't and we return -1. + */ +int32 +exprTypmod(const Node *expr) +{ + if (!expr) + return -1; + + switch (nodeTag(expr)) + { + case T_Var: + return ((const Var *) expr)->vartypmod; + case T_Const: + return ((const Const *) expr)->consttypmod; + case T_Param: + return ((const Param *) expr)->paramtypmod; + case T_SubscriptingRef: + return ((const SubscriptingRef *) expr)->reftypmod; + case T_FuncExpr: + { + int32 coercedTypmod; + + /* Be smart about length-coercion functions... */ + if (exprIsLengthCoercion(expr, &coercedTypmod)) + return coercedTypmod; + } + break; + case T_NamedArgExpr: + return exprTypmod((Node *) ((const NamedArgExpr *) expr)->arg); + case T_NullIfExpr: + { + /* + * Result is either first argument or NULL, so we can report + * first argument's typmod if known. + */ + const NullIfExpr *nexpr = (const NullIfExpr *) expr; + + return exprTypmod((Node *) linitial(nexpr->args)); + } + break; + case T_SubLink: + { + const SubLink *sublink = (const SubLink *) expr; + + if (sublink->subLinkType == EXPR_SUBLINK || + sublink->subLinkType == ARRAY_SUBLINK) + { + /* get the typmod of the subselect's first target column */ + Query *qtree = (Query *) sublink->subselect; + TargetEntry *tent; + + if (!qtree || !IsA(qtree, Query)) + elog(ERROR, "cannot get type for untransformed sublink"); + tent = linitial_node(TargetEntry, qtree->targetList); + Assert(!tent->resjunk); + return exprTypmod((Node *) tent->expr); + /* note we don't need to care if it's an array */ + } + /* otherwise, result is RECORD or BOOLEAN, typmod is -1 */ + } + break; + case T_SubPlan: + { + const SubPlan *subplan = (const SubPlan *) expr; + + if (subplan->subLinkType == EXPR_SUBLINK || + subplan->subLinkType == ARRAY_SUBLINK) + { + /* get the typmod of the subselect's first target column */ + /* note we don't need to care if it's an array */ + return subplan->firstColTypmod; + } + /* otherwise, result is RECORD or BOOLEAN, typmod is -1 */ + } + break; + case T_AlternativeSubPlan: + { + const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr; + + /* subplans should all return the same thing */ + return exprTypmod((Node *) linitial(asplan->subplans)); + } + break; + case T_FieldSelect: + return ((const FieldSelect *) expr)->resulttypmod; + case T_RelabelType: + return ((const RelabelType *) expr)->resulttypmod; + case T_ArrayCoerceExpr: + return ((const ArrayCoerceExpr *) expr)->resulttypmod; + case T_CollateExpr: + return exprTypmod((Node *) ((const CollateExpr *) expr)->arg); + case T_CaseExpr: + { + /* + * If all the alternatives agree on type/typmod, return that + * typmod, else use -1 + */ + const CaseExpr *cexpr = (const CaseExpr *) expr; + Oid casetype = cexpr->casetype; + int32 typmod; + ListCell *arg; + + if (!cexpr->defresult) + return -1; + if (exprType((Node *) cexpr->defresult) != casetype) + return -1; + typmod = exprTypmod((Node *) cexpr->defresult); + if (typmod < 0) + return -1; /* no point in trying harder */ + foreach(arg, cexpr->args) + { + CaseWhen *w = lfirst_node(CaseWhen, arg); + + if (exprType((Node *) w->result) != casetype) + return -1; + if (exprTypmod((Node *) w->result) != typmod) + return -1; + } + return typmod; + } + break; + case T_CaseTestExpr: + return ((const CaseTestExpr *) expr)->typeMod; + case T_ArrayExpr: + { + /* + * If all the elements agree on type/typmod, return that + * typmod, else use -1 + */ + const ArrayExpr *arrayexpr = (const ArrayExpr *) expr; + Oid commontype; + int32 typmod; + ListCell *elem; + + if (arrayexpr->elements == NIL) + return -1; + typmod = exprTypmod((Node *) linitial(arrayexpr->elements)); + if (typmod < 0) + return -1; /* no point in trying harder */ + if (arrayexpr->multidims) + commontype = arrayexpr->array_typeid; + else + commontype = arrayexpr->element_typeid; + foreach(elem, arrayexpr->elements) + { + Node *e = (Node *) lfirst(elem); + + if (exprType(e) != commontype) + return -1; + if (exprTypmod(e) != typmod) + return -1; + } + return typmod; + } + break; + case T_CoalesceExpr: + { + /* + * If all the alternatives agree on type/typmod, return that + * typmod, else use -1 + */ + const CoalesceExpr *cexpr = (const CoalesceExpr *) expr; + Oid coalescetype = cexpr->coalescetype; + int32 typmod; + ListCell *arg; + + if (exprType((Node *) linitial(cexpr->args)) != coalescetype) + return -1; + typmod = exprTypmod((Node *) linitial(cexpr->args)); + if (typmod < 0) + return -1; /* no point in trying harder */ + for_each_from(arg, cexpr->args, 1) + { + Node *e = (Node *) lfirst(arg); + + if (exprType(e) != coalescetype) + return -1; + if (exprTypmod(e) != typmod) + return -1; + } + return typmod; + } + break; + case T_MinMaxExpr: + { + /* + * If all the alternatives agree on type/typmod, return that + * typmod, else use -1 + */ + const MinMaxExpr *mexpr = (const MinMaxExpr *) expr; + Oid minmaxtype = mexpr->minmaxtype; + int32 typmod; + ListCell *arg; + + if (exprType((Node *) linitial(mexpr->args)) != minmaxtype) + return -1; + typmod = exprTypmod((Node *) linitial(mexpr->args)); + if (typmod < 0) + return -1; /* no point in trying harder */ + for_each_from(arg, mexpr->args, 1) + { + Node *e = (Node *) lfirst(arg); + + if (exprType(e) != minmaxtype) + return -1; + if (exprTypmod(e) != typmod) + return -1; + } + return typmod; + } + break; + case T_SQLValueFunction: + return ((const SQLValueFunction *) expr)->typmod; + case T_CoerceToDomain: + return ((const CoerceToDomain *) expr)->resulttypmod; + case T_CoerceToDomainValue: + return ((const CoerceToDomainValue *) expr)->typeMod; + case T_SetToDefault: + return ((const SetToDefault *) expr)->typeMod; + case T_PlaceHolderVar: + return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr); + default: + break; + } + return -1; +} + +/* + * exprIsLengthCoercion + * Detect whether an expression tree is an application of a datatype's + * typmod-coercion function. Optionally extract the result's typmod. + * + * If coercedTypmod is not NULL, the typmod is stored there if the expression + * is a length-coercion function, else -1 is stored there. + * + * Note that a combined type-and-length coercion will be treated as a + * length coercion by this routine. + */ +bool +exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod) +{ + if (coercedTypmod != NULL) + *coercedTypmod = -1; /* default result on failure */ + + /* + * Scalar-type length coercions are FuncExprs, array-type length coercions + * are ArrayCoerceExprs + */ + if (expr && IsA(expr, FuncExpr)) + { + const FuncExpr *func = (const FuncExpr *) expr; + int nargs; + Const *second_arg; + + /* + * If it didn't come from a coercion context, reject. + */ + if (func->funcformat != COERCE_EXPLICIT_CAST && + func->funcformat != COERCE_IMPLICIT_CAST) + return false; + + /* + * If it's not a two-argument or three-argument function with the + * second argument being an int4 constant, it can't have been created + * from a length coercion (it must be a type coercion, instead). + */ + nargs = list_length(func->args); + if (nargs < 2 || nargs > 3) + return false; + + second_arg = (Const *) lsecond(func->args); + if (!IsA(second_arg, Const) || + second_arg->consttype != INT4OID || + second_arg->constisnull) + return false; + + /* + * OK, it is indeed a length-coercion function. + */ + if (coercedTypmod != NULL) + *coercedTypmod = DatumGetInt32(second_arg->constvalue); + + return true; + } + + if (expr && IsA(expr, ArrayCoerceExpr)) + { + const ArrayCoerceExpr *acoerce = (const ArrayCoerceExpr *) expr; + + /* It's not a length coercion unless there's a nondefault typmod */ + if (acoerce->resulttypmod < 0) + return false; + + /* + * OK, it is indeed a length-coercion expression. + */ + if (coercedTypmod != NULL) + *coercedTypmod = acoerce->resulttypmod; + + return true; + } + + return false; +} + +/* + * applyRelabelType + * Add a RelabelType node if needed to make the expression expose + * the specified type, typmod, and collation. + * + * This is primarily intended to be used during planning. Therefore, it must + * maintain the post-eval_const_expressions invariants that there are not + * adjacent RelabelTypes, and that the tree is fully const-folded (hence, + * we mustn't return a RelabelType atop a Const). If we do find a Const, + * we'll modify it in-place if "overwrite_ok" is true; that should only be + * passed as true if caller knows the Const is newly generated. + */ +Node * +applyRelabelType(Node *arg, Oid rtype, int32 rtypmod, Oid rcollid, + CoercionForm rformat, int rlocation, bool overwrite_ok) +{ + /* + * If we find stacked RelabelTypes (eg, from foo::int::oid) we can discard + * all but the top one, and must do so to ensure that semantically + * equivalent expressions are equal(). + */ + while (arg && IsA(arg, RelabelType)) + arg = (Node *) ((RelabelType *) arg)->arg; + + if (arg && IsA(arg, Const)) + { + /* Modify the Const directly to preserve const-flatness. */ + Const *con = (Const *) arg; + + if (!overwrite_ok) + con = copyObject(con); + con->consttype = rtype; + con->consttypmod = rtypmod; + con->constcollid = rcollid; + /* We keep the Const's original location. */ + return (Node *) con; + } + else if (exprType(arg) == rtype && + exprTypmod(arg) == rtypmod && + exprCollation(arg) == rcollid) + { + /* Sometimes we find a nest of relabels that net out to nothing. */ + return arg; + } + else + { + /* Nope, gotta have a RelabelType. */ + RelabelType *newrelabel = makeNode(RelabelType); + + newrelabel->arg = (Expr *) arg; + newrelabel->resulttype = rtype; + newrelabel->resulttypmod = rtypmod; + newrelabel->resultcollid = rcollid; + newrelabel->relabelformat = rformat; + newrelabel->location = rlocation; + return (Node *) newrelabel; + } +} + +/* + * relabel_to_typmod + * Add a RelabelType node that changes just the typmod of the expression. + * + * Convenience function for a common usage of applyRelabelType. + */ +Node * +relabel_to_typmod(Node *expr, int32 typmod) +{ + return applyRelabelType(expr, exprType(expr), typmod, exprCollation(expr), + COERCE_EXPLICIT_CAST, -1, false); +} + +/* + * strip_implicit_coercions: remove implicit coercions at top level of tree + * + * This doesn't modify or copy the input expression tree, just return a + * pointer to a suitable place within it. + * + * Note: there isn't any useful thing we can do with a RowExpr here, so + * just return it unchanged, even if it's marked as an implicit coercion. + */ +Node * +strip_implicit_coercions(Node *node) +{ + if (node == NULL) + return NULL; + if (IsA(node, FuncExpr)) + { + FuncExpr *f = (FuncExpr *) node; + + if (f->funcformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions(linitial(f->args)); + } + else if (IsA(node, RelabelType)) + { + RelabelType *r = (RelabelType *) node; + + if (r->relabelformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) r->arg); + } + else if (IsA(node, CoerceViaIO)) + { + CoerceViaIO *c = (CoerceViaIO *) node; + + if (c->coerceformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) c->arg); + } + else if (IsA(node, ArrayCoerceExpr)) + { + ArrayCoerceExpr *c = (ArrayCoerceExpr *) node; + + if (c->coerceformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) c->arg); + } + else if (IsA(node, ConvertRowtypeExpr)) + { + ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node; + + if (c->convertformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) c->arg); + } + else if (IsA(node, CoerceToDomain)) + { + CoerceToDomain *c = (CoerceToDomain *) node; + + if (c->coercionformat == COERCE_IMPLICIT_CAST) + return strip_implicit_coercions((Node *) c->arg); + } + return node; +} + +/* + * expression_returns_set + * Test whether an expression returns a set result. + * + * Because we use expression_tree_walker(), this can also be applied to + * whole targetlists; it'll produce true if any one of the tlist items + * returns a set. + */ +bool +expression_returns_set(Node *clause) +{ + return expression_returns_set_walker(clause, NULL); +} + +static bool +expression_returns_set_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, FuncExpr)) + { + FuncExpr *expr = (FuncExpr *) node; + + if (expr->funcretset) + return true; + /* else fall through to check args */ + } + if (IsA(node, OpExpr)) + { + OpExpr *expr = (OpExpr *) node; + + if (expr->opretset) + return true; + /* else fall through to check args */ + } + + /* + * If you add any more cases that return sets, also fix + * expression_returns_set_rows() in clauses.c and IS_SRF_CALL() in + * tlist.c. + */ + + /* Avoid recursion for some cases that parser checks not to return a set */ + if (IsA(node, Aggref)) + return false; + if (IsA(node, GroupingFunc)) + return false; + if (IsA(node, WindowFunc)) + return false; + + return expression_tree_walker(node, expression_returns_set_walker, + context); +} + + +/* + * exprCollation - + * returns the Oid of the collation of the expression's result. + * + * Note: expression nodes that can invoke functions generally have an + * "inputcollid" field, which is what the function should use as collation. + * That is the resolved common collation of the node's inputs. It is often + * but not always the same as the result collation; in particular, if the + * function produces a non-collatable result type from collatable inputs + * or vice versa, the two are different. + */ +Oid +exprCollation(const Node *expr) +{ + Oid coll; + + if (!expr) + return InvalidOid; + + switch (nodeTag(expr)) + { + case T_Var: + coll = ((const Var *) expr)->varcollid; + break; + case T_Const: + coll = ((const Const *) expr)->constcollid; + break; + case T_Param: + coll = ((const Param *) expr)->paramcollid; + break; + case T_Aggref: + coll = ((const Aggref *) expr)->aggcollid; + break; + case T_GroupingFunc: + coll = InvalidOid; + break; + case T_WindowFunc: + coll = ((const WindowFunc *) expr)->wincollid; + break; + case T_SubscriptingRef: + coll = ((const SubscriptingRef *) expr)->refcollid; + break; + case T_FuncExpr: + coll = ((const FuncExpr *) expr)->funccollid; + break; + case T_NamedArgExpr: + coll = exprCollation((Node *) ((const NamedArgExpr *) expr)->arg); + break; + case T_OpExpr: + coll = ((const OpExpr *) expr)->opcollid; + break; + case T_DistinctExpr: + coll = ((const DistinctExpr *) expr)->opcollid; + break; + case T_NullIfExpr: + coll = ((const NullIfExpr *) expr)->opcollid; + break; + case T_ScalarArrayOpExpr: + /* ScalarArrayOpExpr's result is boolean ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_BoolExpr: + /* BoolExpr's result is boolean ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_SubLink: + { + const SubLink *sublink = (const SubLink *) expr; + + if (sublink->subLinkType == EXPR_SUBLINK || + sublink->subLinkType == ARRAY_SUBLINK) + { + /* get the collation of subselect's first target column */ + Query *qtree = (Query *) sublink->subselect; + TargetEntry *tent; + + if (!qtree || !IsA(qtree, Query)) + elog(ERROR, "cannot get collation for untransformed sublink"); + tent = linitial_node(TargetEntry, qtree->targetList); + Assert(!tent->resjunk); + coll = exprCollation((Node *) tent->expr); + /* collation doesn't change if it's converted to array */ + } + else + { + /* otherwise, SubLink's result is RECORD or BOOLEAN */ + coll = InvalidOid; /* ... so it has no collation */ + } + } + break; + case T_SubPlan: + { + const SubPlan *subplan = (const SubPlan *) expr; + + if (subplan->subLinkType == EXPR_SUBLINK || + subplan->subLinkType == ARRAY_SUBLINK) + { + /* get the collation of subselect's first target column */ + coll = subplan->firstColCollation; + /* collation doesn't change if it's converted to array */ + } + else + { + /* otherwise, SubPlan's result is RECORD or BOOLEAN */ + coll = InvalidOid; /* ... so it has no collation */ + } + } + break; + case T_AlternativeSubPlan: + { + const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr; + + /* subplans should all return the same thing */ + coll = exprCollation((Node *) linitial(asplan->subplans)); + } + break; + case T_FieldSelect: + coll = ((const FieldSelect *) expr)->resultcollid; + break; + case T_FieldStore: + /* FieldStore's result is composite ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_RelabelType: + coll = ((const RelabelType *) expr)->resultcollid; + break; + case T_CoerceViaIO: + coll = ((const CoerceViaIO *) expr)->resultcollid; + break; + case T_ArrayCoerceExpr: + coll = ((const ArrayCoerceExpr *) expr)->resultcollid; + break; + case T_ConvertRowtypeExpr: + /* ConvertRowtypeExpr's result is composite ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_CollateExpr: + coll = ((const CollateExpr *) expr)->collOid; + break; + case T_CaseExpr: + coll = ((const CaseExpr *) expr)->casecollid; + break; + case T_CaseTestExpr: + coll = ((const CaseTestExpr *) expr)->collation; + break; + case T_ArrayExpr: + coll = ((const ArrayExpr *) expr)->array_collid; + break; + case T_RowExpr: + /* RowExpr's result is composite ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_RowCompareExpr: + /* RowCompareExpr's result is boolean ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_CoalesceExpr: + coll = ((const CoalesceExpr *) expr)->coalescecollid; + break; + case T_MinMaxExpr: + coll = ((const MinMaxExpr *) expr)->minmaxcollid; + break; + case T_SQLValueFunction: + /* Returns either NAME or a non-collatable type */ + if (((const SQLValueFunction *) expr)->type == NAMEOID) + coll = C_COLLATION_OID; + else + coll = InvalidOid; + break; + case T_XmlExpr: + + /* + * XMLSERIALIZE returns text from non-collatable inputs, so its + * collation is always default. The other cases return boolean or + * XML, which are non-collatable. + */ + if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE) + coll = DEFAULT_COLLATION_OID; + else + coll = InvalidOid; + break; + case T_NullTest: + /* NullTest's result is boolean ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_BooleanTest: + /* BooleanTest's result is boolean ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_CoerceToDomain: + coll = ((const CoerceToDomain *) expr)->resultcollid; + break; + case T_CoerceToDomainValue: + coll = ((const CoerceToDomainValue *) expr)->collation; + break; + case T_SetToDefault: + coll = ((const SetToDefault *) expr)->collation; + break; + case T_CurrentOfExpr: + /* CurrentOfExpr's result is boolean ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_NextValueExpr: + /* NextValueExpr's result is an integer type ... */ + coll = InvalidOid; /* ... so it has no collation */ + break; + case T_InferenceElem: + coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr); + break; + case T_PlaceHolderVar: + coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr); + break; + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); + coll = InvalidOid; /* keep compiler quiet */ + break; + } + return coll; +} + +/* + * exprInputCollation - + * returns the Oid of the collation a function should use, if available. + * + * Result is InvalidOid if the node type doesn't store this information. + */ +Oid +exprInputCollation(const Node *expr) +{ + Oid coll; + + if (!expr) + return InvalidOid; + + switch (nodeTag(expr)) + { + case T_Aggref: + coll = ((const Aggref *) expr)->inputcollid; + break; + case T_WindowFunc: + coll = ((const WindowFunc *) expr)->inputcollid; + break; + case T_FuncExpr: + coll = ((const FuncExpr *) expr)->inputcollid; + break; + case T_OpExpr: + coll = ((const OpExpr *) expr)->inputcollid; + break; + case T_DistinctExpr: + coll = ((const DistinctExpr *) expr)->inputcollid; + break; + case T_NullIfExpr: + coll = ((const NullIfExpr *) expr)->inputcollid; + break; + case T_ScalarArrayOpExpr: + coll = ((const ScalarArrayOpExpr *) expr)->inputcollid; + break; + case T_MinMaxExpr: + coll = ((const MinMaxExpr *) expr)->inputcollid; + break; + default: + coll = InvalidOid; + break; + } + return coll; +} + +/* + * exprSetCollation - + * Assign collation information to an expression tree node. + * + * Note: since this is only used during parse analysis, we don't need to + * worry about subplans or PlaceHolderVars. + */ +void +exprSetCollation(Node *expr, Oid collation) +{ + switch (nodeTag(expr)) + { + case T_Var: + ((Var *) expr)->varcollid = collation; + break; + case T_Const: + ((Const *) expr)->constcollid = collation; + break; + case T_Param: + ((Param *) expr)->paramcollid = collation; + break; + case T_Aggref: + ((Aggref *) expr)->aggcollid = collation; + break; + case T_GroupingFunc: + Assert(!OidIsValid(collation)); + break; + case T_WindowFunc: + ((WindowFunc *) expr)->wincollid = collation; + break; + case T_SubscriptingRef: + ((SubscriptingRef *) expr)->refcollid = collation; + break; + case T_FuncExpr: + ((FuncExpr *) expr)->funccollid = collation; + break; + case T_NamedArgExpr: + Assert(collation == exprCollation((Node *) ((NamedArgExpr *) expr)->arg)); + break; + case T_OpExpr: + ((OpExpr *) expr)->opcollid = collation; + break; + case T_DistinctExpr: + ((DistinctExpr *) expr)->opcollid = collation; + break; + case T_NullIfExpr: + ((NullIfExpr *) expr)->opcollid = collation; + break; + case T_ScalarArrayOpExpr: + /* ScalarArrayOpExpr's result is boolean ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_BoolExpr: + /* BoolExpr's result is boolean ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_SubLink: +#ifdef USE_ASSERT_CHECKING + { + SubLink *sublink = (SubLink *) expr; + + if (sublink->subLinkType == EXPR_SUBLINK || + sublink->subLinkType == ARRAY_SUBLINK) + { + /* get the collation of subselect's first target column */ + Query *qtree = (Query *) sublink->subselect; + TargetEntry *tent; + + if (!qtree || !IsA(qtree, Query)) + elog(ERROR, "cannot set collation for untransformed sublink"); + tent = linitial_node(TargetEntry, qtree->targetList); + Assert(!tent->resjunk); + Assert(collation == exprCollation((Node *) tent->expr)); + } + else + { + /* otherwise, result is RECORD or BOOLEAN */ + Assert(!OidIsValid(collation)); + } + } +#endif /* USE_ASSERT_CHECKING */ + break; + case T_FieldSelect: + ((FieldSelect *) expr)->resultcollid = collation; + break; + case T_FieldStore: + /* FieldStore's result is composite ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_RelabelType: + ((RelabelType *) expr)->resultcollid = collation; + break; + case T_CoerceViaIO: + ((CoerceViaIO *) expr)->resultcollid = collation; + break; + case T_ArrayCoerceExpr: + ((ArrayCoerceExpr *) expr)->resultcollid = collation; + break; + case T_ConvertRowtypeExpr: + /* ConvertRowtypeExpr's result is composite ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_CaseExpr: + ((CaseExpr *) expr)->casecollid = collation; + break; + case T_ArrayExpr: + ((ArrayExpr *) expr)->array_collid = collation; + break; + case T_RowExpr: + /* RowExpr's result is composite ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_RowCompareExpr: + /* RowCompareExpr's result is boolean ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_CoalesceExpr: + ((CoalesceExpr *) expr)->coalescecollid = collation; + break; + case T_MinMaxExpr: + ((MinMaxExpr *) expr)->minmaxcollid = collation; + break; + case T_SQLValueFunction: + Assert((((SQLValueFunction *) expr)->type == NAMEOID) ? + (collation == C_COLLATION_OID) : + (collation == InvalidOid)); + break; + case T_XmlExpr: + Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ? + (collation == DEFAULT_COLLATION_OID) : + (collation == InvalidOid)); + break; + case T_NullTest: + /* NullTest's result is boolean ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_BooleanTest: + /* BooleanTest's result is boolean ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_CoerceToDomain: + ((CoerceToDomain *) expr)->resultcollid = collation; + break; + case T_CoerceToDomainValue: + ((CoerceToDomainValue *) expr)->collation = collation; + break; + case T_SetToDefault: + ((SetToDefault *) expr)->collation = collation; + break; + case T_CurrentOfExpr: + /* CurrentOfExpr's result is boolean ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + case T_NextValueExpr: + /* NextValueExpr's result is an integer type ... */ + Assert(!OidIsValid(collation)); /* ... so never set a collation */ + break; + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); + break; + } +} + +/* + * exprSetInputCollation - + * Assign input-collation information to an expression tree node. + * + * This is a no-op for node types that don't store their input collation. + * Note we omit RowCompareExpr, which needs special treatment since it + * contains multiple input collation OIDs. + */ +void +exprSetInputCollation(Node *expr, Oid inputcollation) +{ + switch (nodeTag(expr)) + { + case T_Aggref: + ((Aggref *) expr)->inputcollid = inputcollation; + break; + case T_WindowFunc: + ((WindowFunc *) expr)->inputcollid = inputcollation; + break; + case T_FuncExpr: + ((FuncExpr *) expr)->inputcollid = inputcollation; + break; + case T_OpExpr: + ((OpExpr *) expr)->inputcollid = inputcollation; + break; + case T_DistinctExpr: + ((DistinctExpr *) expr)->inputcollid = inputcollation; + break; + case T_NullIfExpr: + ((NullIfExpr *) expr)->inputcollid = inputcollation; + break; + case T_ScalarArrayOpExpr: + ((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation; + break; + case T_MinMaxExpr: + ((MinMaxExpr *) expr)->inputcollid = inputcollation; + break; + default: + break; + } +} + + +/* + * exprLocation - + * returns the parse location of an expression tree, for error reports + * + * -1 is returned if the location can't be determined. + * + * For expressions larger than a single token, the intent here is to + * return the location of the expression's leftmost token, not necessarily + * the topmost Node's location field. For example, an OpExpr's location + * field will point at the operator name, but if it is not a prefix operator + * then we should return the location of the left-hand operand instead. + * The reason is that we want to reference the entire expression not just + * that operator, and pointing to its start seems to be the most natural way. + * + * The location is not perfect --- for example, since the grammar doesn't + * explicitly represent parentheses in the parsetree, given something that + * had been written "(a + b) * c" we are going to point at "a" not "(". + * But it should be plenty good enough for error reporting purposes. + * + * You might think that this code is overly general, for instance why check + * the operands of a FuncExpr node, when the function name can be expected + * to be to the left of them? There are a couple of reasons. The grammar + * sometimes builds expressions that aren't quite what the user wrote; + * for instance x IS NOT BETWEEN ... becomes a NOT-expression whose keyword + * pointer is to the right of its leftmost argument. Also, nodes that were + * inserted implicitly by parse analysis (such as FuncExprs for implicit + * coercions) will have location -1, and so we can have odd combinations of + * known and unknown locations in a tree. + */ +int +exprLocation(const Node *expr) +{ + int loc; + + if (expr == NULL) + return -1; + switch (nodeTag(expr)) + { + case T_RangeVar: + loc = ((const RangeVar *) expr)->location; + break; + case T_TableFunc: + loc = ((const TableFunc *) expr)->location; + break; + case T_Var: + loc = ((const Var *) expr)->location; + break; + case T_Const: + loc = ((const Const *) expr)->location; + break; + case T_Param: + loc = ((const Param *) expr)->location; + break; + case T_Aggref: + /* function name should always be the first thing */ + loc = ((const Aggref *) expr)->location; + break; + case T_GroupingFunc: + loc = ((const GroupingFunc *) expr)->location; + break; + case T_WindowFunc: + /* function name should always be the first thing */ + loc = ((const WindowFunc *) expr)->location; + break; + case T_SubscriptingRef: + /* just use container argument's location */ + loc = exprLocation((Node *) ((const SubscriptingRef *) expr)->refexpr); + break; + case T_FuncExpr: + { + const FuncExpr *fexpr = (const FuncExpr *) expr; + + /* consider both function name and leftmost arg */ + loc = leftmostLoc(fexpr->location, + exprLocation((Node *) fexpr->args)); + } + break; + case T_NamedArgExpr: + { + const NamedArgExpr *na = (const NamedArgExpr *) expr; + + /* consider both argument name and value */ + loc = leftmostLoc(na->location, + exprLocation((Node *) na->arg)); + } + break; + case T_OpExpr: + case T_DistinctExpr: /* struct-equivalent to OpExpr */ + case T_NullIfExpr: /* struct-equivalent to OpExpr */ + { + const OpExpr *opexpr = (const OpExpr *) expr; + + /* consider both operator name and leftmost arg */ + loc = leftmostLoc(opexpr->location, + exprLocation((Node *) opexpr->args)); + } + break; + case T_ScalarArrayOpExpr: + { + const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr; + + /* consider both operator name and leftmost arg */ + loc = leftmostLoc(saopexpr->location, + exprLocation((Node *) saopexpr->args)); + } + break; + case T_BoolExpr: + { + const BoolExpr *bexpr = (const BoolExpr *) expr; + + /* + * Same as above, to handle either NOT or AND/OR. We can't + * special-case NOT because of the way that it's used for + * things like IS NOT BETWEEN. + */ + loc = leftmostLoc(bexpr->location, + exprLocation((Node *) bexpr->args)); + } + break; + case T_SubLink: + { + const SubLink *sublink = (const SubLink *) expr; + + /* check the testexpr, if any, and the operator/keyword */ + loc = leftmostLoc(exprLocation(sublink->testexpr), + sublink->location); + } + break; + case T_FieldSelect: + /* just use argument's location */ + loc = exprLocation((Node *) ((const FieldSelect *) expr)->arg); + break; + case T_FieldStore: + /* just use argument's location */ + loc = exprLocation((Node *) ((const FieldStore *) expr)->arg); + break; + case T_RelabelType: + { + const RelabelType *rexpr = (const RelabelType *) expr; + + /* Much as above */ + loc = leftmostLoc(rexpr->location, + exprLocation((Node *) rexpr->arg)); + } + break; + case T_CoerceViaIO: + { + const CoerceViaIO *cexpr = (const CoerceViaIO *) expr; + + /* Much as above */ + loc = leftmostLoc(cexpr->location, + exprLocation((Node *) cexpr->arg)); + } + break; + case T_ArrayCoerceExpr: + { + const ArrayCoerceExpr *cexpr = (const ArrayCoerceExpr *) expr; + + /* Much as above */ + loc = leftmostLoc(cexpr->location, + exprLocation((Node *) cexpr->arg)); + } + break; + case T_ConvertRowtypeExpr: + { + const ConvertRowtypeExpr *cexpr = (const ConvertRowtypeExpr *) expr; + + /* Much as above */ + loc = leftmostLoc(cexpr->location, + exprLocation((Node *) cexpr->arg)); + } + break; + case T_CollateExpr: + /* just use argument's location */ + loc = exprLocation((Node *) ((const CollateExpr *) expr)->arg); + break; + case T_CaseExpr: + /* CASE keyword should always be the first thing */ + loc = ((const CaseExpr *) expr)->location; + break; + case T_CaseWhen: + /* WHEN keyword should always be the first thing */ + loc = ((const CaseWhen *) expr)->location; + break; + case T_ArrayExpr: + /* the location points at ARRAY or [, which must be leftmost */ + loc = ((const ArrayExpr *) expr)->location; + break; + case T_RowExpr: + /* the location points at ROW or (, which must be leftmost */ + loc = ((const RowExpr *) expr)->location; + break; + case T_RowCompareExpr: + /* just use leftmost argument's location */ + loc = exprLocation((Node *) ((const RowCompareExpr *) expr)->largs); + break; + case T_CoalesceExpr: + /* COALESCE keyword should always be the first thing */ + loc = ((const CoalesceExpr *) expr)->location; + break; + case T_MinMaxExpr: + /* GREATEST/LEAST keyword should always be the first thing */ + loc = ((const MinMaxExpr *) expr)->location; + break; + case T_SQLValueFunction: + /* function keyword should always be the first thing */ + loc = ((const SQLValueFunction *) expr)->location; + break; + case T_XmlExpr: + { + const XmlExpr *xexpr = (const XmlExpr *) expr; + + /* consider both function name and leftmost arg */ + loc = leftmostLoc(xexpr->location, + exprLocation((Node *) xexpr->args)); + } + break; + case T_NullTest: + { + const NullTest *nexpr = (const NullTest *) expr; + + /* Much as above */ + loc = leftmostLoc(nexpr->location, + exprLocation((Node *) nexpr->arg)); + } + break; + case T_BooleanTest: + { + const BooleanTest *bexpr = (const BooleanTest *) expr; + + /* Much as above */ + loc = leftmostLoc(bexpr->location, + exprLocation((Node *) bexpr->arg)); + } + break; + case T_CoerceToDomain: + { + const CoerceToDomain *cexpr = (const CoerceToDomain *) expr; + + /* Much as above */ + loc = leftmostLoc(cexpr->location, + exprLocation((Node *) cexpr->arg)); + } + break; + case T_CoerceToDomainValue: + loc = ((const CoerceToDomainValue *) expr)->location; + break; + case T_SetToDefault: + loc = ((const SetToDefault *) expr)->location; + break; + case T_TargetEntry: + /* just use argument's location */ + loc = exprLocation((Node *) ((const TargetEntry *) expr)->expr); + break; + case T_IntoClause: + /* use the contained RangeVar's location --- close enough */ + loc = exprLocation((Node *) ((const IntoClause *) expr)->rel); + break; + case T_List: + { + /* report location of first list member that has a location */ + ListCell *lc; + + loc = -1; /* just to suppress compiler warning */ + foreach(lc, (const List *) expr) + { + loc = exprLocation((Node *) lfirst(lc)); + if (loc >= 0) + break; + } + } + break; + case T_A_Expr: + { + const A_Expr *aexpr = (const A_Expr *) expr; + + /* use leftmost of operator or left operand (if any) */ + /* we assume right operand can't be to left of operator */ + loc = leftmostLoc(aexpr->location, + exprLocation(aexpr->lexpr)); + } + break; + case T_ColumnRef: + loc = ((const ColumnRef *) expr)->location; + break; + case T_ParamRef: + loc = ((const ParamRef *) expr)->location; + break; + case T_A_Const: + loc = ((const A_Const *) expr)->location; + break; + case T_FuncCall: + { + const FuncCall *fc = (const FuncCall *) expr; + + /* consider both function name and leftmost arg */ + /* (we assume any ORDER BY nodes must be to right of name) */ + loc = leftmostLoc(fc->location, + exprLocation((Node *) fc->args)); + } + break; + case T_A_ArrayExpr: + /* the location points at ARRAY or [, which must be leftmost */ + loc = ((const A_ArrayExpr *) expr)->location; + break; + case T_ResTarget: + /* we need not examine the contained expression (if any) */ + loc = ((const ResTarget *) expr)->location; + break; + case T_MultiAssignRef: + loc = exprLocation(((const MultiAssignRef *) expr)->source); + break; + case T_TypeCast: + { + const TypeCast *tc = (const TypeCast *) expr; + + /* + * This could represent CAST(), ::, or TypeName 'literal', so + * any of the components might be leftmost. + */ + loc = exprLocation(tc->arg); + loc = leftmostLoc(loc, tc->typeName->location); + loc = leftmostLoc(loc, tc->location); + } + break; + case T_CollateClause: + /* just use argument's location */ + loc = exprLocation(((const CollateClause *) expr)->arg); + break; + case T_SortBy: + /* just use argument's location (ignore operator, if any) */ + loc = exprLocation(((const SortBy *) expr)->node); + break; + case T_WindowDef: + loc = ((const WindowDef *) expr)->location; + break; + case T_RangeTableSample: + loc = ((const RangeTableSample *) expr)->location; + break; + case T_TypeName: + loc = ((const TypeName *) expr)->location; + break; + case T_ColumnDef: + loc = ((const ColumnDef *) expr)->location; + break; + case T_Constraint: + loc = ((const Constraint *) expr)->location; + break; + case T_FunctionParameter: + /* just use typename's location */ + loc = exprLocation((Node *) ((const FunctionParameter *) expr)->argType); + break; + case T_XmlSerialize: + /* XMLSERIALIZE keyword should always be the first thing */ + loc = ((const XmlSerialize *) expr)->location; + break; + case T_GroupingSet: + loc = ((const GroupingSet *) expr)->location; + break; + case T_WithClause: + loc = ((const WithClause *) expr)->location; + break; + case T_InferClause: + loc = ((const InferClause *) expr)->location; + break; + case T_OnConflictClause: + loc = ((const OnConflictClause *) expr)->location; + break; + case T_CTESearchClause: + loc = ((const CTESearchClause *) expr)->location; + break; + case T_CTECycleClause: + loc = ((const CTECycleClause *) expr)->location; + break; + case T_CommonTableExpr: + loc = ((const CommonTableExpr *) expr)->location; + break; + case T_PlaceHolderVar: + /* just use argument's location */ + loc = exprLocation((Node *) ((const PlaceHolderVar *) expr)->phexpr); + break; + case T_InferenceElem: + /* just use nested expr's location */ + loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr); + break; + case T_PartitionElem: + loc = ((const PartitionElem *) expr)->location; + break; + case T_PartitionSpec: + loc = ((const PartitionSpec *) expr)->location; + break; + case T_PartitionBoundSpec: + loc = ((const PartitionBoundSpec *) expr)->location; + break; + case T_PartitionRangeDatum: + loc = ((const PartitionRangeDatum *) expr)->location; + break; + default: + /* for any other node type it's just unknown... */ + loc = -1; + break; + } + return loc; +} + +/* + * leftmostLoc - support for exprLocation + * + * Take the minimum of two parse location values, but ignore unknowns + */ +static int +leftmostLoc(int loc1, int loc2) +{ + if (loc1 < 0) + return loc2; + else if (loc2 < 0) + return loc1; + else + return Min(loc1, loc2); +} + + +/* + * fix_opfuncids + * Calculate opfuncid field from opno for each OpExpr node in given tree. + * The given tree can be anything expression_tree_walker handles. + * + * The argument is modified in-place. (This is OK since we'd want the + * same change for any node, even if it gets visited more than once due to + * shared structure.) + */ +void +fix_opfuncids(Node *node) +{ + /* This tree walk requires no special setup, so away we go... */ + fix_opfuncids_walker(node, NULL); +} + +static bool +fix_opfuncids_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, OpExpr)) + set_opfuncid((OpExpr *) node); + else if (IsA(node, DistinctExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, NullIfExpr)) + set_opfuncid((OpExpr *) node); /* rely on struct equivalence */ + else if (IsA(node, ScalarArrayOpExpr)) + set_sa_opfuncid((ScalarArrayOpExpr *) node); + return expression_tree_walker(node, fix_opfuncids_walker, context); +} + +/* + * set_opfuncid + * Set the opfuncid (procedure OID) in an OpExpr node, + * if it hasn't been set already. + * + * Because of struct equivalence, this can also be used for + * DistinctExpr and NullIfExpr nodes. + */ +void +set_opfuncid(OpExpr *opexpr) +{ + if (opexpr->opfuncid == InvalidOid) + opexpr->opfuncid = get_opcode(opexpr->opno); +} + +/* + * set_sa_opfuncid + * As above, for ScalarArrayOpExpr nodes. + */ +void +set_sa_opfuncid(ScalarArrayOpExpr *opexpr) +{ + if (opexpr->opfuncid == InvalidOid) + opexpr->opfuncid = get_opcode(opexpr->opno); +} + + +/* + * check_functions_in_node - + * apply checker() to each function OID contained in given expression node + * + * Returns true if the checker() function does; for nodes representing more + * than one function call, returns true if the checker() function does so + * for any of those functions. Returns false if node does not invoke any + * SQL-visible function. Caller must not pass node == NULL. + * + * This function examines only the given node; it does not recurse into any + * sub-expressions. Callers typically prefer to keep control of the recursion + * for themselves, in case additional checks should be made, or because they + * have special rules about which parts of the tree need to be visited. + * + * Note: we ignore MinMaxExpr, SQLValueFunction, XmlExpr, CoerceToDomain, + * and NextValueExpr nodes, because they do not contain SQL function OIDs. + * However, they can invoke SQL-visible functions, so callers should take + * thought about how to treat them. + */ +bool +check_functions_in_node(Node *node, check_function_callback checker, + void *context) +{ + switch (nodeTag(node)) + { + case T_Aggref: + { + Aggref *expr = (Aggref *) node; + + if (checker(expr->aggfnoid, context)) + return true; + } + break; + case T_WindowFunc: + { + WindowFunc *expr = (WindowFunc *) node; + + if (checker(expr->winfnoid, context)) + return true; + } + break; + case T_FuncExpr: + { + FuncExpr *expr = (FuncExpr *) node; + + if (checker(expr->funcid, context)) + return true; + } + break; + case T_OpExpr: + case T_DistinctExpr: /* struct-equivalent to OpExpr */ + case T_NullIfExpr: /* struct-equivalent to OpExpr */ + { + OpExpr *expr = (OpExpr *) node; + + /* Set opfuncid if it wasn't set already */ + set_opfuncid(expr); + if (checker(expr->opfuncid, context)) + return true; + } + break; + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + set_sa_opfuncid(expr); + if (checker(expr->opfuncid, context)) + return true; + } + break; + case T_CoerceViaIO: + { + CoerceViaIO *expr = (CoerceViaIO *) node; + Oid iofunc; + Oid typioparam; + bool typisvarlena; + + /* check the result type's input function */ + getTypeInputInfo(expr->resulttype, + &iofunc, &typioparam); + if (checker(iofunc, context)) + return true; + /* check the input type's output function */ + getTypeOutputInfo(exprType((Node *) expr->arg), + &iofunc, &typisvarlena); + if (checker(iofunc, context)) + return true; + } + break; + case T_RowCompareExpr: + { + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + ListCell *opid; + + foreach(opid, rcexpr->opnos) + { + Oid opfuncid = get_opcode(lfirst_oid(opid)); + + if (checker(opfuncid, context)) + return true; + } + } + break; + default: + break; + } + return false; +} + + +/* + * Standard expression-tree walking support + * + * We used to have near-duplicate code in many different routines that + * understood how to recurse through an expression node tree. That was + * a pain to maintain, and we frequently had bugs due to some particular + * routine neglecting to support a particular node type. In most cases, + * these routines only actually care about certain node types, and don't + * care about other types except insofar as they have to recurse through + * non-primitive node types. Therefore, we now provide generic tree-walking + * logic to consolidate the redundant "boilerplate" code. There are + * two versions: expression_tree_walker() and expression_tree_mutator(). + */ + +/* + * expression_tree_walker() is designed to support routines that traverse + * a tree in a read-only fashion (although it will also work for routines + * that modify nodes in-place but never add/delete/replace nodes). + * A walker routine should look like this: + * + * bool my_walker (Node *node, my_struct *context) + * { + * if (node == NULL) + * return false; + * // check for nodes that special work is required for, eg: + * if (IsA(node, Var)) + * { + * ... do special actions for Var nodes + * } + * else if (IsA(node, ...)) + * { + * ... do special actions for other node types + * } + * // for any node type not specially processed, do: + * return expression_tree_walker(node, my_walker, (void *) context); + * } + * + * The "context" argument points to a struct that holds whatever context + * information the walker routine needs --- it can be used to return data + * gathered by the walker, too. This argument is not touched by + * expression_tree_walker, but it is passed down to recursive sub-invocations + * of my_walker. The tree walk is started from a setup routine that + * fills in the appropriate context struct, calls my_walker with the top-level + * node of the tree, and then examines the results. + * + * The walker routine should return "false" to continue the tree walk, or + * "true" to abort the walk and immediately return "true" to the top-level + * caller. This can be used to short-circuit the traversal if the walker + * has found what it came for. "false" is returned to the top-level caller + * iff no invocation of the walker returned "true". + * + * The node types handled by expression_tree_walker include all those + * normally found in target lists and qualifier clauses during the planning + * stage. In particular, it handles List nodes since a cnf-ified qual clause + * will have List structure at the top level, and it handles TargetEntry nodes + * so that a scan of a target list can be handled without additional code. + * Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are + * handled, so that query jointrees and setOperation trees can be processed + * without additional code. + * + * expression_tree_walker will handle SubLink nodes by recursing normally + * into the "testexpr" subtree (which is an expression belonging to the outer + * plan). It will also call the walker on the sub-Query node; however, when + * expression_tree_walker itself is called on a Query node, it does nothing + * and returns "false". The net effect is that unless the walker does + * something special at a Query node, sub-selects will not be visited during + * an expression tree walk. This is exactly the behavior wanted in many cases + * --- and for those walkers that do want to recurse into sub-selects, special + * behavior is typically needed anyway at the entry to a sub-select (such as + * incrementing a depth counter). A walker that wants to examine sub-selects + * should include code along the lines of: + * + * if (IsA(node, Query)) + * { + * adjust context for subquery; + * result = query_tree_walker((Query *) node, my_walker, context, + * 0); // adjust flags as needed + * restore context if needed; + * return result; + * } + * + * query_tree_walker is a convenience routine (see below) that calls the + * walker on all the expression subtrees of the given Query node. + * + * expression_tree_walker will handle SubPlan nodes by recursing normally + * into the "testexpr" and the "args" list (which are expressions belonging to + * the outer plan). It will not touch the completed subplan, however. Since + * there is no link to the original Query, it is not possible to recurse into + * subselects of an already-planned expression tree. This is OK for current + * uses, but may need to be revisited in future. + */ + +bool +expression_tree_walker(Node *node, + bool (*walker) (), + void *context) +{ + ListCell *temp; + + /* + * The walker has already visited the current node, and so we need only + * recurse into any sub-nodes it has. + * + * We assume that the walker is not interested in List nodes per se, so + * when we expect a List we just recurse directly to self without + * bothering to call the walker. + */ + if (node == NULL) + return false; + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + switch (nodeTag(node)) + { + case T_Var: + case T_Const: + case T_Param: + case T_CaseTestExpr: + case T_SQLValueFunction: + case T_CoerceToDomainValue: + case T_SetToDefault: + case T_CurrentOfExpr: + case T_NextValueExpr: + case T_RangeTblRef: + case T_SortGroupClause: + case T_CTESearchClause: + /* primitive node types with no expression subnodes */ + break; + case T_WithCheckOption: + return walker(((WithCheckOption *) node)->qual, context); + case T_Aggref: + { + Aggref *expr = (Aggref *) node; + + /* recurse directly on List */ + if (expression_tree_walker((Node *) expr->aggdirectargs, + walker, context)) + return true; + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + if (expression_tree_walker((Node *) expr->aggorder, + walker, context)) + return true; + if (expression_tree_walker((Node *) expr->aggdistinct, + walker, context)) + return true; + if (walker((Node *) expr->aggfilter, context)) + return true; + } + break; + case T_GroupingFunc: + { + GroupingFunc *grouping = (GroupingFunc *) node; + + if (expression_tree_walker((Node *) grouping->args, + walker, context)) + return true; + } + break; + case T_WindowFunc: + { + WindowFunc *expr = (WindowFunc *) node; + + /* recurse directly on List */ + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + if (walker((Node *) expr->aggfilter, context)) + return true; + } + break; + case T_SubscriptingRef: + { + SubscriptingRef *sbsref = (SubscriptingRef *) node; + + /* recurse directly for upper/lower container index lists */ + if (expression_tree_walker((Node *) sbsref->refupperindexpr, + walker, context)) + return true; + if (expression_tree_walker((Node *) sbsref->reflowerindexpr, + walker, context)) + return true; + /* walker must see the refexpr and refassgnexpr, however */ + if (walker(sbsref->refexpr, context)) + return true; + + if (walker(sbsref->refassgnexpr, context)) + return true; + } + break; + case T_FuncExpr: + { + FuncExpr *expr = (FuncExpr *) node; + + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; + case T_NamedArgExpr: + return walker(((NamedArgExpr *) node)->arg, context); + case T_OpExpr: + case T_DistinctExpr: /* struct-equivalent to OpExpr */ + case T_NullIfExpr: /* struct-equivalent to OpExpr */ + { + OpExpr *expr = (OpExpr *) node; + + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; + case T_BoolExpr: + { + BoolExpr *expr = (BoolExpr *) node; + + if (expression_tree_walker((Node *) expr->args, + walker, context)) + return true; + } + break; + case T_SubLink: + { + SubLink *sublink = (SubLink *) node; + + if (walker(sublink->testexpr, context)) + return true; + + /* + * Also invoke the walker on the sublink's Query node, so it + * can recurse into the sub-query if it wants to. + */ + return walker(sublink->subselect, context); + } + break; + case T_SubPlan: + { + SubPlan *subplan = (SubPlan *) node; + + /* recurse into the testexpr, but not into the Plan */ + if (walker(subplan->testexpr, context)) + return true; + /* also examine args list */ + if (expression_tree_walker((Node *) subplan->args, + walker, context)) + return true; + } + break; + case T_AlternativeSubPlan: + return walker(((AlternativeSubPlan *) node)->subplans, context); + case T_FieldSelect: + return walker(((FieldSelect *) node)->arg, context); + case T_FieldStore: + { + FieldStore *fstore = (FieldStore *) node; + + if (walker(fstore->arg, context)) + return true; + if (walker(fstore->newvals, context)) + return true; + } + break; + case T_RelabelType: + return walker(((RelabelType *) node)->arg, context); + case T_CoerceViaIO: + return walker(((CoerceViaIO *) node)->arg, context); + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + + if (walker(acoerce->arg, context)) + return true; + if (walker(acoerce->elemexpr, context)) + return true; + } + break; + case T_ConvertRowtypeExpr: + return walker(((ConvertRowtypeExpr *) node)->arg, context); + case T_CollateExpr: + return walker(((CollateExpr *) node)->arg, context); + case T_CaseExpr: + { + CaseExpr *caseexpr = (CaseExpr *) node; + + if (walker(caseexpr->arg, context)) + return true; + /* we assume walker doesn't care about CaseWhens, either */ + foreach(temp, caseexpr->args) + { + CaseWhen *when = lfirst_node(CaseWhen, temp); + + if (walker(when->expr, context)) + return true; + if (walker(when->result, context)) + return true; + } + if (walker(caseexpr->defresult, context)) + return true; + } + break; + case T_ArrayExpr: + return walker(((ArrayExpr *) node)->elements, context); + case T_RowExpr: + /* Assume colnames isn't interesting */ + return walker(((RowExpr *) node)->args, context); + case T_RowCompareExpr: + { + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + + if (walker(rcexpr->largs, context)) + return true; + if (walker(rcexpr->rargs, context)) + return true; + } + break; + case T_CoalesceExpr: + return walker(((CoalesceExpr *) node)->args, context); + case T_MinMaxExpr: + return walker(((MinMaxExpr *) node)->args, context); + case T_XmlExpr: + { + XmlExpr *xexpr = (XmlExpr *) node; + + if (walker(xexpr->named_args, context)) + return true; + /* we assume walker doesn't care about arg_names */ + if (walker(xexpr->args, context)) + return true; + } + break; + case T_NullTest: + return walker(((NullTest *) node)->arg, context); + case T_BooleanTest: + return walker(((BooleanTest *) node)->arg, context); + case T_CoerceToDomain: + return walker(((CoerceToDomain *) node)->arg, context); + case T_TargetEntry: + return walker(((TargetEntry *) node)->expr, context); + case T_Query: + /* Do nothing with a sub-Query, per discussion above */ + break; + case T_WindowClause: + { + WindowClause *wc = (WindowClause *) node; + + if (walker(wc->partitionClause, context)) + return true; + if (walker(wc->orderClause, context)) + return true; + if (walker(wc->startOffset, context)) + return true; + if (walker(wc->endOffset, context)) + return true; + } + break; + case T_CTECycleClause: + { + CTECycleClause *cc = (CTECycleClause *) node; + + if (walker(cc->cycle_mark_value, context)) + return true; + if (walker(cc->cycle_mark_default, context)) + return true; + } + break; + case T_CommonTableExpr: + { + CommonTableExpr *cte = (CommonTableExpr *) node; + + /* + * Invoke the walker on the CTE's Query node, so it can + * recurse into the sub-query if it wants to. + */ + if (walker(cte->ctequery, context)) + return true; + + if (walker(cte->search_clause, context)) + return true; + if (walker(cte->cycle_clause, context)) + return true; + } + break; + case T_PartitionBoundSpec: + { + PartitionBoundSpec *pbs = (PartitionBoundSpec *) node; + + if (walker(pbs->listdatums, context)) + return true; + if (walker(pbs->lowerdatums, context)) + return true; + if (walker(pbs->upperdatums, context)) + return true; + } + break; + case T_PartitionRangeDatum: + { + PartitionRangeDatum *prd = (PartitionRangeDatum *) node; + + if (walker(prd->value, context)) + return true; + } + break; + case T_List: + foreach(temp, (List *) node) + { + if (walker((Node *) lfirst(temp), context)) + return true; + } + break; + case T_FromExpr: + { + FromExpr *from = (FromExpr *) node; + + if (walker(from->fromlist, context)) + return true; + if (walker(from->quals, context)) + return true; + } + break; + case T_OnConflictExpr: + { + OnConflictExpr *onconflict = (OnConflictExpr *) node; + + if (walker((Node *) onconflict->arbiterElems, context)) + return true; + if (walker(onconflict->arbiterWhere, context)) + return true; + if (walker(onconflict->onConflictSet, context)) + return true; + if (walker(onconflict->onConflictWhere, context)) + return true; + if (walker(onconflict->exclRelTlist, context)) + return true; + } + break; + case T_MergeAction: + { + MergeAction *action = (MergeAction *) node; + + if (walker(action->qual, context)) + return true; + if (walker(action->targetList, context)) + return true; + } + break; + case T_PartitionPruneStepOp: + { + PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node; + + if (walker((Node *) opstep->exprs, context)) + return true; + } + break; + case T_PartitionPruneStepCombine: + /* no expression subnodes */ + break; + case T_JoinExpr: + { + JoinExpr *join = (JoinExpr *) node; + + if (walker(join->larg, context)) + return true; + if (walker(join->rarg, context)) + return true; + if (walker(join->quals, context)) + return true; + + /* + * alias clause, using list are deemed uninteresting. + */ + } + break; + case T_SetOperationStmt: + { + SetOperationStmt *setop = (SetOperationStmt *) node; + + if (walker(setop->larg, context)) + return true; + if (walker(setop->rarg, context)) + return true; + + /* groupClauses are deemed uninteresting */ + } + break; + case T_IndexClause: + { + IndexClause *iclause = (IndexClause *) node; + + if (walker(iclause->rinfo, context)) + return true; + if (expression_tree_walker((Node *) iclause->indexquals, + walker, context)) + return true; + } + break; + case T_PlaceHolderVar: + return walker(((PlaceHolderVar *) node)->phexpr, context); + case T_InferenceElem: + return walker(((InferenceElem *) node)->expr, context); + case T_AppendRelInfo: + { + AppendRelInfo *appinfo = (AppendRelInfo *) node; + + if (expression_tree_walker((Node *) appinfo->translated_vars, + walker, context)) + return true; + } + break; + case T_PlaceHolderInfo: + return walker(((PlaceHolderInfo *) node)->ph_var, context); + case T_RangeTblFunction: + return walker(((RangeTblFunction *) node)->funcexpr, context); + case T_TableSampleClause: + { + TableSampleClause *tsc = (TableSampleClause *) node; + + if (expression_tree_walker((Node *) tsc->args, + walker, context)) + return true; + if (walker((Node *) tsc->repeatable, context)) + return true; + } + break; + case T_TableFunc: + { + TableFunc *tf = (TableFunc *) node; + + if (walker(tf->ns_uris, context)) + return true; + if (walker(tf->docexpr, context)) + return true; + if (walker(tf->rowexpr, context)) + return true; + if (walker(tf->colexprs, context)) + return true; + if (walker(tf->coldefexprs, context)) + return true; + } + break; + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return false; +} + +/* + * query_tree_walker --- initiate a walk of a Query's expressions + * + * This routine exists just to reduce the number of places that need to know + * where all the expression subtrees of a Query are. Note it can be used + * for starting a walk at top level of a Query regardless of whether the + * walker intends to descend into subqueries. It is also useful for + * descending into subqueries within a walker. + * + * Some callers want to suppress visitation of certain items in the sub-Query, + * typically because they need to process them specially, or don't actually + * want to recurse into subqueries. This is supported by the flags argument, + * which is the bitwise OR of flag values to add or suppress visitation of + * indicated items. (More flag bits may be added as needed.) + */ +bool +query_tree_walker(Query *query, + bool (*walker) (), + void *context, + int flags) +{ + Assert(query != NULL && IsA(query, Query)); + + /* + * We don't walk any utilityStmt here. However, we can't easily assert + * that it is absent, since there are at least two code paths by which + * action statements from CREATE RULE end up here, and NOTIFY is allowed + * in a rule action. + */ + + if (walker((Node *) query->targetList, context)) + return true; + if (walker((Node *) query->withCheckOptions, context)) + return true; + if (walker((Node *) query->onConflict, context)) + return true; + if (walker((Node *) query->mergeActionList, context)) + return true; + if (walker((Node *) query->returningList, context)) + return true; + if (walker((Node *) query->jointree, context)) + return true; + if (walker(query->setOperations, context)) + return true; + if (walker(query->havingQual, context)) + return true; + if (walker(query->limitOffset, context)) + return true; + if (walker(query->limitCount, context)) + return true; + + /* + * Most callers aren't interested in SortGroupClause nodes since those + * don't contain actual expressions. However they do contain OIDs which + * may be needed by dependency walkers etc. + */ + if ((flags & QTW_EXAMINE_SORTGROUP)) + { + if (walker((Node *) query->groupClause, context)) + return true; + if (walker((Node *) query->windowClause, context)) + return true; + if (walker((Node *) query->sortClause, context)) + return true; + if (walker((Node *) query->distinctClause, context)) + return true; + } + else + { + /* + * But we need to walk the expressions under WindowClause nodes even + * if we're not interested in SortGroupClause nodes. + */ + ListCell *lc; + + foreach(lc, query->windowClause) + { + WindowClause *wc = lfirst_node(WindowClause, lc); + + if (walker(wc->startOffset, context)) + return true; + if (walker(wc->endOffset, context)) + return true; + } + } + + /* + * groupingSets and rowMarks are not walked: + * + * groupingSets contain only ressortgrouprefs (integers) which are + * meaningless without the corresponding groupClause or tlist. + * Accordingly, any walker that needs to care about them needs to handle + * them itself in its Query processing. + * + * rowMarks is not walked because it contains only rangetable indexes (and + * flags etc.) and therefore should be handled at Query level similarly. + */ + + if (!(flags & QTW_IGNORE_CTE_SUBQUERIES)) + { + if (walker((Node *) query->cteList, context)) + return true; + } + if (!(flags & QTW_IGNORE_RANGE_TABLE)) + { + if (range_table_walker(query->rtable, walker, context, flags)) + return true; + } + return false; +} + +/* + * range_table_walker is just the part of query_tree_walker that scans + * a query's rangetable. This is split out since it can be useful on + * its own. + */ +bool +range_table_walker(List *rtable, + bool (*walker) (), + void *context, + int flags) +{ + ListCell *rt; + + foreach(rt, rtable) + { + RangeTblEntry *rte = lfirst_node(RangeTblEntry, rt); + + if (range_table_entry_walker(rte, walker, context, flags)) + return true; + } + return false; +} + +/* + * Some callers even want to scan the expressions in individual RTEs. + */ +bool +range_table_entry_walker(RangeTblEntry *rte, + bool (*walker) (), + void *context, + int flags) +{ + /* + * Walkers might need to examine the RTE node itself either before or + * after visiting its contents (or, conceivably, both). Note that if you + * specify neither flag, the walker won't be called on the RTE at all. + */ + if (flags & QTW_EXAMINE_RTES_BEFORE) + if (walker(rte, context)) + return true; + + switch (rte->rtekind) + { + case RTE_RELATION: + if (walker(rte->tablesample, context)) + return true; + break; + case RTE_SUBQUERY: + if (!(flags & QTW_IGNORE_RT_SUBQUERIES)) + if (walker(rte->subquery, context)) + return true; + break; + case RTE_JOIN: + if (!(flags & QTW_IGNORE_JOINALIASES)) + if (walker(rte->joinaliasvars, context)) + return true; + break; + case RTE_FUNCTION: + if (walker(rte->functions, context)) + return true; + break; + case RTE_TABLEFUNC: + if (walker(rte->tablefunc, context)) + return true; + break; + case RTE_VALUES: + if (walker(rte->values_lists, context)) + return true; + break; + case RTE_CTE: + case RTE_NAMEDTUPLESTORE: + case RTE_RESULT: + /* nothing to do */ + break; + } + + if (walker(rte->securityQuals, context)) + return true; + + if (flags & QTW_EXAMINE_RTES_AFTER) + if (walker(rte, context)) + return true; + + return false; +} + + +/* + * expression_tree_mutator() is designed to support routines that make a + * modified copy of an expression tree, with some nodes being added, + * removed, or replaced by new subtrees. The original tree is (normally) + * not changed. Each recursion level is responsible for returning a copy of + * (or appropriately modified substitute for) the subtree it is handed. + * A mutator routine should look like this: + * + * Node * my_mutator (Node *node, my_struct *context) + * { + * if (node == NULL) + * return NULL; + * // check for nodes that special work is required for, eg: + * if (IsA(node, Var)) + * { + * ... create and return modified copy of Var node + * } + * else if (IsA(node, ...)) + * { + * ... do special transformations of other node types + * } + * // for any node type not specially processed, do: + * return expression_tree_mutator(node, my_mutator, (void *) context); + * } + * + * The "context" argument points to a struct that holds whatever context + * information the mutator routine needs --- it can be used to return extra + * data gathered by the mutator, too. This argument is not touched by + * expression_tree_mutator, but it is passed down to recursive sub-invocations + * of my_mutator. The tree walk is started from a setup routine that + * fills in the appropriate context struct, calls my_mutator with the + * top-level node of the tree, and does any required post-processing. + * + * Each level of recursion must return an appropriately modified Node. + * If expression_tree_mutator() is called, it will make an exact copy + * of the given Node, but invoke my_mutator() to copy the sub-node(s) + * of that Node. In this way, my_mutator() has full control over the + * copying process but need not directly deal with expression trees + * that it has no interest in. + * + * Just as for expression_tree_walker, the node types handled by + * expression_tree_mutator include all those normally found in target lists + * and qualifier clauses during the planning stage. + * + * expression_tree_mutator will handle SubLink nodes by recursing normally + * into the "testexpr" subtree (which is an expression belonging to the outer + * plan). It will also call the mutator on the sub-Query node; however, when + * expression_tree_mutator itself is called on a Query node, it does nothing + * and returns the unmodified Query node. The net effect is that unless the + * mutator does something special at a Query node, sub-selects will not be + * visited or modified; the original sub-select will be linked to by the new + * SubLink node. Mutators that want to descend into sub-selects will usually + * do so by recognizing Query nodes and calling query_tree_mutator (below). + * + * expression_tree_mutator will handle a SubPlan node by recursing into the + * "testexpr" and the "args" list (which belong to the outer plan), but it + * will simply copy the link to the inner plan, since that's typically what + * expression tree mutators want. A mutator that wants to modify the subplan + * can force appropriate behavior by recognizing SubPlan expression nodes + * and doing the right thing. + */ + +Node * +expression_tree_mutator(Node *node, + Node *(*mutator) (), + void *context) +{ + /* + * The mutator has already decided not to modify the current node, but we + * must call the mutator for any sub-nodes. + */ + +#define FLATCOPY(newnode, node, nodetype) \ + ( (newnode) = (nodetype *) palloc(sizeof(nodetype)), \ + memcpy((newnode), (node), sizeof(nodetype)) ) + +#define MUTATE(newfield, oldfield, fieldtype) \ + ( (newfield) = (fieldtype) mutator((Node *) (oldfield), context) ) + + if (node == NULL) + return NULL; + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + switch (nodeTag(node)) + { + /* + * Primitive node types with no expression subnodes. Var and + * Const are frequent enough to deserve special cases, the others + * we just use copyObject for. + */ + case T_Var: + { + Var *var = (Var *) node; + Var *newnode; + + FLATCOPY(newnode, var, Var); + return (Node *) newnode; + } + break; + case T_Const: + { + Const *oldnode = (Const *) node; + Const *newnode; + + FLATCOPY(newnode, oldnode, Const); + /* XXX we don't bother with datumCopy; should we? */ + return (Node *) newnode; + } + break; + case T_Param: + case T_CaseTestExpr: + case T_SQLValueFunction: + case T_CoerceToDomainValue: + case T_SetToDefault: + case T_CurrentOfExpr: + case T_NextValueExpr: + case T_RangeTblRef: + case T_SortGroupClause: + case T_CTESearchClause: + return (Node *) copyObject(node); + case T_WithCheckOption: + { + WithCheckOption *wco = (WithCheckOption *) node; + WithCheckOption *newnode; + + FLATCOPY(newnode, wco, WithCheckOption); + MUTATE(newnode->qual, wco->qual, Node *); + return (Node *) newnode; + } + case T_Aggref: + { + Aggref *aggref = (Aggref *) node; + Aggref *newnode; + + FLATCOPY(newnode, aggref, Aggref); + /* assume mutation doesn't change types of arguments */ + newnode->aggargtypes = list_copy(aggref->aggargtypes); + MUTATE(newnode->aggdirectargs, aggref->aggdirectargs, List *); + MUTATE(newnode->args, aggref->args, List *); + MUTATE(newnode->aggorder, aggref->aggorder, List *); + MUTATE(newnode->aggdistinct, aggref->aggdistinct, List *); + MUTATE(newnode->aggfilter, aggref->aggfilter, Expr *); + return (Node *) newnode; + } + break; + case T_GroupingFunc: + { + GroupingFunc *grouping = (GroupingFunc *) node; + GroupingFunc *newnode; + + FLATCOPY(newnode, grouping, GroupingFunc); + MUTATE(newnode->args, grouping->args, List *); + + /* + * We assume here that mutating the arguments does not change + * the semantics, i.e. that the arguments are not mutated in a + * way that makes them semantically different from their + * previously matching expressions in the GROUP BY clause. + * + * If a mutator somehow wanted to do this, it would have to + * handle the refs and cols lists itself as appropriate. + */ + newnode->refs = list_copy(grouping->refs); + newnode->cols = list_copy(grouping->cols); + + return (Node *) newnode; + } + break; + case T_WindowFunc: + { + WindowFunc *wfunc = (WindowFunc *) node; + WindowFunc *newnode; + + FLATCOPY(newnode, wfunc, WindowFunc); + MUTATE(newnode->args, wfunc->args, List *); + MUTATE(newnode->aggfilter, wfunc->aggfilter, Expr *); + return (Node *) newnode; + } + break; + case T_SubscriptingRef: + { + SubscriptingRef *sbsref = (SubscriptingRef *) node; + SubscriptingRef *newnode; + + FLATCOPY(newnode, sbsref, SubscriptingRef); + MUTATE(newnode->refupperindexpr, sbsref->refupperindexpr, + List *); + MUTATE(newnode->reflowerindexpr, sbsref->reflowerindexpr, + List *); + MUTATE(newnode->refexpr, sbsref->refexpr, + Expr *); + MUTATE(newnode->refassgnexpr, sbsref->refassgnexpr, + Expr *); + + return (Node *) newnode; + } + break; + case T_FuncExpr: + { + FuncExpr *expr = (FuncExpr *) node; + FuncExpr *newnode; + + FLATCOPY(newnode, expr, FuncExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; + case T_NamedArgExpr: + { + NamedArgExpr *nexpr = (NamedArgExpr *) node; + NamedArgExpr *newnode; + + FLATCOPY(newnode, nexpr, NamedArgExpr); + MUTATE(newnode->arg, nexpr->arg, Expr *); + return (Node *) newnode; + } + break; + case T_OpExpr: + { + OpExpr *expr = (OpExpr *) node; + OpExpr *newnode; + + FLATCOPY(newnode, expr, OpExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; + case T_DistinctExpr: + { + DistinctExpr *expr = (DistinctExpr *) node; + DistinctExpr *newnode; + + FLATCOPY(newnode, expr, DistinctExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; + case T_NullIfExpr: + { + NullIfExpr *expr = (NullIfExpr *) node; + NullIfExpr *newnode; + + FLATCOPY(newnode, expr, NullIfExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; + case T_ScalarArrayOpExpr: + { + ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; + ScalarArrayOpExpr *newnode; + + FLATCOPY(newnode, expr, ScalarArrayOpExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; + case T_BoolExpr: + { + BoolExpr *expr = (BoolExpr *) node; + BoolExpr *newnode; + + FLATCOPY(newnode, expr, BoolExpr); + MUTATE(newnode->args, expr->args, List *); + return (Node *) newnode; + } + break; + case T_SubLink: + { + SubLink *sublink = (SubLink *) node; + SubLink *newnode; + + FLATCOPY(newnode, sublink, SubLink); + MUTATE(newnode->testexpr, sublink->testexpr, Node *); + + /* + * Also invoke the mutator on the sublink's Query node, so it + * can recurse into the sub-query if it wants to. + */ + MUTATE(newnode->subselect, sublink->subselect, Node *); + return (Node *) newnode; + } + break; + case T_SubPlan: + { + SubPlan *subplan = (SubPlan *) node; + SubPlan *newnode; + + FLATCOPY(newnode, subplan, SubPlan); + /* transform testexpr */ + MUTATE(newnode->testexpr, subplan->testexpr, Node *); + /* transform args list (params to be passed to subplan) */ + MUTATE(newnode->args, subplan->args, List *); + /* but not the sub-Plan itself, which is referenced as-is */ + return (Node *) newnode; + } + break; + case T_AlternativeSubPlan: + { + AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; + AlternativeSubPlan *newnode; + + FLATCOPY(newnode, asplan, AlternativeSubPlan); + MUTATE(newnode->subplans, asplan->subplans, List *); + return (Node *) newnode; + } + break; + case T_FieldSelect: + { + FieldSelect *fselect = (FieldSelect *) node; + FieldSelect *newnode; + + FLATCOPY(newnode, fselect, FieldSelect); + MUTATE(newnode->arg, fselect->arg, Expr *); + return (Node *) newnode; + } + break; + case T_FieldStore: + { + FieldStore *fstore = (FieldStore *) node; + FieldStore *newnode; + + FLATCOPY(newnode, fstore, FieldStore); + MUTATE(newnode->arg, fstore->arg, Expr *); + MUTATE(newnode->newvals, fstore->newvals, List *); + newnode->fieldnums = list_copy(fstore->fieldnums); + return (Node *) newnode; + } + break; + case T_RelabelType: + { + RelabelType *relabel = (RelabelType *) node; + RelabelType *newnode; + + FLATCOPY(newnode, relabel, RelabelType); + MUTATE(newnode->arg, relabel->arg, Expr *); + return (Node *) newnode; + } + break; + case T_CoerceViaIO: + { + CoerceViaIO *iocoerce = (CoerceViaIO *) node; + CoerceViaIO *newnode; + + FLATCOPY(newnode, iocoerce, CoerceViaIO); + MUTATE(newnode->arg, iocoerce->arg, Expr *); + return (Node *) newnode; + } + break; + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; + ArrayCoerceExpr *newnode; + + FLATCOPY(newnode, acoerce, ArrayCoerceExpr); + MUTATE(newnode->arg, acoerce->arg, Expr *); + MUTATE(newnode->elemexpr, acoerce->elemexpr, Expr *); + return (Node *) newnode; + } + break; + case T_ConvertRowtypeExpr: + { + ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node; + ConvertRowtypeExpr *newnode; + + FLATCOPY(newnode, convexpr, ConvertRowtypeExpr); + MUTATE(newnode->arg, convexpr->arg, Expr *); + return (Node *) newnode; + } + break; + case T_CollateExpr: + { + CollateExpr *collate = (CollateExpr *) node; + CollateExpr *newnode; + + FLATCOPY(newnode, collate, CollateExpr); + MUTATE(newnode->arg, collate->arg, Expr *); + return (Node *) newnode; + } + break; + case T_CaseExpr: + { + CaseExpr *caseexpr = (CaseExpr *) node; + CaseExpr *newnode; + + FLATCOPY(newnode, caseexpr, CaseExpr); + MUTATE(newnode->arg, caseexpr->arg, Expr *); + MUTATE(newnode->args, caseexpr->args, List *); + MUTATE(newnode->defresult, caseexpr->defresult, Expr *); + return (Node *) newnode; + } + break; + case T_CaseWhen: + { + CaseWhen *casewhen = (CaseWhen *) node; + CaseWhen *newnode; + + FLATCOPY(newnode, casewhen, CaseWhen); + MUTATE(newnode->expr, casewhen->expr, Expr *); + MUTATE(newnode->result, casewhen->result, Expr *); + return (Node *) newnode; + } + break; + case T_ArrayExpr: + { + ArrayExpr *arrayexpr = (ArrayExpr *) node; + ArrayExpr *newnode; + + FLATCOPY(newnode, arrayexpr, ArrayExpr); + MUTATE(newnode->elements, arrayexpr->elements, List *); + return (Node *) newnode; + } + break; + case T_RowExpr: + { + RowExpr *rowexpr = (RowExpr *) node; + RowExpr *newnode; + + FLATCOPY(newnode, rowexpr, RowExpr); + MUTATE(newnode->args, rowexpr->args, List *); + /* Assume colnames needn't be duplicated */ + return (Node *) newnode; + } + break; + case T_RowCompareExpr: + { + RowCompareExpr *rcexpr = (RowCompareExpr *) node; + RowCompareExpr *newnode; + + FLATCOPY(newnode, rcexpr, RowCompareExpr); + MUTATE(newnode->largs, rcexpr->largs, List *); + MUTATE(newnode->rargs, rcexpr->rargs, List *); + return (Node *) newnode; + } + break; + case T_CoalesceExpr: + { + CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; + CoalesceExpr *newnode; + + FLATCOPY(newnode, coalesceexpr, CoalesceExpr); + MUTATE(newnode->args, coalesceexpr->args, List *); + return (Node *) newnode; + } + break; + case T_MinMaxExpr: + { + MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; + MinMaxExpr *newnode; + + FLATCOPY(newnode, minmaxexpr, MinMaxExpr); + MUTATE(newnode->args, minmaxexpr->args, List *); + return (Node *) newnode; + } + break; + case T_XmlExpr: + { + XmlExpr *xexpr = (XmlExpr *) node; + XmlExpr *newnode; + + FLATCOPY(newnode, xexpr, XmlExpr); + MUTATE(newnode->named_args, xexpr->named_args, List *); + /* assume mutator does not care about arg_names */ + MUTATE(newnode->args, xexpr->args, List *); + return (Node *) newnode; + } + break; + case T_NullTest: + { + NullTest *ntest = (NullTest *) node; + NullTest *newnode; + + FLATCOPY(newnode, ntest, NullTest); + MUTATE(newnode->arg, ntest->arg, Expr *); + return (Node *) newnode; + } + break; + case T_BooleanTest: + { + BooleanTest *btest = (BooleanTest *) node; + BooleanTest *newnode; + + FLATCOPY(newnode, btest, BooleanTest); + MUTATE(newnode->arg, btest->arg, Expr *); + return (Node *) newnode; + } + break; + case T_CoerceToDomain: + { + CoerceToDomain *ctest = (CoerceToDomain *) node; + CoerceToDomain *newnode; + + FLATCOPY(newnode, ctest, CoerceToDomain); + MUTATE(newnode->arg, ctest->arg, Expr *); + return (Node *) newnode; + } + break; + case T_TargetEntry: + { + TargetEntry *targetentry = (TargetEntry *) node; + TargetEntry *newnode; + + FLATCOPY(newnode, targetentry, TargetEntry); + MUTATE(newnode->expr, targetentry->expr, Expr *); + return (Node *) newnode; + } + break; + case T_Query: + /* Do nothing with a sub-Query, per discussion above */ + return node; + case T_WindowClause: + { + WindowClause *wc = (WindowClause *) node; + WindowClause *newnode; + + FLATCOPY(newnode, wc, WindowClause); + MUTATE(newnode->partitionClause, wc->partitionClause, List *); + MUTATE(newnode->orderClause, wc->orderClause, List *); + MUTATE(newnode->startOffset, wc->startOffset, Node *); + MUTATE(newnode->endOffset, wc->endOffset, Node *); + return (Node *) newnode; + } + break; + case T_CTECycleClause: + { + CTECycleClause *cc = (CTECycleClause *) node; + CTECycleClause *newnode; + + FLATCOPY(newnode, cc, CTECycleClause); + MUTATE(newnode->cycle_mark_value, cc->cycle_mark_value, Node *); + MUTATE(newnode->cycle_mark_default, cc->cycle_mark_default, Node *); + return (Node *) newnode; + } + break; + case T_CommonTableExpr: + { + CommonTableExpr *cte = (CommonTableExpr *) node; + CommonTableExpr *newnode; + + FLATCOPY(newnode, cte, CommonTableExpr); + + /* + * Also invoke the mutator on the CTE's Query node, so it can + * recurse into the sub-query if it wants to. + */ + MUTATE(newnode->ctequery, cte->ctequery, Node *); + + MUTATE(newnode->search_clause, cte->search_clause, CTESearchClause *); + MUTATE(newnode->cycle_clause, cte->cycle_clause, CTECycleClause *); + + return (Node *) newnode; + } + break; + case T_PartitionBoundSpec: + { + PartitionBoundSpec *pbs = (PartitionBoundSpec *) node; + PartitionBoundSpec *newnode; + + FLATCOPY(newnode, pbs, PartitionBoundSpec); + MUTATE(newnode->listdatums, pbs->listdatums, List *); + MUTATE(newnode->lowerdatums, pbs->lowerdatums, List *); + MUTATE(newnode->upperdatums, pbs->upperdatums, List *); + return (Node *) newnode; + } + break; + case T_PartitionRangeDatum: + { + PartitionRangeDatum *prd = (PartitionRangeDatum *) node; + PartitionRangeDatum *newnode; + + FLATCOPY(newnode, prd, PartitionRangeDatum); + MUTATE(newnode->value, prd->value, Node *); + return (Node *) newnode; + } + break; + case T_List: + { + /* + * We assume the mutator isn't interested in the list nodes + * per se, so just invoke it on each list element. NOTE: this + * would fail badly on a list with integer elements! + */ + List *resultlist; + ListCell *temp; + + resultlist = NIL; + foreach(temp, (List *) node) + { + resultlist = lappend(resultlist, + mutator((Node *) lfirst(temp), + context)); + } + return (Node *) resultlist; + } + break; + case T_FromExpr: + { + FromExpr *from = (FromExpr *) node; + FromExpr *newnode; + + FLATCOPY(newnode, from, FromExpr); + MUTATE(newnode->fromlist, from->fromlist, List *); + MUTATE(newnode->quals, from->quals, Node *); + return (Node *) newnode; + } + break; + case T_OnConflictExpr: + { + OnConflictExpr *oc = (OnConflictExpr *) node; + OnConflictExpr *newnode; + + FLATCOPY(newnode, oc, OnConflictExpr); + MUTATE(newnode->arbiterElems, oc->arbiterElems, List *); + MUTATE(newnode->arbiterWhere, oc->arbiterWhere, Node *); + MUTATE(newnode->onConflictSet, oc->onConflictSet, List *); + MUTATE(newnode->onConflictWhere, oc->onConflictWhere, Node *); + MUTATE(newnode->exclRelTlist, oc->exclRelTlist, List *); + + return (Node *) newnode; + } + break; + case T_MergeAction: + { + MergeAction *action = (MergeAction *) node; + MergeAction *newnode; + + FLATCOPY(newnode, action, MergeAction); + MUTATE(newnode->qual, action->qual, Node *); + MUTATE(newnode->targetList, action->targetList, List *); + + return (Node *) newnode; + } + break; + case T_PartitionPruneStepOp: + { + PartitionPruneStepOp *opstep = (PartitionPruneStepOp *) node; + PartitionPruneStepOp *newnode; + + FLATCOPY(newnode, opstep, PartitionPruneStepOp); + MUTATE(newnode->exprs, opstep->exprs, List *); + + return (Node *) newnode; + } + break; + case T_PartitionPruneStepCombine: + /* no expression sub-nodes */ + return (Node *) copyObject(node); + case T_JoinExpr: + { + JoinExpr *join = (JoinExpr *) node; + JoinExpr *newnode; + + FLATCOPY(newnode, join, JoinExpr); + MUTATE(newnode->larg, join->larg, Node *); + MUTATE(newnode->rarg, join->rarg, Node *); + MUTATE(newnode->quals, join->quals, Node *); + /* We do not mutate alias or using by default */ + return (Node *) newnode; + } + break; + case T_SetOperationStmt: + { + SetOperationStmt *setop = (SetOperationStmt *) node; + SetOperationStmt *newnode; + + FLATCOPY(newnode, setop, SetOperationStmt); + MUTATE(newnode->larg, setop->larg, Node *); + MUTATE(newnode->rarg, setop->rarg, Node *); + /* We do not mutate groupClauses by default */ + return (Node *) newnode; + } + break; + case T_IndexClause: + { + IndexClause *iclause = (IndexClause *) node; + IndexClause *newnode; + + FLATCOPY(newnode, iclause, IndexClause); + MUTATE(newnode->rinfo, iclause->rinfo, RestrictInfo *); + MUTATE(newnode->indexquals, iclause->indexquals, List *); + return (Node *) newnode; + } + break; + case T_PlaceHolderVar: + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderVar *newnode; + + FLATCOPY(newnode, phv, PlaceHolderVar); + MUTATE(newnode->phexpr, phv->phexpr, Expr *); + /* Assume we need not copy the relids bitmapset */ + return (Node *) newnode; + } + break; + case T_InferenceElem: + { + InferenceElem *inferenceelemdexpr = (InferenceElem *) node; + InferenceElem *newnode; + + FLATCOPY(newnode, inferenceelemdexpr, InferenceElem); + MUTATE(newnode->expr, newnode->expr, Node *); + return (Node *) newnode; + } + break; + case T_AppendRelInfo: + { + AppendRelInfo *appinfo = (AppendRelInfo *) node; + AppendRelInfo *newnode; + + FLATCOPY(newnode, appinfo, AppendRelInfo); + MUTATE(newnode->translated_vars, appinfo->translated_vars, List *); + /* Assume nothing need be done with parent_colnos[] */ + return (Node *) newnode; + } + break; + case T_PlaceHolderInfo: + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node; + PlaceHolderInfo *newnode; + + FLATCOPY(newnode, phinfo, PlaceHolderInfo); + MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *); + /* Assume we need not copy the relids bitmapsets */ + return (Node *) newnode; + } + break; + case T_RangeTblFunction: + { + RangeTblFunction *rtfunc = (RangeTblFunction *) node; + RangeTblFunction *newnode; + + FLATCOPY(newnode, rtfunc, RangeTblFunction); + MUTATE(newnode->funcexpr, rtfunc->funcexpr, Node *); + /* Assume we need not copy the coldef info lists */ + return (Node *) newnode; + } + break; + case T_TableSampleClause: + { + TableSampleClause *tsc = (TableSampleClause *) node; + TableSampleClause *newnode; + + FLATCOPY(newnode, tsc, TableSampleClause); + MUTATE(newnode->args, tsc->args, List *); + MUTATE(newnode->repeatable, tsc->repeatable, Expr *); + return (Node *) newnode; + } + break; + case T_TableFunc: + { + TableFunc *tf = (TableFunc *) node; + TableFunc *newnode; + + FLATCOPY(newnode, tf, TableFunc); + MUTATE(newnode->ns_uris, tf->ns_uris, List *); + MUTATE(newnode->docexpr, tf->docexpr, Node *); + MUTATE(newnode->rowexpr, tf->rowexpr, Node *); + MUTATE(newnode->colexprs, tf->colexprs, List *); + MUTATE(newnode->coldefexprs, tf->coldefexprs, List *); + return (Node *) newnode; + } + break; + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + /* can't get here, but keep compiler happy */ + return NULL; +} + + +/* + * query_tree_mutator --- initiate modification of a Query's expressions + * + * This routine exists just to reduce the number of places that need to know + * where all the expression subtrees of a Query are. Note it can be used + * for starting a walk at top level of a Query regardless of whether the + * mutator intends to descend into subqueries. It is also useful for + * descending into subqueries within a mutator. + * + * Some callers want to suppress mutating of certain items in the Query, + * typically because they need to process them specially, or don't actually + * want to recurse into subqueries. This is supported by the flags argument, + * which is the bitwise OR of flag values to suppress mutating of + * indicated items. (More flag bits may be added as needed.) + * + * Normally the top-level Query node itself is copied, but some callers want + * it to be modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. + * All modified substructure is safely copied in any case. + */ +Query * +query_tree_mutator(Query *query, + Node *(*mutator) (), + void *context, + int flags) +{ + Assert(query != NULL && IsA(query, Query)); + + if (!(flags & QTW_DONT_COPY_QUERY)) + { + Query *newquery; + + FLATCOPY(newquery, query, Query); + query = newquery; + } + + MUTATE(query->targetList, query->targetList, List *); + MUTATE(query->withCheckOptions, query->withCheckOptions, List *); + MUTATE(query->onConflict, query->onConflict, OnConflictExpr *); + MUTATE(query->mergeActionList, query->mergeActionList, List *); + MUTATE(query->returningList, query->returningList, List *); + MUTATE(query->jointree, query->jointree, FromExpr *); + MUTATE(query->setOperations, query->setOperations, Node *); + MUTATE(query->havingQual, query->havingQual, Node *); + MUTATE(query->limitOffset, query->limitOffset, Node *); + MUTATE(query->limitCount, query->limitCount, Node *); + + /* + * Most callers aren't interested in SortGroupClause nodes since those + * don't contain actual expressions. However they do contain OIDs, which + * may be of interest to some mutators. + */ + + if ((flags & QTW_EXAMINE_SORTGROUP)) + { + MUTATE(query->groupClause, query->groupClause, List *); + MUTATE(query->windowClause, query->windowClause, List *); + MUTATE(query->sortClause, query->sortClause, List *); + MUTATE(query->distinctClause, query->distinctClause, List *); + } + else + { + /* + * But we need to mutate the expressions under WindowClause nodes even + * if we're not interested in SortGroupClause nodes. + */ + List *resultlist; + ListCell *temp; + + resultlist = NIL; + foreach(temp, query->windowClause) + { + WindowClause *wc = lfirst_node(WindowClause, temp); + WindowClause *newnode; + + FLATCOPY(newnode, wc, WindowClause); + MUTATE(newnode->startOffset, wc->startOffset, Node *); + MUTATE(newnode->endOffset, wc->endOffset, Node *); + + resultlist = lappend(resultlist, (Node *) newnode); + } + query->windowClause = resultlist; + } + + /* + * groupingSets and rowMarks are not mutated: + * + * groupingSets contain only ressortgroup refs (integers) which are + * meaningless without the groupClause or tlist. Accordingly, any mutator + * that needs to care about them needs to handle them itself in its Query + * processing. + * + * rowMarks contains only rangetable indexes (and flags etc.) and + * therefore should be handled at Query level similarly. + */ + + if (!(flags & QTW_IGNORE_CTE_SUBQUERIES)) + MUTATE(query->cteList, query->cteList, List *); + else /* else copy CTE list as-is */ + query->cteList = copyObject(query->cteList); + query->rtable = range_table_mutator(query->rtable, + mutator, context, flags); + return query; +} + +/* + * range_table_mutator is just the part of query_tree_mutator that processes + * a query's rangetable. This is split out since it can be useful on + * its own. + */ +List * +range_table_mutator(List *rtable, + Node *(*mutator) (), + void *context, + int flags) +{ + List *newrt = NIL; + ListCell *rt; + + foreach(rt, rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); + RangeTblEntry *newrte; + + FLATCOPY(newrte, rte, RangeTblEntry); + switch (rte->rtekind) + { + case RTE_RELATION: + MUTATE(newrte->tablesample, rte->tablesample, + TableSampleClause *); + /* we don't bother to copy eref, aliases, etc; OK? */ + break; + case RTE_SUBQUERY: + if (!(flags & QTW_IGNORE_RT_SUBQUERIES)) + MUTATE(newrte->subquery, rte->subquery, Query *); + else + { + /* else, copy RT subqueries as-is */ + newrte->subquery = copyObject(rte->subquery); + } + break; + case RTE_JOIN: + if (!(flags & QTW_IGNORE_JOINALIASES)) + MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *); + else + { + /* else, copy join aliases as-is */ + newrte->joinaliasvars = copyObject(rte->joinaliasvars); + } + break; + case RTE_FUNCTION: + MUTATE(newrte->functions, rte->functions, List *); + break; + case RTE_TABLEFUNC: + MUTATE(newrte->tablefunc, rte->tablefunc, TableFunc *); + break; + case RTE_VALUES: + MUTATE(newrte->values_lists, rte->values_lists, List *); + break; + case RTE_CTE: + case RTE_NAMEDTUPLESTORE: + case RTE_RESULT: + /* nothing to do */ + break; + } + MUTATE(newrte->securityQuals, rte->securityQuals, List *); + newrt = lappend(newrt, newrte); + } + return newrt; +} + +/* + * query_or_expression_tree_walker --- hybrid form + * + * This routine will invoke query_tree_walker if called on a Query node, + * else will invoke the walker directly. This is a useful way of starting + * the recursion when the walker's normal change of state is not appropriate + * for the outermost Query node. + */ +bool +query_or_expression_tree_walker(Node *node, + bool (*walker) (), + void *context, + int flags) +{ + if (node && IsA(node, Query)) + return query_tree_walker((Query *) node, + walker, + context, + flags); + else + return walker(node, context); +} + +/* + * query_or_expression_tree_mutator --- hybrid form + * + * This routine will invoke query_tree_mutator if called on a Query node, + * else will invoke the mutator directly. This is a useful way of starting + * the recursion when the mutator's normal change of state is not appropriate + * for the outermost Query node. + */ +Node * +query_or_expression_tree_mutator(Node *node, + Node *(*mutator) (), + void *context, + int flags) +{ + if (node && IsA(node, Query)) + return (Node *) query_tree_mutator((Query *) node, + mutator, + context, + flags); + else + return mutator(node, context); +} + + +/* + * raw_expression_tree_walker --- walk raw parse trees + * + * This has exactly the same API as expression_tree_walker, but instead of + * walking post-analysis parse trees, it knows how to walk the node types + * found in raw grammar output. (There is not currently any need for a + * combined walker, so we keep them separate in the name of efficiency.) + * Unlike expression_tree_walker, there is no special rule about query + * boundaries: we descend to everything that's possibly interesting. + * + * Currently, the node type coverage here extends only to DML statements + * (SELECT/INSERT/UPDATE/DELETE/MERGE) and nodes that can appear in them, + * because this is used mainly during analysis of CTEs, and only DML + * statements can appear in CTEs. + */ +bool +raw_expression_tree_walker(Node *node, + bool (*walker) (), + void *context) +{ + ListCell *temp; + + /* + * The walker has already visited the current node, and so we need only + * recurse into any sub-nodes it has. + */ + if (node == NULL) + return false; + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + switch (nodeTag(node)) + { + case T_SetToDefault: + case T_CurrentOfExpr: + case T_SQLValueFunction: + case T_Integer: + case T_Float: + case T_Boolean: + case T_String: + case T_BitString: + case T_ParamRef: + case T_A_Const: + case T_A_Star: + /* primitive node types with no subnodes */ + break; + case T_Alias: + /* we assume the colnames list isn't interesting */ + break; + case T_RangeVar: + return walker(((RangeVar *) node)->alias, context); + case T_GroupingFunc: + return walker(((GroupingFunc *) node)->args, context); + case T_SubLink: + { + SubLink *sublink = (SubLink *) node; + + if (walker(sublink->testexpr, context)) + return true; + /* we assume the operName is not interesting */ + if (walker(sublink->subselect, context)) + return true; + } + break; + case T_CaseExpr: + { + CaseExpr *caseexpr = (CaseExpr *) node; + + if (walker(caseexpr->arg, context)) + return true; + /* we assume walker doesn't care about CaseWhens, either */ + foreach(temp, caseexpr->args) + { + CaseWhen *when = lfirst_node(CaseWhen, temp); + + if (walker(when->expr, context)) + return true; + if (walker(when->result, context)) + return true; + } + if (walker(caseexpr->defresult, context)) + return true; + } + break; + case T_RowExpr: + /* Assume colnames isn't interesting */ + return walker(((RowExpr *) node)->args, context); + case T_CoalesceExpr: + return walker(((CoalesceExpr *) node)->args, context); + case T_MinMaxExpr: + return walker(((MinMaxExpr *) node)->args, context); + case T_XmlExpr: + { + XmlExpr *xexpr = (XmlExpr *) node; + + if (walker(xexpr->named_args, context)) + return true; + /* we assume walker doesn't care about arg_names */ + if (walker(xexpr->args, context)) + return true; + } + break; + case T_NullTest: + return walker(((NullTest *) node)->arg, context); + case T_BooleanTest: + return walker(((BooleanTest *) node)->arg, context); + case T_JoinExpr: + { + JoinExpr *join = (JoinExpr *) node; + + if (walker(join->larg, context)) + return true; + if (walker(join->rarg, context)) + return true; + if (walker(join->quals, context)) + return true; + if (walker(join->alias, context)) + return true; + /* using list is deemed uninteresting */ + } + break; + case T_IntoClause: + { + IntoClause *into = (IntoClause *) node; + + if (walker(into->rel, context)) + return true; + /* colNames, options are deemed uninteresting */ + /* viewQuery should be null in raw parsetree, but check it */ + if (walker(into->viewQuery, context)) + return true; + } + break; + case T_List: + foreach(temp, (List *) node) + { + if (walker((Node *) lfirst(temp), context)) + return true; + } + break; + case T_InsertStmt: + { + InsertStmt *stmt = (InsertStmt *) node; + + if (walker(stmt->relation, context)) + return true; + if (walker(stmt->cols, context)) + return true; + if (walker(stmt->selectStmt, context)) + return true; + if (walker(stmt->onConflictClause, context)) + return true; + if (walker(stmt->returningList, context)) + return true; + if (walker(stmt->withClause, context)) + return true; + } + break; + case T_DeleteStmt: + { + DeleteStmt *stmt = (DeleteStmt *) node; + + if (walker(stmt->relation, context)) + return true; + if (walker(stmt->usingClause, context)) + return true; + if (walker(stmt->whereClause, context)) + return true; + if (walker(stmt->returningList, context)) + return true; + if (walker(stmt->withClause, context)) + return true; + } + break; + case T_UpdateStmt: + { + UpdateStmt *stmt = (UpdateStmt *) node; + + if (walker(stmt->relation, context)) + return true; + if (walker(stmt->targetList, context)) + return true; + if (walker(stmt->whereClause, context)) + return true; + if (walker(stmt->fromClause, context)) + return true; + if (walker(stmt->returningList, context)) + return true; + if (walker(stmt->withClause, context)) + return true; + } + break; + case T_MergeStmt: + { + MergeStmt *stmt = (MergeStmt *) node; + + if (walker(stmt->relation, context)) + return true; + if (walker(stmt->sourceRelation, context)) + return true; + if (walker(stmt->joinCondition, context)) + return true; + if (walker(stmt->mergeWhenClauses, context)) + return true; + if (walker(stmt->withClause, context)) + return true; + } + break; + case T_MergeWhenClause: + { + MergeWhenClause *mergeWhenClause = (MergeWhenClause *) node; + + if (walker(mergeWhenClause->condition, context)) + return true; + if (walker(mergeWhenClause->targetList, context)) + return true; + if (walker(mergeWhenClause->values, context)) + return true; + } + break; + case T_SelectStmt: + { + SelectStmt *stmt = (SelectStmt *) node; + + if (walker(stmt->distinctClause, context)) + return true; + if (walker(stmt->intoClause, context)) + return true; + if (walker(stmt->targetList, context)) + return true; + if (walker(stmt->fromClause, context)) + return true; + if (walker(stmt->whereClause, context)) + return true; + if (walker(stmt->groupClause, context)) + return true; + if (walker(stmt->havingClause, context)) + return true; + if (walker(stmt->windowClause, context)) + return true; + if (walker(stmt->valuesLists, context)) + return true; + if (walker(stmt->sortClause, context)) + return true; + if (walker(stmt->limitOffset, context)) + return true; + if (walker(stmt->limitCount, context)) + return true; + if (walker(stmt->lockingClause, context)) + return true; + if (walker(stmt->withClause, context)) + return true; + if (walker(stmt->larg, context)) + return true; + if (walker(stmt->rarg, context)) + return true; + } + break; + case T_PLAssignStmt: + { + PLAssignStmt *stmt = (PLAssignStmt *) node; + + if (walker(stmt->indirection, context)) + return true; + if (walker(stmt->val, context)) + return true; + } + break; + case T_A_Expr: + { + A_Expr *expr = (A_Expr *) node; + + if (walker(expr->lexpr, context)) + return true; + if (walker(expr->rexpr, context)) + return true; + /* operator name is deemed uninteresting */ + } + break; + case T_BoolExpr: + { + BoolExpr *expr = (BoolExpr *) node; + + if (walker(expr->args, context)) + return true; + } + break; + case T_ColumnRef: + /* we assume the fields contain nothing interesting */ + break; + case T_FuncCall: + { + FuncCall *fcall = (FuncCall *) node; + + if (walker(fcall->args, context)) + return true; + if (walker(fcall->agg_order, context)) + return true; + if (walker(fcall->agg_filter, context)) + return true; + if (walker(fcall->over, context)) + return true; + /* function name is deemed uninteresting */ + } + break; + case T_NamedArgExpr: + return walker(((NamedArgExpr *) node)->arg, context); + case T_A_Indices: + { + A_Indices *indices = (A_Indices *) node; + + if (walker(indices->lidx, context)) + return true; + if (walker(indices->uidx, context)) + return true; + } + break; + case T_A_Indirection: + { + A_Indirection *indir = (A_Indirection *) node; + + if (walker(indir->arg, context)) + return true; + if (walker(indir->indirection, context)) + return true; + } + break; + case T_A_ArrayExpr: + return walker(((A_ArrayExpr *) node)->elements, context); + case T_ResTarget: + { + ResTarget *rt = (ResTarget *) node; + + if (walker(rt->indirection, context)) + return true; + if (walker(rt->val, context)) + return true; + } + break; + case T_MultiAssignRef: + return walker(((MultiAssignRef *) node)->source, context); + case T_TypeCast: + { + TypeCast *tc = (TypeCast *) node; + + if (walker(tc->arg, context)) + return true; + if (walker(tc->typeName, context)) + return true; + } + break; + case T_CollateClause: + return walker(((CollateClause *) node)->arg, context); + case T_SortBy: + return walker(((SortBy *) node)->node, context); + case T_WindowDef: + { + WindowDef *wd = (WindowDef *) node; + + if (walker(wd->partitionClause, context)) + return true; + if (walker(wd->orderClause, context)) + return true; + if (walker(wd->startOffset, context)) + return true; + if (walker(wd->endOffset, context)) + return true; + } + break; + case T_RangeSubselect: + { + RangeSubselect *rs = (RangeSubselect *) node; + + if (walker(rs->subquery, context)) + return true; + if (walker(rs->alias, context)) + return true; + } + break; + case T_RangeFunction: + { + RangeFunction *rf = (RangeFunction *) node; + + if (walker(rf->functions, context)) + return true; + if (walker(rf->alias, context)) + return true; + if (walker(rf->coldeflist, context)) + return true; + } + break; + case T_RangeTableSample: + { + RangeTableSample *rts = (RangeTableSample *) node; + + if (walker(rts->relation, context)) + return true; + /* method name is deemed uninteresting */ + if (walker(rts->args, context)) + return true; + if (walker(rts->repeatable, context)) + return true; + } + break; + case T_RangeTableFunc: + { + RangeTableFunc *rtf = (RangeTableFunc *) node; + + if (walker(rtf->docexpr, context)) + return true; + if (walker(rtf->rowexpr, context)) + return true; + if (walker(rtf->namespaces, context)) + return true; + if (walker(rtf->columns, context)) + return true; + if (walker(rtf->alias, context)) + return true; + } + break; + case T_RangeTableFuncCol: + { + RangeTableFuncCol *rtfc = (RangeTableFuncCol *) node; + + if (walker(rtfc->colexpr, context)) + return true; + if (walker(rtfc->coldefexpr, context)) + return true; + } + break; + case T_TypeName: + { + TypeName *tn = (TypeName *) node; + + if (walker(tn->typmods, context)) + return true; + if (walker(tn->arrayBounds, context)) + return true; + /* type name itself is deemed uninteresting */ + } + break; + case T_ColumnDef: + { + ColumnDef *coldef = (ColumnDef *) node; + + if (walker(coldef->typeName, context)) + return true; + if (walker(coldef->raw_default, context)) + return true; + if (walker(coldef->collClause, context)) + return true; + /* for now, constraints are ignored */ + } + break; + case T_IndexElem: + { + IndexElem *indelem = (IndexElem *) node; + + if (walker(indelem->expr, context)) + return true; + /* collation and opclass names are deemed uninteresting */ + } + break; + case T_GroupingSet: + return walker(((GroupingSet *) node)->content, context); + case T_LockingClause: + return walker(((LockingClause *) node)->lockedRels, context); + case T_XmlSerialize: + { + XmlSerialize *xs = (XmlSerialize *) node; + + if (walker(xs->expr, context)) + return true; + if (walker(xs->typeName, context)) + return true; + } + break; + case T_WithClause: + return walker(((WithClause *) node)->ctes, context); + case T_InferClause: + { + InferClause *stmt = (InferClause *) node; + + if (walker(stmt->indexElems, context)) + return true; + if (walker(stmt->whereClause, context)) + return true; + } + break; + case T_OnConflictClause: + { + OnConflictClause *stmt = (OnConflictClause *) node; + + if (walker(stmt->infer, context)) + return true; + if (walker(stmt->targetList, context)) + return true; + if (walker(stmt->whereClause, context)) + return true; + } + break; + case T_CommonTableExpr: + /* search_clause and cycle_clause are not interesting here */ + return walker(((CommonTableExpr *) node)->ctequery, context); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return false; +} + +/* + * planstate_tree_walker --- walk plan state trees + * + * The walker has already visited the current node, and so we need only + * recurse into any sub-nodes it has. + */ +bool +planstate_tree_walker(PlanState *planstate, + bool (*walker) (), + void *context) +{ + Plan *plan = planstate->plan; + ListCell *lc; + + /* Guard against stack overflow due to overly complex plan trees */ + check_stack_depth(); + + /* initPlan-s */ + if (planstate_walk_subplans(planstate->initPlan, walker, context)) + return true; + + /* lefttree */ + if (outerPlanState(planstate)) + { + if (walker(outerPlanState(planstate), context)) + return true; + } + + /* righttree */ + if (innerPlanState(planstate)) + { + if (walker(innerPlanState(planstate), context)) + return true; + } + + /* special child plans */ + switch (nodeTag(plan)) + { + case T_Append: + if (planstate_walk_members(((AppendState *) planstate)->appendplans, + ((AppendState *) planstate)->as_nplans, + walker, context)) + return true; + break; + case T_MergeAppend: + if (planstate_walk_members(((MergeAppendState *) planstate)->mergeplans, + ((MergeAppendState *) planstate)->ms_nplans, + walker, context)) + return true; + break; + case T_BitmapAnd: + if (planstate_walk_members(((BitmapAndState *) planstate)->bitmapplans, + ((BitmapAndState *) planstate)->nplans, + walker, context)) + return true; + break; + case T_BitmapOr: + if (planstate_walk_members(((BitmapOrState *) planstate)->bitmapplans, + ((BitmapOrState *) planstate)->nplans, + walker, context)) + return true; + break; + case T_SubqueryScan: + if (walker(((SubqueryScanState *) planstate)->subplan, context)) + return true; + break; + case T_CustomScan: + foreach(lc, ((CustomScanState *) planstate)->custom_ps) + { + if (walker((PlanState *) lfirst(lc), context)) + return true; + } + break; + default: + break; + } + + /* subPlan-s */ + if (planstate_walk_subplans(planstate->subPlan, walker, context)) + return true; + + return false; +} + +/* + * Walk a list of SubPlans (or initPlans, which also use SubPlan nodes). + */ +static bool +planstate_walk_subplans(List *plans, + bool (*walker) (), + void *context) +{ + ListCell *lc; + + foreach(lc, plans) + { + SubPlanState *sps = lfirst_node(SubPlanState, lc); + + if (walker(sps->planstate, context)) + return true; + } + + return false; +} + +/* + * Walk the constituent plans of a ModifyTable, Append, MergeAppend, + * BitmapAnd, or BitmapOr node. + */ +static bool +planstate_walk_members(PlanState **planstates, int nplans, + bool (*walker) (), void *context) +{ + int j; + + for (j = 0; j < nplans; j++) + { + if (walker(planstates[j], context)) + return true; + } + + return false; +} diff --git a/src/backend/nodes/nodes.c b/src/backend/nodes/nodes.c new file mode 100644 index 0000000..c2d3b6a --- /dev/null +++ b/src/backend/nodes/nodes.c @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * nodes.c + * support code for nodes (now that we have removed the home-brew + * inheritance system, our support code for nodes is much simpler) + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/nodes.c + * + * HISTORY + * Andrew Yu Oct 20, 1994 file creation + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/nodes.h" + +/* + * Support for newNode() macro + * + * In a GCC build there is no need for the global variable newNodeMacroHolder. + * However, we create it anyway, to support the case of a non-GCC-built + * loadable module being loaded into a GCC-built backend. + */ + +Node *newNodeMacroHolder; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c new file mode 100644 index 0000000..3f8e586 --- /dev/null +++ b/src/backend/nodes/outfuncs.c @@ -0,0 +1,4634 @@ +/*------------------------------------------------------------------------- + * + * outfuncs.c + * Output functions for Postgres tree nodes. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/outfuncs.c + * + * NOTES + * Every node type that can appear in stored rules' parsetrees *must* + * have an output function defined here (as well as an input function + * in readfuncs.c). In addition, plan nodes should have input and + * output functions so that they can be sent to parallel workers. + * + * For use in debugging, we also provide output functions for nodes + * that appear in raw parsetrees and planner Paths. These node types + * need not have input functions. Output support for raw parsetrees + * is somewhat incomplete, too; in particular, utility statements are + * almost entirely unsupported. We try to support everything that can + * appear in a raw SELECT, though. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <ctype.h> + +#include "lib/stringinfo.h" +#include "miscadmin.h" +#include "nodes/extensible.h" +#include "nodes/pathnodes.h" +#include "nodes/plannodes.h" +#include "utils/datum.h" +#include "utils/rel.h" + +static void outChar(StringInfo str, char c); + + +/* + * Macros to simplify output of different kinds of fields. Use these + * wherever possible to reduce the chance for silly typos. Note that these + * hard-wire conventions about the names of the local variables in an Out + * routine. + */ + +/* Write the label for the node type */ +#define WRITE_NODE_TYPE(nodelabel) \ + appendStringInfoString(str, nodelabel) + +/* Write an integer field (anything written as ":fldname %d") */ +#define WRITE_INT_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname) + +/* Write an unsigned integer field (anything written as ":fldname %u") */ +#define WRITE_UINT_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname) + +/* Write an unsigned integer field (anything written with UINT64_FORMAT) */ +#define WRITE_UINT64_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " " UINT64_FORMAT, \ + node->fldname) + +/* Write an OID field (don't hard-wire assumption that OID is same as uint) */ +#define WRITE_OID_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " %u", node->fldname) + +/* Write a long-integer field */ +#define WRITE_LONG_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " %ld", node->fldname) + +/* Write a char field (ie, one ascii character) */ +#define WRITE_CHAR_FIELD(fldname) \ + (appendStringInfo(str, " :" CppAsString(fldname) " "), \ + outChar(str, node->fldname)) + +/* Write an enumerated-type field as an integer code */ +#define WRITE_ENUM_FIELD(fldname, enumtype) \ + appendStringInfo(str, " :" CppAsString(fldname) " %d", \ + (int) node->fldname) + +/* Write a float field --- caller must give format to define precision */ +#define WRITE_FLOAT_FIELD(fldname,format) \ + appendStringInfo(str, " :" CppAsString(fldname) " " format, node->fldname) + +/* Write a boolean field */ +#define WRITE_BOOL_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " %s", \ + booltostr(node->fldname)) + +/* Write a character-string (possibly NULL) field */ +#define WRITE_STRING_FIELD(fldname) \ + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + outToken(str, node->fldname)) + +/* Write a parse location field (actually same as INT case) */ +#define WRITE_LOCATION_FIELD(fldname) \ + appendStringInfo(str, " :" CppAsString(fldname) " %d", node->fldname) + +/* Write a Node field */ +#define WRITE_NODE_FIELD(fldname) \ + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + outNode(str, node->fldname)) + +/* Write a bitmapset field */ +#define WRITE_BITMAPSET_FIELD(fldname) \ + (appendStringInfoString(str, " :" CppAsString(fldname) " "), \ + outBitmapset(str, node->fldname)) + +#define WRITE_ATTRNUMBER_ARRAY(fldname, len) \ + do { \ + appendStringInfoString(str, " :" CppAsString(fldname) " "); \ + for (int i = 0; i < len; i++) \ + appendStringInfo(str, " %d", node->fldname[i]); \ + } while(0) + +#define WRITE_OID_ARRAY(fldname, len) \ + do { \ + appendStringInfoString(str, " :" CppAsString(fldname) " "); \ + for (int i = 0; i < len; i++) \ + appendStringInfo(str, " %u", node->fldname[i]); \ + } while(0) + +/* + * This macro supports the case that the field is NULL. For the other array + * macros, that is currently not needed. + */ +#define WRITE_INDEX_ARRAY(fldname, len) \ + do { \ + appendStringInfoString(str, " :" CppAsString(fldname) " "); \ + if (node->fldname) \ + for (int i = 0; i < len; i++) \ + appendStringInfo(str, " %u", node->fldname[i]); \ + else \ + appendStringInfoString(str, "<>"); \ + } while(0) + +#define WRITE_INT_ARRAY(fldname, len) \ + do { \ + appendStringInfoString(str, " :" CppAsString(fldname) " "); \ + for (int i = 0; i < len; i++) \ + appendStringInfo(str, " %d", node->fldname[i]); \ + } while(0) + +#define WRITE_BOOL_ARRAY(fldname, len) \ + do { \ + appendStringInfoString(str, " :" CppAsString(fldname) " "); \ + for (int i = 0; i < len; i++) \ + appendStringInfo(str, " %s", booltostr(node->fldname[i])); \ + } while(0) + + +#define booltostr(x) ((x) ? "true" : "false") + + +/* + * outToken + * Convert an ordinary string (eg, an identifier) into a form that + * will be decoded back to a plain token by read.c's functions. + * + * If a null or empty string is given, it is encoded as "<>". + */ +void +outToken(StringInfo str, const char *s) +{ + if (s == NULL || *s == '\0') + { + appendStringInfoString(str, "<>"); + return; + } + + /* + * Look for characters or patterns that are treated specially by read.c + * (either in pg_strtok() or in nodeRead()), and therefore need a + * protective backslash. + */ + /* These characters only need to be quoted at the start of the string */ + if (*s == '<' || + *s == '"' || + isdigit((unsigned char) *s) || + ((*s == '+' || *s == '-') && + (isdigit((unsigned char) s[1]) || s[1] == '.'))) + appendStringInfoChar(str, '\\'); + while (*s) + { + /* These chars must be backslashed anywhere in the string */ + if (*s == ' ' || *s == '\n' || *s == '\t' || + *s == '(' || *s == ')' || *s == '{' || *s == '}' || + *s == '\\') + appendStringInfoChar(str, '\\'); + appendStringInfoChar(str, *s++); + } +} + +/* + * Convert one char. Goes through outToken() so that special characters are + * escaped. + */ +static void +outChar(StringInfo str, char c) +{ + char in[2]; + + in[0] = c; + in[1] = '\0'; + + outToken(str, in); +} + +static void +_outList(StringInfo str, const List *node) +{ + const ListCell *lc; + + appendStringInfoChar(str, '('); + + if (IsA(node, IntList)) + appendStringInfoChar(str, 'i'); + else if (IsA(node, OidList)) + appendStringInfoChar(str, 'o'); + + foreach(lc, node) + { + /* + * For the sake of backward compatibility, we emit a slightly + * different whitespace format for lists of nodes vs. other types of + * lists. XXX: is this necessary? + */ + if (IsA(node, List)) + { + outNode(str, lfirst(lc)); + if (lnext(node, lc)) + appendStringInfoChar(str, ' '); + } + else if (IsA(node, IntList)) + appendStringInfo(str, " %d", lfirst_int(lc)); + else if (IsA(node, OidList)) + appendStringInfo(str, " %u", lfirst_oid(lc)); + else + elog(ERROR, "unrecognized list node type: %d", + (int) node->type); + } + + appendStringInfoChar(str, ')'); +} + +/* + * outBitmapset - + * converts a bitmap set of integers + * + * Note: the output format is "(b int int ...)", similar to an integer List. + */ +void +outBitmapset(StringInfo str, const Bitmapset *bms) +{ + int x; + + appendStringInfoChar(str, '('); + appendStringInfoChar(str, 'b'); + x = -1; + while ((x = bms_next_member(bms, x)) >= 0) + appendStringInfo(str, " %d", x); + appendStringInfoChar(str, ')'); +} + +/* + * Print the value of a Datum given its type. + */ +void +outDatum(StringInfo str, Datum value, int typlen, bool typbyval) +{ + Size length, + i; + char *s; + + length = datumGetSize(value, typbyval, typlen); + + if (typbyval) + { + s = (char *) (&value); + appendStringInfo(str, "%u [ ", (unsigned int) length); + for (i = 0; i < (Size) sizeof(Datum); i++) + appendStringInfo(str, "%d ", (int) (s[i])); + appendStringInfoChar(str, ']'); + } + else + { + s = (char *) DatumGetPointer(value); + if (!PointerIsValid(s)) + appendStringInfoString(str, "0 [ ]"); + else + { + appendStringInfo(str, "%u [ ", (unsigned int) length); + for (i = 0; i < length; i++) + appendStringInfo(str, "%d ", (int) (s[i])); + appendStringInfoChar(str, ']'); + } + } +} + + +/* + * Stuff from plannodes.h + */ + +static void +_outPlannedStmt(StringInfo str, const PlannedStmt *node) +{ + WRITE_NODE_TYPE("PLANNEDSTMT"); + + WRITE_ENUM_FIELD(commandType, CmdType); + WRITE_UINT64_FIELD(queryId); + WRITE_BOOL_FIELD(hasReturning); + WRITE_BOOL_FIELD(hasModifyingCTE); + WRITE_BOOL_FIELD(canSetTag); + WRITE_BOOL_FIELD(transientPlan); + WRITE_BOOL_FIELD(dependsOnRole); + WRITE_BOOL_FIELD(parallelModeNeeded); + WRITE_INT_FIELD(jitFlags); + WRITE_NODE_FIELD(planTree); + WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(resultRelations); + WRITE_NODE_FIELD(appendRelations); + WRITE_NODE_FIELD(subplans); + WRITE_BITMAPSET_FIELD(rewindPlanIDs); + WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(relationOids); + WRITE_NODE_FIELD(invalItems); + WRITE_NODE_FIELD(paramExecTypes); + WRITE_NODE_FIELD(utilityStmt); + WRITE_LOCATION_FIELD(stmt_location); + WRITE_INT_FIELD(stmt_len); +} + +/* + * print the basic stuff of all nodes that inherit from Plan + */ +static void +_outPlanInfo(StringInfo str, const Plan *node) +{ + WRITE_FLOAT_FIELD(startup_cost, "%.2f"); + WRITE_FLOAT_FIELD(total_cost, "%.2f"); + WRITE_FLOAT_FIELD(plan_rows, "%.0f"); + WRITE_INT_FIELD(plan_width); + WRITE_BOOL_FIELD(parallel_aware); + WRITE_BOOL_FIELD(parallel_safe); + WRITE_BOOL_FIELD(async_capable); + WRITE_INT_FIELD(plan_node_id); + WRITE_NODE_FIELD(targetlist); + WRITE_NODE_FIELD(qual); + WRITE_NODE_FIELD(lefttree); + WRITE_NODE_FIELD(righttree); + WRITE_NODE_FIELD(initPlan); + WRITE_BITMAPSET_FIELD(extParam); + WRITE_BITMAPSET_FIELD(allParam); +} + +/* + * print the basic stuff of all nodes that inherit from Scan + */ +static void +_outScanInfo(StringInfo str, const Scan *node) +{ + _outPlanInfo(str, (const Plan *) node); + + WRITE_UINT_FIELD(scanrelid); +} + +/* + * print the basic stuff of all nodes that inherit from Join + */ +static void +_outJoinPlanInfo(StringInfo str, const Join *node) +{ + _outPlanInfo(str, (const Plan *) node); + + WRITE_ENUM_FIELD(jointype, JoinType); + WRITE_BOOL_FIELD(inner_unique); + WRITE_NODE_FIELD(joinqual); +} + + +static void +_outPlan(StringInfo str, const Plan *node) +{ + WRITE_NODE_TYPE("PLAN"); + + _outPlanInfo(str, (const Plan *) node); +} + +static void +_outResult(StringInfo str, const Result *node) +{ + WRITE_NODE_TYPE("RESULT"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_NODE_FIELD(resconstantqual); +} + +static void +_outProjectSet(StringInfo str, const ProjectSet *node) +{ + WRITE_NODE_TYPE("PROJECTSET"); + + _outPlanInfo(str, (const Plan *) node); +} + +static void +_outModifyTable(StringInfo str, const ModifyTable *node) +{ + WRITE_NODE_TYPE("MODIFYTABLE"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_ENUM_FIELD(operation, CmdType); + WRITE_BOOL_FIELD(canSetTag); + WRITE_UINT_FIELD(nominalRelation); + WRITE_UINT_FIELD(rootRelation); + WRITE_BOOL_FIELD(partColsUpdated); + WRITE_NODE_FIELD(resultRelations); + WRITE_NODE_FIELD(updateColnosLists); + WRITE_NODE_FIELD(withCheckOptionLists); + WRITE_NODE_FIELD(returningLists); + WRITE_NODE_FIELD(fdwPrivLists); + WRITE_BITMAPSET_FIELD(fdwDirectModifyPlans); + WRITE_NODE_FIELD(rowMarks); + WRITE_INT_FIELD(epqParam); + WRITE_ENUM_FIELD(onConflictAction, OnConflictAction); + WRITE_NODE_FIELD(arbiterIndexes); + WRITE_NODE_FIELD(onConflictSet); + WRITE_NODE_FIELD(onConflictCols); + WRITE_NODE_FIELD(onConflictWhere); + WRITE_UINT_FIELD(exclRelRTI); + WRITE_NODE_FIELD(exclRelTlist); + WRITE_NODE_FIELD(mergeActionLists); +} + +static void +_outAppend(StringInfo str, const Append *node) +{ + WRITE_NODE_TYPE("APPEND"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_BITMAPSET_FIELD(apprelids); + WRITE_NODE_FIELD(appendplans); + WRITE_INT_FIELD(nasyncplans); + WRITE_INT_FIELD(first_partial_plan); + WRITE_NODE_FIELD(part_prune_info); +} + +static void +_outMergeAppend(StringInfo str, const MergeAppend *node) +{ + WRITE_NODE_TYPE("MERGEAPPEND"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_BITMAPSET_FIELD(apprelids); + WRITE_NODE_FIELD(mergeplans); + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(sortColIdx, node->numCols); + WRITE_OID_ARRAY(sortOperators, node->numCols); + WRITE_OID_ARRAY(collations, node->numCols); + WRITE_BOOL_ARRAY(nullsFirst, node->numCols); + WRITE_NODE_FIELD(part_prune_info); +} + +static void +_outRecursiveUnion(StringInfo str, const RecursiveUnion *node) +{ + WRITE_NODE_TYPE("RECURSIVEUNION"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(wtParam); + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(dupColIdx, node->numCols); + WRITE_OID_ARRAY(dupOperators, node->numCols); + WRITE_OID_ARRAY(dupCollations, node->numCols); + WRITE_LONG_FIELD(numGroups); +} + +static void +_outBitmapAnd(StringInfo str, const BitmapAnd *node) +{ + WRITE_NODE_TYPE("BITMAPAND"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_NODE_FIELD(bitmapplans); +} + +static void +_outBitmapOr(StringInfo str, const BitmapOr *node) +{ + WRITE_NODE_TYPE("BITMAPOR"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_BOOL_FIELD(isshared); + WRITE_NODE_FIELD(bitmapplans); +} + +static void +_outGather(StringInfo str, const Gather *node) +{ + WRITE_NODE_TYPE("GATHER"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(num_workers); + WRITE_INT_FIELD(rescan_param); + WRITE_BOOL_FIELD(single_copy); + WRITE_BOOL_FIELD(invisible); + WRITE_BITMAPSET_FIELD(initParam); +} + +static void +_outGatherMerge(StringInfo str, const GatherMerge *node) +{ + WRITE_NODE_TYPE("GATHERMERGE"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(num_workers); + WRITE_INT_FIELD(rescan_param); + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(sortColIdx, node->numCols); + WRITE_OID_ARRAY(sortOperators, node->numCols); + WRITE_OID_ARRAY(collations, node->numCols); + WRITE_BOOL_ARRAY(nullsFirst, node->numCols); + WRITE_BITMAPSET_FIELD(initParam); +} + +static void +_outScan(StringInfo str, const Scan *node) +{ + WRITE_NODE_TYPE("SCAN"); + + _outScanInfo(str, node); +} + +static void +_outSeqScan(StringInfo str, const SeqScan *node) +{ + WRITE_NODE_TYPE("SEQSCAN"); + + _outScanInfo(str, (const Scan *) node); +} + +static void +_outSampleScan(StringInfo str, const SampleScan *node) +{ + WRITE_NODE_TYPE("SAMPLESCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(tablesample); +} + +static void +_outIndexScan(StringInfo str, const IndexScan *node) +{ + WRITE_NODE_TYPE("INDEXSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_OID_FIELD(indexid); + WRITE_NODE_FIELD(indexqual); + WRITE_NODE_FIELD(indexqualorig); + WRITE_NODE_FIELD(indexorderby); + WRITE_NODE_FIELD(indexorderbyorig); + WRITE_NODE_FIELD(indexorderbyops); + WRITE_ENUM_FIELD(indexorderdir, ScanDirection); +} + +static void +_outIndexOnlyScan(StringInfo str, const IndexOnlyScan *node) +{ + WRITE_NODE_TYPE("INDEXONLYSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_OID_FIELD(indexid); + WRITE_NODE_FIELD(indexqual); + WRITE_NODE_FIELD(recheckqual); + WRITE_NODE_FIELD(indexorderby); + WRITE_NODE_FIELD(indextlist); + WRITE_ENUM_FIELD(indexorderdir, ScanDirection); +} + +static void +_outBitmapIndexScan(StringInfo str, const BitmapIndexScan *node) +{ + WRITE_NODE_TYPE("BITMAPINDEXSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_OID_FIELD(indexid); + WRITE_BOOL_FIELD(isshared); + WRITE_NODE_FIELD(indexqual); + WRITE_NODE_FIELD(indexqualorig); +} + +static void +_outBitmapHeapScan(StringInfo str, const BitmapHeapScan *node) +{ + WRITE_NODE_TYPE("BITMAPHEAPSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(bitmapqualorig); +} + +static void +_outTidScan(StringInfo str, const TidScan *node) +{ + WRITE_NODE_TYPE("TIDSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(tidquals); +} + +static void +_outTidRangeScan(StringInfo str, const TidRangeScan *node) +{ + WRITE_NODE_TYPE("TIDRANGESCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(tidrangequals); +} + +static void +_outSubqueryScan(StringInfo str, const SubqueryScan *node) +{ + WRITE_NODE_TYPE("SUBQUERYSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(subplan); + WRITE_ENUM_FIELD(scanstatus, SubqueryScanStatus); +} + +static void +_outFunctionScan(StringInfo str, const FunctionScan *node) +{ + WRITE_NODE_TYPE("FUNCTIONSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(functions); + WRITE_BOOL_FIELD(funcordinality); +} + +static void +_outTableFuncScan(StringInfo str, const TableFuncScan *node) +{ + WRITE_NODE_TYPE("TABLEFUNCSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(tablefunc); +} + +static void +_outValuesScan(StringInfo str, const ValuesScan *node) +{ + WRITE_NODE_TYPE("VALUESSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_NODE_FIELD(values_lists); +} + +static void +_outCteScan(StringInfo str, const CteScan *node) +{ + WRITE_NODE_TYPE("CTESCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_INT_FIELD(ctePlanId); + WRITE_INT_FIELD(cteParam); +} + +static void +_outNamedTuplestoreScan(StringInfo str, const NamedTuplestoreScan *node) +{ + WRITE_NODE_TYPE("NAMEDTUPLESTORESCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_STRING_FIELD(enrname); +} + +static void +_outWorkTableScan(StringInfo str, const WorkTableScan *node) +{ + WRITE_NODE_TYPE("WORKTABLESCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_INT_FIELD(wtParam); +} + +static void +_outForeignScan(StringInfo str, const ForeignScan *node) +{ + WRITE_NODE_TYPE("FOREIGNSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_ENUM_FIELD(operation, CmdType); + WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(fs_server); + WRITE_NODE_FIELD(fdw_exprs); + WRITE_NODE_FIELD(fdw_private); + WRITE_NODE_FIELD(fdw_scan_tlist); + WRITE_NODE_FIELD(fdw_recheck_quals); + WRITE_BITMAPSET_FIELD(fs_relids); + WRITE_BOOL_FIELD(fsSystemCol); +} + +static void +_outCustomScan(StringInfo str, const CustomScan *node) +{ + WRITE_NODE_TYPE("CUSTOMSCAN"); + + _outScanInfo(str, (const Scan *) node); + + WRITE_UINT_FIELD(flags); + WRITE_NODE_FIELD(custom_plans); + WRITE_NODE_FIELD(custom_exprs); + WRITE_NODE_FIELD(custom_private); + WRITE_NODE_FIELD(custom_scan_tlist); + WRITE_BITMAPSET_FIELD(custom_relids); + /* CustomName is a key to lookup CustomScanMethods */ + appendStringInfoString(str, " :methods "); + outToken(str, node->methods->CustomName); +} + +static void +_outJoin(StringInfo str, const Join *node) +{ + WRITE_NODE_TYPE("JOIN"); + + _outJoinPlanInfo(str, (const Join *) node); +} + +static void +_outNestLoop(StringInfo str, const NestLoop *node) +{ + WRITE_NODE_TYPE("NESTLOOP"); + + _outJoinPlanInfo(str, (const Join *) node); + + WRITE_NODE_FIELD(nestParams); +} + +static void +_outMergeJoin(StringInfo str, const MergeJoin *node) +{ + int numCols; + + WRITE_NODE_TYPE("MERGEJOIN"); + + _outJoinPlanInfo(str, (const Join *) node); + + WRITE_BOOL_FIELD(skip_mark_restore); + WRITE_NODE_FIELD(mergeclauses); + + numCols = list_length(node->mergeclauses); + + WRITE_OID_ARRAY(mergeFamilies, numCols); + WRITE_OID_ARRAY(mergeCollations, numCols); + WRITE_INT_ARRAY(mergeStrategies, numCols); + WRITE_BOOL_ARRAY(mergeNullsFirst, numCols); +} + +static void +_outHashJoin(StringInfo str, const HashJoin *node) +{ + WRITE_NODE_TYPE("HASHJOIN"); + + _outJoinPlanInfo(str, (const Join *) node); + + WRITE_NODE_FIELD(hashclauses); + WRITE_NODE_FIELD(hashoperators); + WRITE_NODE_FIELD(hashcollations); + WRITE_NODE_FIELD(hashkeys); +} + +static void +_outAgg(StringInfo str, const Agg *node) +{ + WRITE_NODE_TYPE("AGG"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_ENUM_FIELD(aggstrategy, AggStrategy); + WRITE_ENUM_FIELD(aggsplit, AggSplit); + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(grpColIdx, node->numCols); + WRITE_OID_ARRAY(grpOperators, node->numCols); + WRITE_OID_ARRAY(grpCollations, node->numCols); + WRITE_LONG_FIELD(numGroups); + WRITE_UINT64_FIELD(transitionSpace); + WRITE_BITMAPSET_FIELD(aggParams); + WRITE_NODE_FIELD(groupingSets); + WRITE_NODE_FIELD(chain); +} + +static void +_outWindowAgg(StringInfo str, const WindowAgg *node) +{ + WRITE_NODE_TYPE("WINDOWAGG"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_UINT_FIELD(winref); + WRITE_INT_FIELD(partNumCols); + WRITE_ATTRNUMBER_ARRAY(partColIdx, node->partNumCols); + WRITE_OID_ARRAY(partOperators, node->partNumCols); + WRITE_OID_ARRAY(partCollations, node->partNumCols); + WRITE_INT_FIELD(ordNumCols); + WRITE_ATTRNUMBER_ARRAY(ordColIdx, node->ordNumCols); + WRITE_OID_ARRAY(ordOperators, node->ordNumCols); + WRITE_OID_ARRAY(ordCollations, node->ordNumCols); + WRITE_INT_FIELD(frameOptions); + WRITE_NODE_FIELD(startOffset); + WRITE_NODE_FIELD(endOffset); + WRITE_NODE_FIELD(runCondition); + WRITE_NODE_FIELD(runConditionOrig); + WRITE_OID_FIELD(startInRangeFunc); + WRITE_OID_FIELD(endInRangeFunc); + WRITE_OID_FIELD(inRangeColl); + WRITE_BOOL_FIELD(inRangeAsc); + WRITE_BOOL_FIELD(inRangeNullsFirst); + WRITE_BOOL_FIELD(topWindow); +} + +static void +_outGroup(StringInfo str, const Group *node) +{ + WRITE_NODE_TYPE("GROUP"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(grpColIdx, node->numCols); + WRITE_OID_ARRAY(grpOperators, node->numCols); + WRITE_OID_ARRAY(grpCollations, node->numCols); +} + +static void +_outMaterial(StringInfo str, const Material *node) +{ + WRITE_NODE_TYPE("MATERIAL"); + + _outPlanInfo(str, (const Plan *) node); +} + +static void +_outMemoize(StringInfo str, const Memoize *node) +{ + WRITE_NODE_TYPE("MEMOIZE"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(numKeys); + WRITE_OID_ARRAY(hashOperators, node->numKeys); + WRITE_OID_ARRAY(collations, node->numKeys); + WRITE_NODE_FIELD(param_exprs); + WRITE_BOOL_FIELD(singlerow); + WRITE_BOOL_FIELD(binary_mode); + WRITE_UINT_FIELD(est_entries); + WRITE_BITMAPSET_FIELD(keyparamids); +} + +static void +_outSortInfo(StringInfo str, const Sort *node) +{ + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(sortColIdx, node->numCols); + WRITE_OID_ARRAY(sortOperators, node->numCols); + WRITE_OID_ARRAY(collations, node->numCols); + WRITE_BOOL_ARRAY(nullsFirst, node->numCols); +} + +static void +_outSort(StringInfo str, const Sort *node) +{ + WRITE_NODE_TYPE("SORT"); + + _outSortInfo(str, node); +} + +static void +_outIncrementalSort(StringInfo str, const IncrementalSort *node) +{ + WRITE_NODE_TYPE("INCREMENTALSORT"); + + _outSortInfo(str, (const Sort *) node); + + WRITE_INT_FIELD(nPresortedCols); +} + +static void +_outUnique(StringInfo str, const Unique *node) +{ + WRITE_NODE_TYPE("UNIQUE"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(uniqColIdx, node->numCols); + WRITE_OID_ARRAY(uniqOperators, node->numCols); + WRITE_OID_ARRAY(uniqCollations, node->numCols); +} + +static void +_outHash(StringInfo str, const Hash *node) +{ + WRITE_NODE_TYPE("HASH"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_NODE_FIELD(hashkeys); + WRITE_OID_FIELD(skewTable); + WRITE_INT_FIELD(skewColumn); + WRITE_BOOL_FIELD(skewInherit); + WRITE_FLOAT_FIELD(rows_total, "%.0f"); +} + +static void +_outSetOp(StringInfo str, const SetOp *node) +{ + WRITE_NODE_TYPE("SETOP"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_ENUM_FIELD(cmd, SetOpCmd); + WRITE_ENUM_FIELD(strategy, SetOpStrategy); + WRITE_INT_FIELD(numCols); + WRITE_ATTRNUMBER_ARRAY(dupColIdx, node->numCols); + WRITE_OID_ARRAY(dupOperators, node->numCols); + WRITE_OID_ARRAY(dupCollations, node->numCols); + WRITE_INT_FIELD(flagColIdx); + WRITE_INT_FIELD(firstFlag); + WRITE_LONG_FIELD(numGroups); +} + +static void +_outLockRows(StringInfo str, const LockRows *node) +{ + WRITE_NODE_TYPE("LOCKROWS"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_NODE_FIELD(rowMarks); + WRITE_INT_FIELD(epqParam); +} + +static void +_outLimit(StringInfo str, const Limit *node) +{ + WRITE_NODE_TYPE("LIMIT"); + + _outPlanInfo(str, (const Plan *) node); + + WRITE_NODE_FIELD(limitOffset); + WRITE_NODE_FIELD(limitCount); + WRITE_ENUM_FIELD(limitOption, LimitOption); + WRITE_INT_FIELD(uniqNumCols); + WRITE_ATTRNUMBER_ARRAY(uniqColIdx, node->uniqNumCols); + WRITE_OID_ARRAY(uniqOperators, node->uniqNumCols); + WRITE_OID_ARRAY(uniqCollations, node->uniqNumCols); +} + +static void +_outNestLoopParam(StringInfo str, const NestLoopParam *node) +{ + WRITE_NODE_TYPE("NESTLOOPPARAM"); + + WRITE_INT_FIELD(paramno); + WRITE_NODE_FIELD(paramval); +} + +static void +_outPlanRowMark(StringInfo str, const PlanRowMark *node) +{ + WRITE_NODE_TYPE("PLANROWMARK"); + + WRITE_UINT_FIELD(rti); + WRITE_UINT_FIELD(prti); + WRITE_UINT_FIELD(rowmarkId); + WRITE_ENUM_FIELD(markType, RowMarkType); + WRITE_INT_FIELD(allMarkTypes); + WRITE_ENUM_FIELD(strength, LockClauseStrength); + WRITE_ENUM_FIELD(waitPolicy, LockWaitPolicy); + WRITE_BOOL_FIELD(isParent); +} + +static void +_outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) +{ + WRITE_NODE_TYPE("PARTITIONPRUNEINFO"); + + WRITE_NODE_FIELD(prune_infos); + WRITE_BITMAPSET_FIELD(other_subplans); +} + +static void +_outPartitionedRelPruneInfo(StringInfo str, const PartitionedRelPruneInfo *node) +{ + WRITE_NODE_TYPE("PARTITIONEDRELPRUNEINFO"); + + WRITE_UINT_FIELD(rtindex); + WRITE_BITMAPSET_FIELD(present_parts); + WRITE_INT_FIELD(nparts); + WRITE_INT_ARRAY(subplan_map, node->nparts); + WRITE_INT_ARRAY(subpart_map, node->nparts); + WRITE_OID_ARRAY(relid_map, node->nparts); + WRITE_NODE_FIELD(initial_pruning_steps); + WRITE_NODE_FIELD(exec_pruning_steps); + WRITE_BITMAPSET_FIELD(execparamids); +} + +static void +_outPartitionPruneStepOp(StringInfo str, const PartitionPruneStepOp *node) +{ + WRITE_NODE_TYPE("PARTITIONPRUNESTEPOP"); + + WRITE_INT_FIELD(step.step_id); + WRITE_INT_FIELD(opstrategy); + WRITE_NODE_FIELD(exprs); + WRITE_NODE_FIELD(cmpfns); + WRITE_BITMAPSET_FIELD(nullkeys); +} + +static void +_outPartitionPruneStepCombine(StringInfo str, const PartitionPruneStepCombine *node) +{ + WRITE_NODE_TYPE("PARTITIONPRUNESTEPCOMBINE"); + + WRITE_INT_FIELD(step.step_id); + WRITE_ENUM_FIELD(combineOp, PartitionPruneCombineOp); + WRITE_NODE_FIELD(source_stepids); +} + +static void +_outPlanInvalItem(StringInfo str, const PlanInvalItem *node) +{ + WRITE_NODE_TYPE("PLANINVALITEM"); + + WRITE_INT_FIELD(cacheId); + WRITE_UINT_FIELD(hashValue); +} + +/***************************************************************************** + * + * Stuff from primnodes.h. + * + *****************************************************************************/ + +static void +_outAlias(StringInfo str, const Alias *node) +{ + WRITE_NODE_TYPE("ALIAS"); + + WRITE_STRING_FIELD(aliasname); + WRITE_NODE_FIELD(colnames); +} + +static void +_outRangeVar(StringInfo str, const RangeVar *node) +{ + WRITE_NODE_TYPE("RANGEVAR"); + + /* + * we deliberately ignore catalogname here, since it is presently not + * semantically meaningful + */ + WRITE_STRING_FIELD(schemaname); + WRITE_STRING_FIELD(relname); + WRITE_BOOL_FIELD(inh); + WRITE_CHAR_FIELD(relpersistence); + WRITE_NODE_FIELD(alias); + WRITE_LOCATION_FIELD(location); +} + +static void +_outTableFunc(StringInfo str, const TableFunc *node) +{ + WRITE_NODE_TYPE("TABLEFUNC"); + + WRITE_NODE_FIELD(ns_uris); + WRITE_NODE_FIELD(ns_names); + WRITE_NODE_FIELD(docexpr); + WRITE_NODE_FIELD(rowexpr); + WRITE_NODE_FIELD(colnames); + WRITE_NODE_FIELD(coltypes); + WRITE_NODE_FIELD(coltypmods); + WRITE_NODE_FIELD(colcollations); + WRITE_NODE_FIELD(colexprs); + WRITE_NODE_FIELD(coldefexprs); + WRITE_BITMAPSET_FIELD(notnulls); + WRITE_INT_FIELD(ordinalitycol); + WRITE_LOCATION_FIELD(location); +} + +static void +_outIntoClause(StringInfo str, const IntoClause *node) +{ + WRITE_NODE_TYPE("INTOCLAUSE"); + + WRITE_NODE_FIELD(rel); + WRITE_NODE_FIELD(colNames); + WRITE_STRING_FIELD(accessMethod); + WRITE_NODE_FIELD(options); + WRITE_ENUM_FIELD(onCommit, OnCommitAction); + WRITE_STRING_FIELD(tableSpaceName); + WRITE_NODE_FIELD(viewQuery); + WRITE_BOOL_FIELD(skipData); +} + +static void +_outVar(StringInfo str, const Var *node) +{ + WRITE_NODE_TYPE("VAR"); + + WRITE_INT_FIELD(varno); + WRITE_INT_FIELD(varattno); + WRITE_OID_FIELD(vartype); + WRITE_INT_FIELD(vartypmod); + WRITE_OID_FIELD(varcollid); + WRITE_UINT_FIELD(varlevelsup); + WRITE_UINT_FIELD(varnosyn); + WRITE_INT_FIELD(varattnosyn); + WRITE_LOCATION_FIELD(location); +} + +static void +_outConst(StringInfo str, const Const *node) +{ + WRITE_NODE_TYPE("CONST"); + + WRITE_OID_FIELD(consttype); + WRITE_INT_FIELD(consttypmod); + WRITE_OID_FIELD(constcollid); + WRITE_INT_FIELD(constlen); + WRITE_BOOL_FIELD(constbyval); + WRITE_BOOL_FIELD(constisnull); + WRITE_LOCATION_FIELD(location); + + appendStringInfoString(str, " :constvalue "); + if (node->constisnull) + appendStringInfoString(str, "<>"); + else + outDatum(str, node->constvalue, node->constlen, node->constbyval); +} + +static void +_outParam(StringInfo str, const Param *node) +{ + WRITE_NODE_TYPE("PARAM"); + + WRITE_ENUM_FIELD(paramkind, ParamKind); + WRITE_INT_FIELD(paramid); + WRITE_OID_FIELD(paramtype); + WRITE_INT_FIELD(paramtypmod); + WRITE_OID_FIELD(paramcollid); + WRITE_LOCATION_FIELD(location); +} + +static void +_outAggref(StringInfo str, const Aggref *node) +{ + WRITE_NODE_TYPE("AGGREF"); + + WRITE_OID_FIELD(aggfnoid); + WRITE_OID_FIELD(aggtype); + WRITE_OID_FIELD(aggcollid); + WRITE_OID_FIELD(inputcollid); + WRITE_OID_FIELD(aggtranstype); + WRITE_NODE_FIELD(aggargtypes); + WRITE_NODE_FIELD(aggdirectargs); + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(aggorder); + WRITE_NODE_FIELD(aggdistinct); + WRITE_NODE_FIELD(aggfilter); + WRITE_BOOL_FIELD(aggstar); + WRITE_BOOL_FIELD(aggvariadic); + WRITE_CHAR_FIELD(aggkind); + WRITE_UINT_FIELD(agglevelsup); + WRITE_ENUM_FIELD(aggsplit, AggSplit); + WRITE_INT_FIELD(aggno); + WRITE_INT_FIELD(aggtransno); + WRITE_LOCATION_FIELD(location); +} + +static void +_outGroupingFunc(StringInfo str, const GroupingFunc *node) +{ + WRITE_NODE_TYPE("GROUPINGFUNC"); + + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(refs); + WRITE_NODE_FIELD(cols); + WRITE_UINT_FIELD(agglevelsup); + WRITE_LOCATION_FIELD(location); +} + +static void +_outWindowFunc(StringInfo str, const WindowFunc *node) +{ + WRITE_NODE_TYPE("WINDOWFUNC"); + + WRITE_OID_FIELD(winfnoid); + WRITE_OID_FIELD(wintype); + WRITE_OID_FIELD(wincollid); + WRITE_OID_FIELD(inputcollid); + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(aggfilter); + WRITE_UINT_FIELD(winref); + WRITE_BOOL_FIELD(winstar); + WRITE_BOOL_FIELD(winagg); + WRITE_LOCATION_FIELD(location); +} + +static void +_outSubscriptingRef(StringInfo str, const SubscriptingRef *node) +{ + WRITE_NODE_TYPE("SUBSCRIPTINGREF"); + + WRITE_OID_FIELD(refcontainertype); + WRITE_OID_FIELD(refelemtype); + WRITE_OID_FIELD(refrestype); + WRITE_INT_FIELD(reftypmod); + WRITE_OID_FIELD(refcollid); + WRITE_NODE_FIELD(refupperindexpr); + WRITE_NODE_FIELD(reflowerindexpr); + WRITE_NODE_FIELD(refexpr); + WRITE_NODE_FIELD(refassgnexpr); +} + +static void +_outFuncExpr(StringInfo str, const FuncExpr *node) +{ + WRITE_NODE_TYPE("FUNCEXPR"); + + WRITE_OID_FIELD(funcid); + WRITE_OID_FIELD(funcresulttype); + WRITE_BOOL_FIELD(funcretset); + WRITE_BOOL_FIELD(funcvariadic); + WRITE_ENUM_FIELD(funcformat, CoercionForm); + WRITE_OID_FIELD(funccollid); + WRITE_OID_FIELD(inputcollid); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outNamedArgExpr(StringInfo str, const NamedArgExpr *node) +{ + WRITE_NODE_TYPE("NAMEDARGEXPR"); + + WRITE_NODE_FIELD(arg); + WRITE_STRING_FIELD(name); + WRITE_INT_FIELD(argnumber); + WRITE_LOCATION_FIELD(location); +} + +static void +_outOpExpr(StringInfo str, const OpExpr *node) +{ + WRITE_NODE_TYPE("OPEXPR"); + + WRITE_OID_FIELD(opno); + WRITE_OID_FIELD(opfuncid); + WRITE_OID_FIELD(opresulttype); + WRITE_BOOL_FIELD(opretset); + WRITE_OID_FIELD(opcollid); + WRITE_OID_FIELD(inputcollid); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outDistinctExpr(StringInfo str, const DistinctExpr *node) +{ + WRITE_NODE_TYPE("DISTINCTEXPR"); + + WRITE_OID_FIELD(opno); + WRITE_OID_FIELD(opfuncid); + WRITE_OID_FIELD(opresulttype); + WRITE_BOOL_FIELD(opretset); + WRITE_OID_FIELD(opcollid); + WRITE_OID_FIELD(inputcollid); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outNullIfExpr(StringInfo str, const NullIfExpr *node) +{ + WRITE_NODE_TYPE("NULLIFEXPR"); + + WRITE_OID_FIELD(opno); + WRITE_OID_FIELD(opfuncid); + WRITE_OID_FIELD(opresulttype); + WRITE_BOOL_FIELD(opretset); + WRITE_OID_FIELD(opcollid); + WRITE_OID_FIELD(inputcollid); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node) +{ + WRITE_NODE_TYPE("SCALARARRAYOPEXPR"); + + WRITE_OID_FIELD(opno); + WRITE_OID_FIELD(opfuncid); + WRITE_OID_FIELD(hashfuncid); + WRITE_OID_FIELD(negfuncid); + WRITE_BOOL_FIELD(useOr); + WRITE_OID_FIELD(inputcollid); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outBoolExpr(StringInfo str, const BoolExpr *node) +{ + char *opstr = NULL; + + WRITE_NODE_TYPE("BOOLEXPR"); + + /* do-it-yourself enum representation */ + switch (node->boolop) + { + case AND_EXPR: + opstr = "and"; + break; + case OR_EXPR: + opstr = "or"; + break; + case NOT_EXPR: + opstr = "not"; + break; + } + appendStringInfoString(str, " :boolop "); + outToken(str, opstr); + + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outSubLink(StringInfo str, const SubLink *node) +{ + WRITE_NODE_TYPE("SUBLINK"); + + WRITE_ENUM_FIELD(subLinkType, SubLinkType); + WRITE_INT_FIELD(subLinkId); + WRITE_NODE_FIELD(testexpr); + WRITE_NODE_FIELD(operName); + WRITE_NODE_FIELD(subselect); + WRITE_LOCATION_FIELD(location); +} + +static void +_outSubPlan(StringInfo str, const SubPlan *node) +{ + WRITE_NODE_TYPE("SUBPLAN"); + + WRITE_ENUM_FIELD(subLinkType, SubLinkType); + WRITE_NODE_FIELD(testexpr); + WRITE_NODE_FIELD(paramIds); + WRITE_INT_FIELD(plan_id); + WRITE_STRING_FIELD(plan_name); + WRITE_OID_FIELD(firstColType); + WRITE_INT_FIELD(firstColTypmod); + WRITE_OID_FIELD(firstColCollation); + WRITE_BOOL_FIELD(useHashTable); + WRITE_BOOL_FIELD(unknownEqFalse); + WRITE_BOOL_FIELD(parallel_safe); + WRITE_NODE_FIELD(setParam); + WRITE_NODE_FIELD(parParam); + WRITE_NODE_FIELD(args); + WRITE_FLOAT_FIELD(startup_cost, "%.2f"); + WRITE_FLOAT_FIELD(per_call_cost, "%.2f"); +} + +static void +_outAlternativeSubPlan(StringInfo str, const AlternativeSubPlan *node) +{ + WRITE_NODE_TYPE("ALTERNATIVESUBPLAN"); + + WRITE_NODE_FIELD(subplans); +} + +static void +_outFieldSelect(StringInfo str, const FieldSelect *node) +{ + WRITE_NODE_TYPE("FIELDSELECT"); + + WRITE_NODE_FIELD(arg); + WRITE_INT_FIELD(fieldnum); + WRITE_OID_FIELD(resulttype); + WRITE_INT_FIELD(resulttypmod); + WRITE_OID_FIELD(resultcollid); +} + +static void +_outFieldStore(StringInfo str, const FieldStore *node) +{ + WRITE_NODE_TYPE("FIELDSTORE"); + + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(newvals); + WRITE_NODE_FIELD(fieldnums); + WRITE_OID_FIELD(resulttype); +} + +static void +_outRelabelType(StringInfo str, const RelabelType *node) +{ + WRITE_NODE_TYPE("RELABELTYPE"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(resulttype); + WRITE_INT_FIELD(resulttypmod); + WRITE_OID_FIELD(resultcollid); + WRITE_ENUM_FIELD(relabelformat, CoercionForm); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCoerceViaIO(StringInfo str, const CoerceViaIO *node) +{ + WRITE_NODE_TYPE("COERCEVIAIO"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(resulttype); + WRITE_OID_FIELD(resultcollid); + WRITE_ENUM_FIELD(coerceformat, CoercionForm); + WRITE_LOCATION_FIELD(location); +} + +static void +_outArrayCoerceExpr(StringInfo str, const ArrayCoerceExpr *node) +{ + WRITE_NODE_TYPE("ARRAYCOERCEEXPR"); + + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(elemexpr); + WRITE_OID_FIELD(resulttype); + WRITE_INT_FIELD(resulttypmod); + WRITE_OID_FIELD(resultcollid); + WRITE_ENUM_FIELD(coerceformat, CoercionForm); + WRITE_LOCATION_FIELD(location); +} + +static void +_outConvertRowtypeExpr(StringInfo str, const ConvertRowtypeExpr *node) +{ + WRITE_NODE_TYPE("CONVERTROWTYPEEXPR"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(resulttype); + WRITE_ENUM_FIELD(convertformat, CoercionForm); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCollateExpr(StringInfo str, const CollateExpr *node) +{ + WRITE_NODE_TYPE("COLLATEEXPR"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(collOid); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCaseExpr(StringInfo str, const CaseExpr *node) +{ + WRITE_NODE_TYPE("CASEEXPR"); + + WRITE_OID_FIELD(casetype); + WRITE_OID_FIELD(casecollid); + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(defresult); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCaseWhen(StringInfo str, const CaseWhen *node) +{ + WRITE_NODE_TYPE("CASEWHEN"); + + WRITE_NODE_FIELD(expr); + WRITE_NODE_FIELD(result); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCaseTestExpr(StringInfo str, const CaseTestExpr *node) +{ + WRITE_NODE_TYPE("CASETESTEXPR"); + + WRITE_OID_FIELD(typeId); + WRITE_INT_FIELD(typeMod); + WRITE_OID_FIELD(collation); +} + +static void +_outArrayExpr(StringInfo str, const ArrayExpr *node) +{ + WRITE_NODE_TYPE("ARRAYEXPR"); + + WRITE_OID_FIELD(array_typeid); + WRITE_OID_FIELD(array_collid); + WRITE_OID_FIELD(element_typeid); + WRITE_NODE_FIELD(elements); + WRITE_BOOL_FIELD(multidims); + WRITE_LOCATION_FIELD(location); +} + +static void +_outRowExpr(StringInfo str, const RowExpr *node) +{ + WRITE_NODE_TYPE("ROWEXPR"); + + WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(row_typeid); + WRITE_ENUM_FIELD(row_format, CoercionForm); + WRITE_NODE_FIELD(colnames); + WRITE_LOCATION_FIELD(location); +} + +static void +_outRowCompareExpr(StringInfo str, const RowCompareExpr *node) +{ + WRITE_NODE_TYPE("ROWCOMPAREEXPR"); + + WRITE_ENUM_FIELD(rctype, RowCompareType); + WRITE_NODE_FIELD(opnos); + WRITE_NODE_FIELD(opfamilies); + WRITE_NODE_FIELD(inputcollids); + WRITE_NODE_FIELD(largs); + WRITE_NODE_FIELD(rargs); +} + +static void +_outCoalesceExpr(StringInfo str, const CoalesceExpr *node) +{ + WRITE_NODE_TYPE("COALESCEEXPR"); + + WRITE_OID_FIELD(coalescetype); + WRITE_OID_FIELD(coalescecollid); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outMinMaxExpr(StringInfo str, const MinMaxExpr *node) +{ + WRITE_NODE_TYPE("MINMAXEXPR"); + + WRITE_OID_FIELD(minmaxtype); + WRITE_OID_FIELD(minmaxcollid); + WRITE_OID_FIELD(inputcollid); + WRITE_ENUM_FIELD(op, MinMaxOp); + WRITE_NODE_FIELD(args); + WRITE_LOCATION_FIELD(location); +} + +static void +_outSQLValueFunction(StringInfo str, const SQLValueFunction *node) +{ + WRITE_NODE_TYPE("SQLVALUEFUNCTION"); + + WRITE_ENUM_FIELD(op, SQLValueFunctionOp); + WRITE_OID_FIELD(type); + WRITE_INT_FIELD(typmod); + WRITE_LOCATION_FIELD(location); +} + +static void +_outXmlExpr(StringInfo str, const XmlExpr *node) +{ + WRITE_NODE_TYPE("XMLEXPR"); + + WRITE_ENUM_FIELD(op, XmlExprOp); + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(named_args); + WRITE_NODE_FIELD(arg_names); + WRITE_NODE_FIELD(args); + WRITE_ENUM_FIELD(xmloption, XmlOptionType); + WRITE_OID_FIELD(type); + WRITE_INT_FIELD(typmod); + WRITE_LOCATION_FIELD(location); +} + +static void +_outNullTest(StringInfo str, const NullTest *node) +{ + WRITE_NODE_TYPE("NULLTEST"); + + WRITE_NODE_FIELD(arg); + WRITE_ENUM_FIELD(nulltesttype, NullTestType); + WRITE_BOOL_FIELD(argisrow); + WRITE_LOCATION_FIELD(location); +} + +static void +_outBooleanTest(StringInfo str, const BooleanTest *node) +{ + WRITE_NODE_TYPE("BOOLEANTEST"); + + WRITE_NODE_FIELD(arg); + WRITE_ENUM_FIELD(booltesttype, BoolTestType); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCoerceToDomain(StringInfo str, const CoerceToDomain *node) +{ + WRITE_NODE_TYPE("COERCETODOMAIN"); + + WRITE_NODE_FIELD(arg); + WRITE_OID_FIELD(resulttype); + WRITE_INT_FIELD(resulttypmod); + WRITE_OID_FIELD(resultcollid); + WRITE_ENUM_FIELD(coercionformat, CoercionForm); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCoerceToDomainValue(StringInfo str, const CoerceToDomainValue *node) +{ + WRITE_NODE_TYPE("COERCETODOMAINVALUE"); + + WRITE_OID_FIELD(typeId); + WRITE_INT_FIELD(typeMod); + WRITE_OID_FIELD(collation); + WRITE_LOCATION_FIELD(location); +} + +static void +_outSetToDefault(StringInfo str, const SetToDefault *node) +{ + WRITE_NODE_TYPE("SETTODEFAULT"); + + WRITE_OID_FIELD(typeId); + WRITE_INT_FIELD(typeMod); + WRITE_OID_FIELD(collation); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCurrentOfExpr(StringInfo str, const CurrentOfExpr *node) +{ + WRITE_NODE_TYPE("CURRENTOFEXPR"); + + WRITE_UINT_FIELD(cvarno); + WRITE_STRING_FIELD(cursor_name); + WRITE_INT_FIELD(cursor_param); +} + +static void +_outNextValueExpr(StringInfo str, const NextValueExpr *node) +{ + WRITE_NODE_TYPE("NEXTVALUEEXPR"); + + WRITE_OID_FIELD(seqid); + WRITE_OID_FIELD(typeId); +} + +static void +_outInferenceElem(StringInfo str, const InferenceElem *node) +{ + WRITE_NODE_TYPE("INFERENCEELEM"); + + WRITE_NODE_FIELD(expr); + WRITE_OID_FIELD(infercollid); + WRITE_OID_FIELD(inferopclass); +} + +static void +_outTargetEntry(StringInfo str, const TargetEntry *node) +{ + WRITE_NODE_TYPE("TARGETENTRY"); + + WRITE_NODE_FIELD(expr); + WRITE_INT_FIELD(resno); + WRITE_STRING_FIELD(resname); + WRITE_UINT_FIELD(ressortgroupref); + WRITE_OID_FIELD(resorigtbl); + WRITE_INT_FIELD(resorigcol); + WRITE_BOOL_FIELD(resjunk); +} + +static void +_outRangeTblRef(StringInfo str, const RangeTblRef *node) +{ + WRITE_NODE_TYPE("RANGETBLREF"); + + WRITE_INT_FIELD(rtindex); +} + +static void +_outJoinExpr(StringInfo str, const JoinExpr *node) +{ + WRITE_NODE_TYPE("JOINEXPR"); + + WRITE_ENUM_FIELD(jointype, JoinType); + WRITE_BOOL_FIELD(isNatural); + WRITE_NODE_FIELD(larg); + WRITE_NODE_FIELD(rarg); + WRITE_NODE_FIELD(usingClause); + WRITE_NODE_FIELD(join_using_alias); + WRITE_NODE_FIELD(quals); + WRITE_NODE_FIELD(alias); + WRITE_INT_FIELD(rtindex); +} + +static void +_outFromExpr(StringInfo str, const FromExpr *node) +{ + WRITE_NODE_TYPE("FROMEXPR"); + + WRITE_NODE_FIELD(fromlist); + WRITE_NODE_FIELD(quals); +} + +static void +_outOnConflictExpr(StringInfo str, const OnConflictExpr *node) +{ + WRITE_NODE_TYPE("ONCONFLICTEXPR"); + + WRITE_ENUM_FIELD(action, OnConflictAction); + WRITE_NODE_FIELD(arbiterElems); + WRITE_NODE_FIELD(arbiterWhere); + WRITE_OID_FIELD(constraint); + WRITE_NODE_FIELD(onConflictSet); + WRITE_NODE_FIELD(onConflictWhere); + WRITE_INT_FIELD(exclRelIndex); + WRITE_NODE_FIELD(exclRelTlist); +} + +/***************************************************************************** + * + * Stuff from pathnodes.h. + * + *****************************************************************************/ + +/* + * print the basic stuff of all nodes that inherit from Path + * + * Note we do NOT print the parent, else we'd be in infinite recursion. + * We can print the parent's relids for identification purposes, though. + * We print the pathtarget only if it's not the default one for the rel. + * We also do not print the whole of param_info, since it's printed by + * _outRelOptInfo; it's sufficient and less cluttering to print just the + * required outer relids. + */ +static void +_outPathInfo(StringInfo str, const Path *node) +{ + WRITE_ENUM_FIELD(pathtype, NodeTag); + appendStringInfoString(str, " :parent_relids "); + outBitmapset(str, node->parent->relids); + if (node->pathtarget != node->parent->reltarget) + WRITE_NODE_FIELD(pathtarget); + appendStringInfoString(str, " :required_outer "); + if (node->param_info) + outBitmapset(str, node->param_info->ppi_req_outer); + else + outBitmapset(str, NULL); + WRITE_BOOL_FIELD(parallel_aware); + WRITE_BOOL_FIELD(parallel_safe); + WRITE_INT_FIELD(parallel_workers); + WRITE_FLOAT_FIELD(rows, "%.0f"); + WRITE_FLOAT_FIELD(startup_cost, "%.2f"); + WRITE_FLOAT_FIELD(total_cost, "%.2f"); + WRITE_NODE_FIELD(pathkeys); +} + +/* + * print the basic stuff of all nodes that inherit from JoinPath + */ +static void +_outJoinPathInfo(StringInfo str, const JoinPath *node) +{ + _outPathInfo(str, (const Path *) node); + + WRITE_ENUM_FIELD(jointype, JoinType); + WRITE_BOOL_FIELD(inner_unique); + WRITE_NODE_FIELD(outerjoinpath); + WRITE_NODE_FIELD(innerjoinpath); + WRITE_NODE_FIELD(joinrestrictinfo); +} + +static void +_outPath(StringInfo str, const Path *node) +{ + WRITE_NODE_TYPE("PATH"); + + _outPathInfo(str, (const Path *) node); +} + +static void +_outIndexPath(StringInfo str, const IndexPath *node) +{ + WRITE_NODE_TYPE("INDEXPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(indexinfo); + WRITE_NODE_FIELD(indexclauses); + WRITE_NODE_FIELD(indexorderbys); + WRITE_NODE_FIELD(indexorderbycols); + WRITE_ENUM_FIELD(indexscandir, ScanDirection); + WRITE_FLOAT_FIELD(indextotalcost, "%.2f"); + WRITE_FLOAT_FIELD(indexselectivity, "%.4f"); +} + +static void +_outBitmapHeapPath(StringInfo str, const BitmapHeapPath *node) +{ + WRITE_NODE_TYPE("BITMAPHEAPPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(bitmapqual); +} + +static void +_outBitmapAndPath(StringInfo str, const BitmapAndPath *node) +{ + WRITE_NODE_TYPE("BITMAPANDPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(bitmapquals); + WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f"); +} + +static void +_outBitmapOrPath(StringInfo str, const BitmapOrPath *node) +{ + WRITE_NODE_TYPE("BITMAPORPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(bitmapquals); + WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f"); +} + +static void +_outTidPath(StringInfo str, const TidPath *node) +{ + WRITE_NODE_TYPE("TIDPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(tidquals); +} + +static void +_outTidRangePath(StringInfo str, const TidRangePath *node) +{ + WRITE_NODE_TYPE("TIDRANGEPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(tidrangequals); +} + +static void +_outSubqueryScanPath(StringInfo str, const SubqueryScanPath *node) +{ + WRITE_NODE_TYPE("SUBQUERYSCANPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); +} + +static void +_outForeignPath(StringInfo str, const ForeignPath *node) +{ + WRITE_NODE_TYPE("FOREIGNPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(fdw_outerpath); + WRITE_NODE_FIELD(fdw_private); +} + +static void +_outCustomPath(StringInfo str, const CustomPath *node) +{ + WRITE_NODE_TYPE("CUSTOMPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_UINT_FIELD(flags); + WRITE_NODE_FIELD(custom_paths); + WRITE_NODE_FIELD(custom_private); + appendStringInfoString(str, " :methods "); + outToken(str, node->methods->CustomName); +} + +static void +_outAppendPath(StringInfo str, const AppendPath *node) +{ + WRITE_NODE_TYPE("APPENDPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpaths); + WRITE_INT_FIELD(first_partial_path); + WRITE_FLOAT_FIELD(limit_tuples, "%.0f"); +} + +static void +_outMergeAppendPath(StringInfo str, const MergeAppendPath *node) +{ + WRITE_NODE_TYPE("MERGEAPPENDPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpaths); + WRITE_FLOAT_FIELD(limit_tuples, "%.0f"); +} + +static void +_outGroupResultPath(StringInfo str, const GroupResultPath *node) +{ + WRITE_NODE_TYPE("GROUPRESULTPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(quals); +} + +static void +_outMaterialPath(StringInfo str, const MaterialPath *node) +{ + WRITE_NODE_TYPE("MATERIALPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); +} + +static void +_outMemoizePath(StringInfo str, const MemoizePath *node) +{ + WRITE_NODE_TYPE("MEMOIZEPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_NODE_FIELD(hash_operators); + WRITE_NODE_FIELD(param_exprs); + WRITE_BOOL_FIELD(singlerow); + WRITE_BOOL_FIELD(binary_mode); + WRITE_FLOAT_FIELD(calls, "%.0f"); + WRITE_UINT_FIELD(est_entries); +} + +static void +_outUniquePath(StringInfo str, const UniquePath *node) +{ + WRITE_NODE_TYPE("UNIQUEPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_ENUM_FIELD(umethod, UniquePathMethod); + WRITE_NODE_FIELD(in_operators); + WRITE_NODE_FIELD(uniq_exprs); +} + +static void +_outGatherPath(StringInfo str, const GatherPath *node) +{ + WRITE_NODE_TYPE("GATHERPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_BOOL_FIELD(single_copy); + WRITE_INT_FIELD(num_workers); +} + +static void +_outProjectionPath(StringInfo str, const ProjectionPath *node) +{ + WRITE_NODE_TYPE("PROJECTIONPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_BOOL_FIELD(dummypp); +} + +static void +_outProjectSetPath(StringInfo str, const ProjectSetPath *node) +{ + WRITE_NODE_TYPE("PROJECTSETPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); +} + +static void +_outSortPathInfo(StringInfo str, const SortPath *node) +{ + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); +} + +static void +_outSortPath(StringInfo str, const SortPath *node) +{ + WRITE_NODE_TYPE("SORTPATH"); + + _outSortPathInfo(str, node); +} + +static void +_outIncrementalSortPath(StringInfo str, const IncrementalSortPath *node) +{ + WRITE_NODE_TYPE("INCREMENTALSORTPATH"); + + _outSortPathInfo(str, (const SortPath *) node); + + WRITE_INT_FIELD(nPresortedCols); +} + +static void +_outGroupPath(StringInfo str, const GroupPath *node) +{ + WRITE_NODE_TYPE("GROUPPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_NODE_FIELD(groupClause); + WRITE_NODE_FIELD(qual); +} + +static void +_outUpperUniquePath(StringInfo str, const UpperUniquePath *node) +{ + WRITE_NODE_TYPE("UPPERUNIQUEPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_INT_FIELD(numkeys); +} + +static void +_outAggPath(StringInfo str, const AggPath *node) +{ + WRITE_NODE_TYPE("AGGPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_ENUM_FIELD(aggstrategy, AggStrategy); + WRITE_ENUM_FIELD(aggsplit, AggSplit); + WRITE_FLOAT_FIELD(numGroups, "%.0f"); + WRITE_UINT64_FIELD(transitionSpace); + WRITE_NODE_FIELD(groupClause); + WRITE_NODE_FIELD(qual); +} + +static void +_outRollupData(StringInfo str, const RollupData *node) +{ + WRITE_NODE_TYPE("ROLLUP"); + + WRITE_NODE_FIELD(groupClause); + WRITE_NODE_FIELD(gsets); + WRITE_NODE_FIELD(gsets_data); + WRITE_FLOAT_FIELD(numGroups, "%.0f"); + WRITE_BOOL_FIELD(hashable); + WRITE_BOOL_FIELD(is_hashed); +} + +static void +_outGroupingSetData(StringInfo str, const GroupingSetData *node) +{ + WRITE_NODE_TYPE("GSDATA"); + + WRITE_NODE_FIELD(set); + WRITE_FLOAT_FIELD(numGroups, "%.0f"); +} + +static void +_outGroupingSetsPath(StringInfo str, const GroupingSetsPath *node) +{ + WRITE_NODE_TYPE("GROUPINGSETSPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_ENUM_FIELD(aggstrategy, AggStrategy); + WRITE_NODE_FIELD(rollups); + WRITE_NODE_FIELD(qual); + WRITE_UINT64_FIELD(transitionSpace); +} + +static void +_outMinMaxAggPath(StringInfo str, const MinMaxAggPath *node) +{ + WRITE_NODE_TYPE("MINMAXAGGPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(mmaggregates); + WRITE_NODE_FIELD(quals); +} + +static void +_outWindowAggPath(StringInfo str, const WindowAggPath *node) +{ + WRITE_NODE_TYPE("WINDOWAGGPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_NODE_FIELD(winclause); + WRITE_NODE_FIELD(qual); + WRITE_BOOL_FIELD(topwindow); +} + +static void +_outSetOpPath(StringInfo str, const SetOpPath *node) +{ + WRITE_NODE_TYPE("SETOPPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_ENUM_FIELD(cmd, SetOpCmd); + WRITE_ENUM_FIELD(strategy, SetOpStrategy); + WRITE_NODE_FIELD(distinctList); + WRITE_INT_FIELD(flagColIdx); + WRITE_INT_FIELD(firstFlag); + WRITE_FLOAT_FIELD(numGroups, "%.0f"); +} + +static void +_outRecursiveUnionPath(StringInfo str, const RecursiveUnionPath *node) +{ + WRITE_NODE_TYPE("RECURSIVEUNIONPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(leftpath); + WRITE_NODE_FIELD(rightpath); + WRITE_NODE_FIELD(distinctList); + WRITE_INT_FIELD(wtParam); + WRITE_FLOAT_FIELD(numGroups, "%.0f"); +} + +static void +_outLockRowsPath(StringInfo str, const LockRowsPath *node) +{ + WRITE_NODE_TYPE("LOCKROWSPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_NODE_FIELD(rowMarks); + WRITE_INT_FIELD(epqParam); +} + +static void +_outModifyTablePath(StringInfo str, const ModifyTablePath *node) +{ + WRITE_NODE_TYPE("MODIFYTABLEPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_ENUM_FIELD(operation, CmdType); + WRITE_BOOL_FIELD(canSetTag); + WRITE_UINT_FIELD(nominalRelation); + WRITE_UINT_FIELD(rootRelation); + WRITE_BOOL_FIELD(partColsUpdated); + WRITE_NODE_FIELD(resultRelations); + WRITE_NODE_FIELD(updateColnosLists); + WRITE_NODE_FIELD(withCheckOptionLists); + WRITE_NODE_FIELD(returningLists); + WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(onconflict); + WRITE_INT_FIELD(epqParam); + WRITE_NODE_FIELD(mergeActionLists); +} + +static void +_outLimitPath(StringInfo str, const LimitPath *node) +{ + WRITE_NODE_TYPE("LIMITPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_NODE_FIELD(limitOffset); + WRITE_NODE_FIELD(limitCount); + WRITE_ENUM_FIELD(limitOption, LimitOption); +} + +static void +_outGatherMergePath(StringInfo str, const GatherMergePath *node) +{ + WRITE_NODE_TYPE("GATHERMERGEPATH"); + + _outPathInfo(str, (const Path *) node); + + WRITE_NODE_FIELD(subpath); + WRITE_INT_FIELD(num_workers); +} + +static void +_outNestPath(StringInfo str, const NestPath *node) +{ + WRITE_NODE_TYPE("NESTPATH"); + + _outJoinPathInfo(str, (const JoinPath *) node); +} + +static void +_outMergePath(StringInfo str, const MergePath *node) +{ + WRITE_NODE_TYPE("MERGEPATH"); + + _outJoinPathInfo(str, (const JoinPath *) node); + + WRITE_NODE_FIELD(path_mergeclauses); + WRITE_NODE_FIELD(outersortkeys); + WRITE_NODE_FIELD(innersortkeys); + WRITE_BOOL_FIELD(skip_mark_restore); + WRITE_BOOL_FIELD(materialize_inner); +} + +static void +_outHashPath(StringInfo str, const HashPath *node) +{ + WRITE_NODE_TYPE("HASHPATH"); + + _outJoinPathInfo(str, (const JoinPath *) node); + + WRITE_NODE_FIELD(path_hashclauses); + WRITE_INT_FIELD(num_batches); + WRITE_FLOAT_FIELD(inner_rows_total, "%.0f"); +} + +static void +_outPlannerGlobal(StringInfo str, const PlannerGlobal *node) +{ + WRITE_NODE_TYPE("PLANNERGLOBAL"); + + /* NB: this isn't a complete set of fields */ + WRITE_NODE_FIELD(subplans); + WRITE_BITMAPSET_FIELD(rewindPlanIDs); + WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrowmarks); + WRITE_NODE_FIELD(resultRelations); + WRITE_NODE_FIELD(appendRelations); + WRITE_NODE_FIELD(relationOids); + WRITE_NODE_FIELD(invalItems); + WRITE_NODE_FIELD(paramExecTypes); + WRITE_UINT_FIELD(lastPHId); + WRITE_UINT_FIELD(lastRowMarkId); + WRITE_INT_FIELD(lastPlanNodeId); + WRITE_BOOL_FIELD(transientPlan); + WRITE_BOOL_FIELD(dependsOnRole); + WRITE_BOOL_FIELD(parallelModeOK); + WRITE_BOOL_FIELD(parallelModeNeeded); + WRITE_CHAR_FIELD(maxParallelHazard); +} + +static void +_outPlannerInfo(StringInfo str, const PlannerInfo *node) +{ + WRITE_NODE_TYPE("PLANNERINFO"); + + /* NB: this isn't a complete set of fields */ + WRITE_NODE_FIELD(parse); + WRITE_NODE_FIELD(glob); + WRITE_UINT_FIELD(query_level); + WRITE_NODE_FIELD(plan_params); + WRITE_BITMAPSET_FIELD(outer_params); + WRITE_BITMAPSET_FIELD(all_baserels); + WRITE_BITMAPSET_FIELD(nullable_baserels); + WRITE_NODE_FIELD(join_rel_list); + WRITE_INT_FIELD(join_cur_level); + WRITE_NODE_FIELD(init_plans); + WRITE_NODE_FIELD(cte_plan_ids); + WRITE_NODE_FIELD(multiexpr_params); + WRITE_NODE_FIELD(eq_classes); + WRITE_BOOL_FIELD(ec_merging_done); + WRITE_NODE_FIELD(canon_pathkeys); + WRITE_NODE_FIELD(left_join_clauses); + WRITE_NODE_FIELD(right_join_clauses); + WRITE_NODE_FIELD(full_join_clauses); + WRITE_NODE_FIELD(join_info_list); + WRITE_BITMAPSET_FIELD(all_result_relids); + WRITE_BITMAPSET_FIELD(leaf_result_relids); + WRITE_NODE_FIELD(append_rel_list); + WRITE_NODE_FIELD(row_identity_vars); + WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(placeholder_list); + WRITE_NODE_FIELD(fkey_list); + WRITE_NODE_FIELD(query_pathkeys); + WRITE_NODE_FIELD(group_pathkeys); + WRITE_NODE_FIELD(window_pathkeys); + WRITE_NODE_FIELD(distinct_pathkeys); + WRITE_NODE_FIELD(sort_pathkeys); + WRITE_NODE_FIELD(processed_tlist); + WRITE_NODE_FIELD(update_colnos); + WRITE_NODE_FIELD(minmax_aggs); + WRITE_FLOAT_FIELD(total_table_pages, "%.0f"); + WRITE_FLOAT_FIELD(tuple_fraction, "%.4f"); + WRITE_FLOAT_FIELD(limit_tuples, "%.0f"); + WRITE_UINT_FIELD(qual_security_level); + WRITE_BOOL_FIELD(hasJoinRTEs); + WRITE_BOOL_FIELD(hasLateralRTEs); + WRITE_BOOL_FIELD(hasHavingQual); + WRITE_BOOL_FIELD(hasPseudoConstantQuals); + WRITE_BOOL_FIELD(hasAlternativeSubPlans); + WRITE_BOOL_FIELD(hasRecursion); + WRITE_INT_FIELD(wt_param_id); + WRITE_BITMAPSET_FIELD(curOuterRels); + WRITE_NODE_FIELD(curOuterParams); + WRITE_BOOL_FIELD(partColsUpdated); +} + +static void +_outRelOptInfo(StringInfo str, const RelOptInfo *node) +{ + WRITE_NODE_TYPE("RELOPTINFO"); + + /* NB: this isn't a complete set of fields */ + WRITE_ENUM_FIELD(reloptkind, RelOptKind); + WRITE_BITMAPSET_FIELD(relids); + WRITE_FLOAT_FIELD(rows, "%.0f"); + WRITE_BOOL_FIELD(consider_startup); + WRITE_BOOL_FIELD(consider_param_startup); + WRITE_BOOL_FIELD(consider_parallel); + WRITE_NODE_FIELD(reltarget); + WRITE_NODE_FIELD(pathlist); + WRITE_NODE_FIELD(ppilist); + WRITE_NODE_FIELD(partial_pathlist); + WRITE_NODE_FIELD(cheapest_startup_path); + WRITE_NODE_FIELD(cheapest_total_path); + WRITE_NODE_FIELD(cheapest_unique_path); + WRITE_NODE_FIELD(cheapest_parameterized_paths); + WRITE_BITMAPSET_FIELD(direct_lateral_relids); + WRITE_BITMAPSET_FIELD(lateral_relids); + WRITE_UINT_FIELD(relid); + WRITE_OID_FIELD(reltablespace); + WRITE_ENUM_FIELD(rtekind, RTEKind); + WRITE_INT_FIELD(min_attr); + WRITE_INT_FIELD(max_attr); + WRITE_NODE_FIELD(lateral_vars); + WRITE_BITMAPSET_FIELD(lateral_referencers); + WRITE_NODE_FIELD(indexlist); + WRITE_NODE_FIELD(statlist); + WRITE_UINT_FIELD(pages); + WRITE_FLOAT_FIELD(tuples, "%.0f"); + WRITE_FLOAT_FIELD(allvisfrac, "%.6f"); + WRITE_BITMAPSET_FIELD(eclass_indexes); + WRITE_NODE_FIELD(subroot); + WRITE_NODE_FIELD(subplan_params); + WRITE_INT_FIELD(rel_parallel_workers); + WRITE_UINT_FIELD(amflags); + WRITE_OID_FIELD(serverid); + WRITE_OID_FIELD(userid); + WRITE_BOOL_FIELD(useridiscurrent); + /* we don't try to print fdwroutine or fdw_private */ + /* can't print unique_for_rels/non_unique_for_rels; BMSes aren't Nodes */ + WRITE_NODE_FIELD(baserestrictinfo); + WRITE_UINT_FIELD(baserestrict_min_security); + WRITE_NODE_FIELD(joininfo); + WRITE_BOOL_FIELD(has_eclass_joins); + WRITE_BOOL_FIELD(consider_partitionwise_join); + WRITE_BITMAPSET_FIELD(top_parent_relids); + WRITE_BOOL_FIELD(partbounds_merged); + WRITE_BITMAPSET_FIELD(live_parts); + WRITE_BITMAPSET_FIELD(all_partrels); +} + +static void +_outIndexOptInfo(StringInfo str, const IndexOptInfo *node) +{ + WRITE_NODE_TYPE("INDEXOPTINFO"); + + /* NB: this isn't a complete set of fields */ + WRITE_OID_FIELD(indexoid); + /* Do NOT print rel field, else infinite recursion */ + WRITE_UINT_FIELD(pages); + WRITE_FLOAT_FIELD(tuples, "%.0f"); + WRITE_INT_FIELD(tree_height); + WRITE_INT_FIELD(ncolumns); + /* array fields aren't really worth the trouble to print */ + WRITE_OID_FIELD(relam); + /* indexprs is redundant since we print indextlist */ + WRITE_NODE_FIELD(indpred); + WRITE_NODE_FIELD(indextlist); + WRITE_NODE_FIELD(indrestrictinfo); + WRITE_BOOL_FIELD(predOK); + WRITE_BOOL_FIELD(unique); + WRITE_BOOL_FIELD(immediate); + WRITE_BOOL_FIELD(hypothetical); + /* we don't bother with fields copied from the index AM's API struct */ +} + +static void +_outForeignKeyOptInfo(StringInfo str, const ForeignKeyOptInfo *node) +{ + int i; + + WRITE_NODE_TYPE("FOREIGNKEYOPTINFO"); + + WRITE_UINT_FIELD(con_relid); + WRITE_UINT_FIELD(ref_relid); + WRITE_INT_FIELD(nkeys); + WRITE_ATTRNUMBER_ARRAY(conkey, node->nkeys); + WRITE_ATTRNUMBER_ARRAY(confkey, node->nkeys); + WRITE_OID_ARRAY(conpfeqop, node->nkeys); + WRITE_INT_FIELD(nmatched_ec); + WRITE_INT_FIELD(nconst_ec); + WRITE_INT_FIELD(nmatched_rcols); + WRITE_INT_FIELD(nmatched_ri); + /* for compactness, just print the number of matches per column: */ + appendStringInfoString(str, " :eclass"); + for (i = 0; i < node->nkeys; i++) + appendStringInfo(str, " %d", (node->eclass[i] != NULL)); + appendStringInfoString(str, " :rinfos"); + for (i = 0; i < node->nkeys; i++) + appendStringInfo(str, " %d", list_length(node->rinfos[i])); +} + +static void +_outStatisticExtInfo(StringInfo str, const StatisticExtInfo *node) +{ + WRITE_NODE_TYPE("STATISTICEXTINFO"); + + /* NB: this isn't a complete set of fields */ + WRITE_OID_FIELD(statOid); + /* don't write rel, leads to infinite recursion in plan tree dump */ + WRITE_CHAR_FIELD(kind); + WRITE_BITMAPSET_FIELD(keys); +} + +static void +_outEquivalenceClass(StringInfo str, const EquivalenceClass *node) +{ + /* + * To simplify reading, we just chase up to the topmost merged EC and + * print that, without bothering to show the merge-ees separately. + */ + while (node->ec_merged) + node = node->ec_merged; + + WRITE_NODE_TYPE("EQUIVALENCECLASS"); + + WRITE_NODE_FIELD(ec_opfamilies); + WRITE_OID_FIELD(ec_collation); + WRITE_NODE_FIELD(ec_members); + WRITE_NODE_FIELD(ec_sources); + WRITE_NODE_FIELD(ec_derives); + WRITE_BITMAPSET_FIELD(ec_relids); + WRITE_BOOL_FIELD(ec_has_const); + WRITE_BOOL_FIELD(ec_has_volatile); + WRITE_BOOL_FIELD(ec_below_outer_join); + WRITE_BOOL_FIELD(ec_broken); + WRITE_UINT_FIELD(ec_sortref); + WRITE_UINT_FIELD(ec_min_security); + WRITE_UINT_FIELD(ec_max_security); +} + +static void +_outEquivalenceMember(StringInfo str, const EquivalenceMember *node) +{ + WRITE_NODE_TYPE("EQUIVALENCEMEMBER"); + + WRITE_NODE_FIELD(em_expr); + WRITE_BITMAPSET_FIELD(em_relids); + WRITE_BITMAPSET_FIELD(em_nullable_relids); + WRITE_BOOL_FIELD(em_is_const); + WRITE_BOOL_FIELD(em_is_child); + WRITE_OID_FIELD(em_datatype); +} + +static void +_outPathKey(StringInfo str, const PathKey *node) +{ + WRITE_NODE_TYPE("PATHKEY"); + + WRITE_NODE_FIELD(pk_eclass); + WRITE_OID_FIELD(pk_opfamily); + WRITE_INT_FIELD(pk_strategy); + WRITE_BOOL_FIELD(pk_nulls_first); +} + +static void +_outPathTarget(StringInfo str, const PathTarget *node) +{ + WRITE_NODE_TYPE("PATHTARGET"); + + WRITE_NODE_FIELD(exprs); + WRITE_INDEX_ARRAY(sortgrouprefs, list_length(node->exprs)); + WRITE_FLOAT_FIELD(cost.startup, "%.2f"); + WRITE_FLOAT_FIELD(cost.per_tuple, "%.2f"); + WRITE_INT_FIELD(width); + WRITE_ENUM_FIELD(has_volatile_expr, VolatileFunctionStatus); +} + +static void +_outParamPathInfo(StringInfo str, const ParamPathInfo *node) +{ + WRITE_NODE_TYPE("PARAMPATHINFO"); + + WRITE_BITMAPSET_FIELD(ppi_req_outer); + WRITE_FLOAT_FIELD(ppi_rows, "%.0f"); + WRITE_NODE_FIELD(ppi_clauses); +} + +static void +_outRestrictInfo(StringInfo str, const RestrictInfo *node) +{ + WRITE_NODE_TYPE("RESTRICTINFO"); + + /* NB: this isn't a complete set of fields */ + WRITE_NODE_FIELD(clause); + WRITE_BOOL_FIELD(is_pushed_down); + WRITE_BOOL_FIELD(outerjoin_delayed); + WRITE_BOOL_FIELD(can_join); + WRITE_BOOL_FIELD(pseudoconstant); + WRITE_BOOL_FIELD(leakproof); + WRITE_ENUM_FIELD(has_volatile, VolatileFunctionStatus); + WRITE_UINT_FIELD(security_level); + WRITE_BITMAPSET_FIELD(clause_relids); + WRITE_BITMAPSET_FIELD(required_relids); + WRITE_BITMAPSET_FIELD(outer_relids); + WRITE_BITMAPSET_FIELD(nullable_relids); + WRITE_BITMAPSET_FIELD(left_relids); + WRITE_BITMAPSET_FIELD(right_relids); + WRITE_NODE_FIELD(orclause); + /* don't write parent_ec, leads to infinite recursion in plan tree dump */ + WRITE_FLOAT_FIELD(norm_selec, "%.4f"); + WRITE_FLOAT_FIELD(outer_selec, "%.4f"); + WRITE_NODE_FIELD(mergeopfamilies); + /* don't write left_ec, leads to infinite recursion in plan tree dump */ + /* don't write right_ec, leads to infinite recursion in plan tree dump */ + WRITE_NODE_FIELD(left_em); + WRITE_NODE_FIELD(right_em); + WRITE_BOOL_FIELD(outer_is_left); + WRITE_OID_FIELD(hashjoinoperator); + WRITE_OID_FIELD(left_hasheqoperator); + WRITE_OID_FIELD(right_hasheqoperator); +} + +static void +_outIndexClause(StringInfo str, const IndexClause *node) +{ + WRITE_NODE_TYPE("INDEXCLAUSE"); + + WRITE_NODE_FIELD(rinfo); + WRITE_NODE_FIELD(indexquals); + WRITE_BOOL_FIELD(lossy); + WRITE_INT_FIELD(indexcol); + WRITE_NODE_FIELD(indexcols); +} + +static void +_outPlaceHolderVar(StringInfo str, const PlaceHolderVar *node) +{ + WRITE_NODE_TYPE("PLACEHOLDERVAR"); + + WRITE_NODE_FIELD(phexpr); + WRITE_BITMAPSET_FIELD(phrels); + WRITE_UINT_FIELD(phid); + WRITE_UINT_FIELD(phlevelsup); +} + +static void +_outSpecialJoinInfo(StringInfo str, const SpecialJoinInfo *node) +{ + WRITE_NODE_TYPE("SPECIALJOININFO"); + + WRITE_BITMAPSET_FIELD(min_lefthand); + WRITE_BITMAPSET_FIELD(min_righthand); + WRITE_BITMAPSET_FIELD(syn_lefthand); + WRITE_BITMAPSET_FIELD(syn_righthand); + WRITE_ENUM_FIELD(jointype, JoinType); + WRITE_BOOL_FIELD(lhs_strict); + WRITE_BOOL_FIELD(delay_upper_joins); + WRITE_BOOL_FIELD(semi_can_btree); + WRITE_BOOL_FIELD(semi_can_hash); + WRITE_NODE_FIELD(semi_operators); + WRITE_NODE_FIELD(semi_rhs_exprs); +} + +static void +_outAppendRelInfo(StringInfo str, const AppendRelInfo *node) +{ + WRITE_NODE_TYPE("APPENDRELINFO"); + + WRITE_UINT_FIELD(parent_relid); + WRITE_UINT_FIELD(child_relid); + WRITE_OID_FIELD(parent_reltype); + WRITE_OID_FIELD(child_reltype); + WRITE_NODE_FIELD(translated_vars); + WRITE_INT_FIELD(num_child_cols); + WRITE_ATTRNUMBER_ARRAY(parent_colnos, node->num_child_cols); + WRITE_OID_FIELD(parent_reloid); +} + +static void +_outRowIdentityVarInfo(StringInfo str, const RowIdentityVarInfo *node) +{ + WRITE_NODE_TYPE("ROWIDENTITYVARINFO"); + + WRITE_NODE_FIELD(rowidvar); + WRITE_INT_FIELD(rowidwidth); + WRITE_STRING_FIELD(rowidname); + WRITE_BITMAPSET_FIELD(rowidrels); +} + +static void +_outPlaceHolderInfo(StringInfo str, const PlaceHolderInfo *node) +{ + WRITE_NODE_TYPE("PLACEHOLDERINFO"); + + WRITE_UINT_FIELD(phid); + WRITE_NODE_FIELD(ph_var); + WRITE_BITMAPSET_FIELD(ph_eval_at); + WRITE_BITMAPSET_FIELD(ph_lateral); + WRITE_BITMAPSET_FIELD(ph_needed); + WRITE_INT_FIELD(ph_width); +} + +static void +_outMinMaxAggInfo(StringInfo str, const MinMaxAggInfo *node) +{ + WRITE_NODE_TYPE("MINMAXAGGINFO"); + + WRITE_OID_FIELD(aggfnoid); + WRITE_OID_FIELD(aggsortop); + WRITE_NODE_FIELD(target); + /* We intentionally omit subroot --- too large, not interesting enough */ + WRITE_NODE_FIELD(path); + WRITE_FLOAT_FIELD(pathcost, "%.2f"); + WRITE_NODE_FIELD(param); +} + +static void +_outPlannerParamItem(StringInfo str, const PlannerParamItem *node) +{ + WRITE_NODE_TYPE("PLANNERPARAMITEM"); + + WRITE_NODE_FIELD(item); + WRITE_INT_FIELD(paramId); +} + +/***************************************************************************** + * + * Stuff from extensible.h + * + *****************************************************************************/ + +static void +_outExtensibleNode(StringInfo str, const ExtensibleNode *node) +{ + const ExtensibleNodeMethods *methods; + + methods = GetExtensibleNodeMethods(node->extnodename, false); + + WRITE_NODE_TYPE("EXTENSIBLENODE"); + + WRITE_STRING_FIELD(extnodename); + + /* serialize the private fields */ + methods->nodeOut(str, node); +} + +/***************************************************************************** + * + * Stuff from parsenodes.h. + * + *****************************************************************************/ + +/* + * print the basic stuff of all nodes that inherit from CreateStmt + */ +static void +_outCreateStmtInfo(StringInfo str, const CreateStmt *node) +{ + WRITE_NODE_FIELD(relation); + WRITE_NODE_FIELD(tableElts); + WRITE_NODE_FIELD(inhRelations); + WRITE_NODE_FIELD(partspec); + WRITE_NODE_FIELD(partbound); + WRITE_NODE_FIELD(ofTypename); + WRITE_NODE_FIELD(constraints); + WRITE_NODE_FIELD(options); + WRITE_ENUM_FIELD(oncommit, OnCommitAction); + WRITE_STRING_FIELD(tablespacename); + WRITE_STRING_FIELD(accessMethod); + WRITE_BOOL_FIELD(if_not_exists); +} + +static void +_outCreateStmt(StringInfo str, const CreateStmt *node) +{ + WRITE_NODE_TYPE("CREATESTMT"); + + _outCreateStmtInfo(str, (const CreateStmt *) node); +} + +static void +_outCreateForeignTableStmt(StringInfo str, const CreateForeignTableStmt *node) +{ + WRITE_NODE_TYPE("CREATEFOREIGNTABLESTMT"); + + _outCreateStmtInfo(str, (const CreateStmt *) node); + + WRITE_STRING_FIELD(servername); + WRITE_NODE_FIELD(options); +} + +static void +_outImportForeignSchemaStmt(StringInfo str, const ImportForeignSchemaStmt *node) +{ + WRITE_NODE_TYPE("IMPORTFOREIGNSCHEMASTMT"); + + WRITE_STRING_FIELD(server_name); + WRITE_STRING_FIELD(remote_schema); + WRITE_STRING_FIELD(local_schema); + WRITE_ENUM_FIELD(list_type, ImportForeignSchemaType); + WRITE_NODE_FIELD(table_list); + WRITE_NODE_FIELD(options); +} + +static void +_outIndexStmt(StringInfo str, const IndexStmt *node) +{ + WRITE_NODE_TYPE("INDEXSTMT"); + + WRITE_STRING_FIELD(idxname); + WRITE_NODE_FIELD(relation); + WRITE_STRING_FIELD(accessMethod); + WRITE_STRING_FIELD(tableSpace); + WRITE_NODE_FIELD(indexParams); + WRITE_NODE_FIELD(indexIncludingParams); + WRITE_NODE_FIELD(options); + WRITE_NODE_FIELD(whereClause); + WRITE_NODE_FIELD(excludeOpNames); + WRITE_STRING_FIELD(idxcomment); + WRITE_OID_FIELD(indexOid); + WRITE_OID_FIELD(oldNode); + WRITE_UINT_FIELD(oldCreateSubid); + WRITE_UINT_FIELD(oldFirstRelfilenodeSubid); + WRITE_BOOL_FIELD(unique); + WRITE_BOOL_FIELD(nulls_not_distinct); + WRITE_BOOL_FIELD(primary); + WRITE_BOOL_FIELD(isconstraint); + WRITE_BOOL_FIELD(deferrable); + WRITE_BOOL_FIELD(initdeferred); + WRITE_BOOL_FIELD(transformed); + WRITE_BOOL_FIELD(concurrent); + WRITE_BOOL_FIELD(if_not_exists); + WRITE_BOOL_FIELD(reset_default_tblspc); +} + +static void +_outCreateStatsStmt(StringInfo str, const CreateStatsStmt *node) +{ + WRITE_NODE_TYPE("CREATESTATSSTMT"); + + WRITE_NODE_FIELD(defnames); + WRITE_NODE_FIELD(stat_types); + WRITE_NODE_FIELD(exprs); + WRITE_NODE_FIELD(relations); + WRITE_STRING_FIELD(stxcomment); + WRITE_BOOL_FIELD(transformed); + WRITE_BOOL_FIELD(if_not_exists); +} + +static void +_outAlterStatsStmt(StringInfo str, const AlterStatsStmt *node) +{ + WRITE_NODE_TYPE("ALTERSTATSSTMT"); + + WRITE_NODE_FIELD(defnames); + WRITE_INT_FIELD(stxstattarget); + WRITE_BOOL_FIELD(missing_ok); +} + +static void +_outNotifyStmt(StringInfo str, const NotifyStmt *node) +{ + WRITE_NODE_TYPE("NOTIFYSTMT"); + + WRITE_STRING_FIELD(conditionname); + WRITE_STRING_FIELD(payload); +} + +static void +_outDeclareCursorStmt(StringInfo str, const DeclareCursorStmt *node) +{ + WRITE_NODE_TYPE("DECLARECURSORSTMT"); + + WRITE_STRING_FIELD(portalname); + WRITE_INT_FIELD(options); + WRITE_NODE_FIELD(query); +} + +static void +_outSelectStmt(StringInfo str, const SelectStmt *node) +{ + WRITE_NODE_TYPE("SELECT"); + + WRITE_NODE_FIELD(distinctClause); + WRITE_NODE_FIELD(intoClause); + WRITE_NODE_FIELD(targetList); + WRITE_NODE_FIELD(fromClause); + WRITE_NODE_FIELD(whereClause); + WRITE_NODE_FIELD(groupClause); + WRITE_BOOL_FIELD(groupDistinct); + WRITE_NODE_FIELD(havingClause); + WRITE_NODE_FIELD(windowClause); + WRITE_NODE_FIELD(valuesLists); + WRITE_NODE_FIELD(sortClause); + WRITE_NODE_FIELD(limitOffset); + WRITE_NODE_FIELD(limitCount); + WRITE_ENUM_FIELD(limitOption, LimitOption); + WRITE_NODE_FIELD(lockingClause); + WRITE_NODE_FIELD(withClause); + WRITE_ENUM_FIELD(op, SetOperation); + WRITE_BOOL_FIELD(all); + WRITE_NODE_FIELD(larg); + WRITE_NODE_FIELD(rarg); +} + +static void +_outReturnStmt(StringInfo str, const ReturnStmt *node) +{ + WRITE_NODE_TYPE("RETURN"); + + WRITE_NODE_FIELD(returnval); +} + +static void +_outPLAssignStmt(StringInfo str, const PLAssignStmt *node) +{ + WRITE_NODE_TYPE("PLASSIGN"); + + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(indirection); + WRITE_INT_FIELD(nnames); + WRITE_NODE_FIELD(val); + WRITE_LOCATION_FIELD(location); +} + +static void +_outFuncCall(StringInfo str, const FuncCall *node) +{ + WRITE_NODE_TYPE("FUNCCALL"); + + WRITE_NODE_FIELD(funcname); + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(agg_order); + WRITE_NODE_FIELD(agg_filter); + WRITE_NODE_FIELD(over); + WRITE_BOOL_FIELD(agg_within_group); + WRITE_BOOL_FIELD(agg_star); + WRITE_BOOL_FIELD(agg_distinct); + WRITE_BOOL_FIELD(func_variadic); + WRITE_ENUM_FIELD(funcformat, CoercionForm); + WRITE_LOCATION_FIELD(location); +} + +static void +_outDefElem(StringInfo str, const DefElem *node) +{ + WRITE_NODE_TYPE("DEFELEM"); + + WRITE_STRING_FIELD(defnamespace); + WRITE_STRING_FIELD(defname); + WRITE_NODE_FIELD(arg); + WRITE_ENUM_FIELD(defaction, DefElemAction); + WRITE_LOCATION_FIELD(location); +} + +static void +_outTableLikeClause(StringInfo str, const TableLikeClause *node) +{ + WRITE_NODE_TYPE("TABLELIKECLAUSE"); + + WRITE_NODE_FIELD(relation); + WRITE_UINT_FIELD(options); + WRITE_OID_FIELD(relationOid); +} + +static void +_outLockingClause(StringInfo str, const LockingClause *node) +{ + WRITE_NODE_TYPE("LOCKINGCLAUSE"); + + WRITE_NODE_FIELD(lockedRels); + WRITE_ENUM_FIELD(strength, LockClauseStrength); + WRITE_ENUM_FIELD(waitPolicy, LockWaitPolicy); +} + +static void +_outXmlSerialize(StringInfo str, const XmlSerialize *node) +{ + WRITE_NODE_TYPE("XMLSERIALIZE"); + + WRITE_ENUM_FIELD(xmloption, XmlOptionType); + WRITE_NODE_FIELD(expr); + WRITE_NODE_FIELD(typeName); + WRITE_LOCATION_FIELD(location); +} + +static void +_outTriggerTransition(StringInfo str, const TriggerTransition *node) +{ + WRITE_NODE_TYPE("TRIGGERTRANSITION"); + + WRITE_STRING_FIELD(name); + WRITE_BOOL_FIELD(isNew); + WRITE_BOOL_FIELD(isTable); +} + +static void +_outColumnDef(StringInfo str, const ColumnDef *node) +{ + WRITE_NODE_TYPE("COLUMNDEF"); + + WRITE_STRING_FIELD(colname); + WRITE_NODE_FIELD(typeName); + WRITE_STRING_FIELD(compression); + WRITE_INT_FIELD(inhcount); + WRITE_BOOL_FIELD(is_local); + WRITE_BOOL_FIELD(is_not_null); + WRITE_BOOL_FIELD(is_from_type); + WRITE_CHAR_FIELD(storage); + WRITE_NODE_FIELD(raw_default); + WRITE_NODE_FIELD(cooked_default); + WRITE_CHAR_FIELD(identity); + WRITE_NODE_FIELD(identitySequence); + WRITE_CHAR_FIELD(generated); + WRITE_NODE_FIELD(collClause); + WRITE_OID_FIELD(collOid); + WRITE_NODE_FIELD(constraints); + WRITE_NODE_FIELD(fdwoptions); + WRITE_LOCATION_FIELD(location); +} + +static void +_outTypeName(StringInfo str, const TypeName *node) +{ + WRITE_NODE_TYPE("TYPENAME"); + + WRITE_NODE_FIELD(names); + WRITE_OID_FIELD(typeOid); + WRITE_BOOL_FIELD(setof); + WRITE_BOOL_FIELD(pct_type); + WRITE_NODE_FIELD(typmods); + WRITE_INT_FIELD(typemod); + WRITE_NODE_FIELD(arrayBounds); + WRITE_LOCATION_FIELD(location); +} + +static void +_outTypeCast(StringInfo str, const TypeCast *node) +{ + WRITE_NODE_TYPE("TYPECAST"); + + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(typeName); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCollateClause(StringInfo str, const CollateClause *node) +{ + WRITE_NODE_TYPE("COLLATECLAUSE"); + + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(collname); + WRITE_LOCATION_FIELD(location); +} + +static void +_outIndexElem(StringInfo str, const IndexElem *node) +{ + WRITE_NODE_TYPE("INDEXELEM"); + + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(expr); + WRITE_STRING_FIELD(indexcolname); + WRITE_NODE_FIELD(collation); + WRITE_NODE_FIELD(opclass); + WRITE_NODE_FIELD(opclassopts); + WRITE_ENUM_FIELD(ordering, SortByDir); + WRITE_ENUM_FIELD(nulls_ordering, SortByNulls); +} + +static void +_outStatsElem(StringInfo str, const StatsElem *node) +{ + WRITE_NODE_TYPE("STATSELEM"); + + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(expr); +} + +static void +_outQuery(StringInfo str, const Query *node) +{ + WRITE_NODE_TYPE("QUERY"); + + WRITE_ENUM_FIELD(commandType, CmdType); + WRITE_ENUM_FIELD(querySource, QuerySource); + /* we intentionally do not print the queryId field */ + WRITE_BOOL_FIELD(canSetTag); + + /* + * Hack to work around missing outfuncs routines for a lot of the + * utility-statement node types. (The only one we actually *need* for + * rules support is NotifyStmt.) Someday we ought to support 'em all, but + * for the meantime do this to avoid getting lots of warnings when running + * with debug_print_parse on. + */ + if (node->utilityStmt) + { + switch (nodeTag(node->utilityStmt)) + { + case T_CreateStmt: + case T_IndexStmt: + case T_NotifyStmt: + case T_DeclareCursorStmt: + WRITE_NODE_FIELD(utilityStmt); + break; + default: + appendStringInfoString(str, " :utilityStmt ?"); + break; + } + } + else + appendStringInfoString(str, " :utilityStmt <>"); + + WRITE_INT_FIELD(resultRelation); + WRITE_BOOL_FIELD(hasAggs); + WRITE_BOOL_FIELD(hasWindowFuncs); + WRITE_BOOL_FIELD(hasTargetSRFs); + WRITE_BOOL_FIELD(hasSubLinks); + WRITE_BOOL_FIELD(hasDistinctOn); + WRITE_BOOL_FIELD(hasRecursive); + WRITE_BOOL_FIELD(hasModifyingCTE); + WRITE_BOOL_FIELD(hasForUpdate); + WRITE_BOOL_FIELD(hasRowSecurity); + WRITE_BOOL_FIELD(isReturn); + WRITE_NODE_FIELD(cteList); + WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(jointree); + WRITE_NODE_FIELD(targetList); + WRITE_ENUM_FIELD(override, OverridingKind); + WRITE_NODE_FIELD(onConflict); + WRITE_NODE_FIELD(returningList); + WRITE_NODE_FIELD(groupClause); + WRITE_BOOL_FIELD(groupDistinct); + WRITE_NODE_FIELD(groupingSets); + WRITE_NODE_FIELD(havingQual); + WRITE_NODE_FIELD(windowClause); + WRITE_NODE_FIELD(distinctClause); + WRITE_NODE_FIELD(sortClause); + WRITE_NODE_FIELD(limitOffset); + WRITE_NODE_FIELD(limitCount); + WRITE_ENUM_FIELD(limitOption, LimitOption); + WRITE_NODE_FIELD(rowMarks); + WRITE_NODE_FIELD(setOperations); + WRITE_NODE_FIELD(constraintDeps); + WRITE_NODE_FIELD(withCheckOptions); + WRITE_NODE_FIELD(mergeActionList); + WRITE_BOOL_FIELD(mergeUseOuterJoin); + WRITE_LOCATION_FIELD(stmt_location); + WRITE_INT_FIELD(stmt_len); +} + +static void +_outWithCheckOption(StringInfo str, const WithCheckOption *node) +{ + WRITE_NODE_TYPE("WITHCHECKOPTION"); + + WRITE_ENUM_FIELD(kind, WCOKind); + WRITE_STRING_FIELD(relname); + WRITE_STRING_FIELD(polname); + WRITE_NODE_FIELD(qual); + WRITE_BOOL_FIELD(cascaded); +} + +static void +_outSortGroupClause(StringInfo str, const SortGroupClause *node) +{ + WRITE_NODE_TYPE("SORTGROUPCLAUSE"); + + WRITE_UINT_FIELD(tleSortGroupRef); + WRITE_OID_FIELD(eqop); + WRITE_OID_FIELD(sortop); + WRITE_BOOL_FIELD(nulls_first); + WRITE_BOOL_FIELD(hashable); +} + +static void +_outGroupingSet(StringInfo str, const GroupingSet *node) +{ + WRITE_NODE_TYPE("GROUPINGSET"); + + WRITE_ENUM_FIELD(kind, GroupingSetKind); + WRITE_NODE_FIELD(content); + WRITE_LOCATION_FIELD(location); +} + +static void +_outWindowClause(StringInfo str, const WindowClause *node) +{ + WRITE_NODE_TYPE("WINDOWCLAUSE"); + + WRITE_STRING_FIELD(name); + WRITE_STRING_FIELD(refname); + WRITE_NODE_FIELD(partitionClause); + WRITE_NODE_FIELD(orderClause); + WRITE_INT_FIELD(frameOptions); + WRITE_NODE_FIELD(startOffset); + WRITE_NODE_FIELD(endOffset); + WRITE_NODE_FIELD(runCondition); + WRITE_OID_FIELD(startInRangeFunc); + WRITE_OID_FIELD(endInRangeFunc); + WRITE_OID_FIELD(inRangeColl); + WRITE_BOOL_FIELD(inRangeAsc); + WRITE_BOOL_FIELD(inRangeNullsFirst); + WRITE_UINT_FIELD(winref); + WRITE_BOOL_FIELD(copiedOrder); +} + +static void +_outRowMarkClause(StringInfo str, const RowMarkClause *node) +{ + WRITE_NODE_TYPE("ROWMARKCLAUSE"); + + WRITE_UINT_FIELD(rti); + WRITE_ENUM_FIELD(strength, LockClauseStrength); + WRITE_ENUM_FIELD(waitPolicy, LockWaitPolicy); + WRITE_BOOL_FIELD(pushedDown); +} + +static void +_outWithClause(StringInfo str, const WithClause *node) +{ + WRITE_NODE_TYPE("WITHCLAUSE"); + + WRITE_NODE_FIELD(ctes); + WRITE_BOOL_FIELD(recursive); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCTESearchClause(StringInfo str, const CTESearchClause *node) +{ + WRITE_NODE_TYPE("CTESEARCHCLAUSE"); + + WRITE_NODE_FIELD(search_col_list); + WRITE_BOOL_FIELD(search_breadth_first); + WRITE_STRING_FIELD(search_seq_column); + WRITE_LOCATION_FIELD(location); +} + +static void +_outCTECycleClause(StringInfo str, const CTECycleClause *node) +{ + WRITE_NODE_TYPE("CTECYCLECLAUSE"); + + WRITE_NODE_FIELD(cycle_col_list); + WRITE_STRING_FIELD(cycle_mark_column); + WRITE_NODE_FIELD(cycle_mark_value); + WRITE_NODE_FIELD(cycle_mark_default); + WRITE_STRING_FIELD(cycle_path_column); + WRITE_LOCATION_FIELD(location); + WRITE_OID_FIELD(cycle_mark_type); + WRITE_INT_FIELD(cycle_mark_typmod); + WRITE_OID_FIELD(cycle_mark_collation); + WRITE_OID_FIELD(cycle_mark_neop); +} + +static void +_outCommonTableExpr(StringInfo str, const CommonTableExpr *node) +{ + WRITE_NODE_TYPE("COMMONTABLEEXPR"); + + WRITE_STRING_FIELD(ctename); + WRITE_NODE_FIELD(aliascolnames); + WRITE_ENUM_FIELD(ctematerialized, CTEMaterialize); + WRITE_NODE_FIELD(ctequery); + WRITE_NODE_FIELD(search_clause); + WRITE_NODE_FIELD(cycle_clause); + WRITE_LOCATION_FIELD(location); + WRITE_BOOL_FIELD(cterecursive); + WRITE_INT_FIELD(cterefcount); + WRITE_NODE_FIELD(ctecolnames); + WRITE_NODE_FIELD(ctecoltypes); + WRITE_NODE_FIELD(ctecoltypmods); + WRITE_NODE_FIELD(ctecolcollations); +} + +static void +_outMergeWhenClause(StringInfo str, const MergeWhenClause *node) +{ + WRITE_NODE_TYPE("MERGEWHENCLAUSE"); + + WRITE_BOOL_FIELD(matched); + WRITE_ENUM_FIELD(commandType, CmdType); + WRITE_ENUM_FIELD(override, OverridingKind); + WRITE_NODE_FIELD(condition); + WRITE_NODE_FIELD(targetList); + WRITE_NODE_FIELD(values); +} + +static void +_outMergeAction(StringInfo str, const MergeAction *node) +{ + WRITE_NODE_TYPE("MERGEACTION"); + + WRITE_BOOL_FIELD(matched); + WRITE_ENUM_FIELD(commandType, CmdType); + WRITE_ENUM_FIELD(override, OverridingKind); + WRITE_NODE_FIELD(qual); + WRITE_NODE_FIELD(targetList); + WRITE_NODE_FIELD(updateColnos); +} + +static void +_outSetOperationStmt(StringInfo str, const SetOperationStmt *node) +{ + WRITE_NODE_TYPE("SETOPERATIONSTMT"); + + WRITE_ENUM_FIELD(op, SetOperation); + WRITE_BOOL_FIELD(all); + WRITE_NODE_FIELD(larg); + WRITE_NODE_FIELD(rarg); + WRITE_NODE_FIELD(colTypes); + WRITE_NODE_FIELD(colTypmods); + WRITE_NODE_FIELD(colCollations); + WRITE_NODE_FIELD(groupClauses); +} + +static void +_outRangeTblEntry(StringInfo str, const RangeTblEntry *node) +{ + WRITE_NODE_TYPE("RANGETBLENTRY"); + + /* put alias + eref first to make dump more legible */ + WRITE_NODE_FIELD(alias); + WRITE_NODE_FIELD(eref); + WRITE_ENUM_FIELD(rtekind, RTEKind); + + switch (node->rtekind) + { + case RTE_RELATION: + WRITE_OID_FIELD(relid); + WRITE_CHAR_FIELD(relkind); + WRITE_INT_FIELD(rellockmode); + WRITE_NODE_FIELD(tablesample); + break; + case RTE_SUBQUERY: + WRITE_NODE_FIELD(subquery); + WRITE_BOOL_FIELD(security_barrier); + break; + case RTE_JOIN: + WRITE_ENUM_FIELD(jointype, JoinType); + WRITE_INT_FIELD(joinmergedcols); + WRITE_NODE_FIELD(joinaliasvars); + WRITE_NODE_FIELD(joinleftcols); + WRITE_NODE_FIELD(joinrightcols); + WRITE_NODE_FIELD(join_using_alias); + break; + case RTE_FUNCTION: + WRITE_NODE_FIELD(functions); + WRITE_BOOL_FIELD(funcordinality); + break; + case RTE_TABLEFUNC: + WRITE_NODE_FIELD(tablefunc); + break; + case RTE_VALUES: + WRITE_NODE_FIELD(values_lists); + WRITE_NODE_FIELD(coltypes); + WRITE_NODE_FIELD(coltypmods); + WRITE_NODE_FIELD(colcollations); + break; + case RTE_CTE: + WRITE_STRING_FIELD(ctename); + WRITE_UINT_FIELD(ctelevelsup); + WRITE_BOOL_FIELD(self_reference); + WRITE_NODE_FIELD(coltypes); + WRITE_NODE_FIELD(coltypmods); + WRITE_NODE_FIELD(colcollations); + break; + case RTE_NAMEDTUPLESTORE: + WRITE_STRING_FIELD(enrname); + WRITE_FLOAT_FIELD(enrtuples, "%.0f"); + WRITE_OID_FIELD(relid); + WRITE_NODE_FIELD(coltypes); + WRITE_NODE_FIELD(coltypmods); + WRITE_NODE_FIELD(colcollations); + break; + case RTE_RESULT: + /* no extra fields */ + break; + default: + elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind); + break; + } + + WRITE_BOOL_FIELD(lateral); + WRITE_BOOL_FIELD(inh); + WRITE_BOOL_FIELD(inFromCl); + WRITE_UINT_FIELD(requiredPerms); + WRITE_OID_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); + WRITE_NODE_FIELD(securityQuals); +} + +static void +_outRangeTblFunction(StringInfo str, const RangeTblFunction *node) +{ + WRITE_NODE_TYPE("RANGETBLFUNCTION"); + + WRITE_NODE_FIELD(funcexpr); + WRITE_INT_FIELD(funccolcount); + WRITE_NODE_FIELD(funccolnames); + WRITE_NODE_FIELD(funccoltypes); + WRITE_NODE_FIELD(funccoltypmods); + WRITE_NODE_FIELD(funccolcollations); + WRITE_BITMAPSET_FIELD(funcparams); +} + +static void +_outTableSampleClause(StringInfo str, const TableSampleClause *node) +{ + WRITE_NODE_TYPE("TABLESAMPLECLAUSE"); + + WRITE_OID_FIELD(tsmhandler); + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(repeatable); +} + +static void +_outA_Expr(StringInfo str, const A_Expr *node) +{ + WRITE_NODE_TYPE("AEXPR"); + + switch (node->kind) + { + case AEXPR_OP: + appendStringInfoChar(str, ' '); + WRITE_NODE_FIELD(name); + break; + case AEXPR_OP_ANY: + appendStringInfoChar(str, ' '); + WRITE_NODE_FIELD(name); + appendStringInfoString(str, " ANY "); + break; + case AEXPR_OP_ALL: + appendStringInfoChar(str, ' '); + WRITE_NODE_FIELD(name); + appendStringInfoString(str, " ALL "); + break; + case AEXPR_DISTINCT: + appendStringInfoString(str, " DISTINCT "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_NOT_DISTINCT: + appendStringInfoString(str, " NOT_DISTINCT "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_NULLIF: + appendStringInfoString(str, " NULLIF "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_IN: + appendStringInfoString(str, " IN "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_LIKE: + appendStringInfoString(str, " LIKE "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_ILIKE: + appendStringInfoString(str, " ILIKE "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_SIMILAR: + appendStringInfoString(str, " SIMILAR "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_BETWEEN: + appendStringInfoString(str, " BETWEEN "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_NOT_BETWEEN: + appendStringInfoString(str, " NOT_BETWEEN "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_BETWEEN_SYM: + appendStringInfoString(str, " BETWEEN_SYM "); + WRITE_NODE_FIELD(name); + break; + case AEXPR_NOT_BETWEEN_SYM: + appendStringInfoString(str, " NOT_BETWEEN_SYM "); + WRITE_NODE_FIELD(name); + break; + default: + appendStringInfoString(str, " ??"); + break; + } + + WRITE_NODE_FIELD(lexpr); + WRITE_NODE_FIELD(rexpr); + WRITE_LOCATION_FIELD(location); +} + +static void +_outInteger(StringInfo str, const Integer *node) +{ + appendStringInfo(str, "%d", node->ival); +} + +static void +_outFloat(StringInfo str, const Float *node) +{ + /* + * We assume the value is a valid numeric literal and so does not need + * quoting. + */ + appendStringInfoString(str, node->fval); +} + +static void +_outBoolean(StringInfo str, const Boolean *node) +{ + appendStringInfoString(str, node->boolval ? "true" : "false"); +} + +static void +_outString(StringInfo str, const String *node) +{ + /* + * We use outToken to provide escaping of the string's content, but we + * don't want it to do anything with an empty string. + */ + appendStringInfoChar(str, '"'); + if (node->sval[0] != '\0') + outToken(str, node->sval); + appendStringInfoChar(str, '"'); +} + +static void +_outBitString(StringInfo str, const BitString *node) +{ + /* internal representation already has leading 'b' */ + appendStringInfoString(str, node->bsval); +} + +static void +_outColumnRef(StringInfo str, const ColumnRef *node) +{ + WRITE_NODE_TYPE("COLUMNREF"); + + WRITE_NODE_FIELD(fields); + WRITE_LOCATION_FIELD(location); +} + +static void +_outParamRef(StringInfo str, const ParamRef *node) +{ + WRITE_NODE_TYPE("PARAMREF"); + + WRITE_INT_FIELD(number); + WRITE_LOCATION_FIELD(location); +} + +/* + * Node types found in raw parse trees (supported for debug purposes) + */ + +static void +_outRawStmt(StringInfo str, const RawStmt *node) +{ + WRITE_NODE_TYPE("RAWSTMT"); + + WRITE_NODE_FIELD(stmt); + WRITE_LOCATION_FIELD(stmt_location); + WRITE_INT_FIELD(stmt_len); +} + +static void +_outA_Const(StringInfo str, const A_Const *node) +{ + WRITE_NODE_TYPE("A_CONST"); + + if (node->isnull) + appendStringInfoString(str, " NULL"); + else + { + appendStringInfoString(str, " :val "); + outNode(str, &node->val); + } + WRITE_LOCATION_FIELD(location); +} + +static void +_outA_Star(StringInfo str, const A_Star *node) +{ + WRITE_NODE_TYPE("A_STAR"); +} + +static void +_outA_Indices(StringInfo str, const A_Indices *node) +{ + WRITE_NODE_TYPE("A_INDICES"); + + WRITE_BOOL_FIELD(is_slice); + WRITE_NODE_FIELD(lidx); + WRITE_NODE_FIELD(uidx); +} + +static void +_outA_Indirection(StringInfo str, const A_Indirection *node) +{ + WRITE_NODE_TYPE("A_INDIRECTION"); + + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(indirection); +} + +static void +_outA_ArrayExpr(StringInfo str, const A_ArrayExpr *node) +{ + WRITE_NODE_TYPE("A_ARRAYEXPR"); + + WRITE_NODE_FIELD(elements); + WRITE_LOCATION_FIELD(location); +} + +static void +_outResTarget(StringInfo str, const ResTarget *node) +{ + WRITE_NODE_TYPE("RESTARGET"); + + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(indirection); + WRITE_NODE_FIELD(val); + WRITE_LOCATION_FIELD(location); +} + +static void +_outMultiAssignRef(StringInfo str, const MultiAssignRef *node) +{ + WRITE_NODE_TYPE("MULTIASSIGNREF"); + + WRITE_NODE_FIELD(source); + WRITE_INT_FIELD(colno); + WRITE_INT_FIELD(ncolumns); +} + +static void +_outSortBy(StringInfo str, const SortBy *node) +{ + WRITE_NODE_TYPE("SORTBY"); + + WRITE_NODE_FIELD(node); + WRITE_ENUM_FIELD(sortby_dir, SortByDir); + WRITE_ENUM_FIELD(sortby_nulls, SortByNulls); + WRITE_NODE_FIELD(useOp); + WRITE_LOCATION_FIELD(location); +} + +static void +_outWindowDef(StringInfo str, const WindowDef *node) +{ + WRITE_NODE_TYPE("WINDOWDEF"); + + WRITE_STRING_FIELD(name); + WRITE_STRING_FIELD(refname); + WRITE_NODE_FIELD(partitionClause); + WRITE_NODE_FIELD(orderClause); + WRITE_INT_FIELD(frameOptions); + WRITE_NODE_FIELD(startOffset); + WRITE_NODE_FIELD(endOffset); + WRITE_LOCATION_FIELD(location); +} + +static void +_outRangeSubselect(StringInfo str, const RangeSubselect *node) +{ + WRITE_NODE_TYPE("RANGESUBSELECT"); + + WRITE_BOOL_FIELD(lateral); + WRITE_NODE_FIELD(subquery); + WRITE_NODE_FIELD(alias); +} + +static void +_outRangeFunction(StringInfo str, const RangeFunction *node) +{ + WRITE_NODE_TYPE("RANGEFUNCTION"); + + WRITE_BOOL_FIELD(lateral); + WRITE_BOOL_FIELD(ordinality); + WRITE_BOOL_FIELD(is_rowsfrom); + WRITE_NODE_FIELD(functions); + WRITE_NODE_FIELD(alias); + WRITE_NODE_FIELD(coldeflist); +} + +static void +_outRangeTableSample(StringInfo str, const RangeTableSample *node) +{ + WRITE_NODE_TYPE("RANGETABLESAMPLE"); + + WRITE_NODE_FIELD(relation); + WRITE_NODE_FIELD(method); + WRITE_NODE_FIELD(args); + WRITE_NODE_FIELD(repeatable); + WRITE_LOCATION_FIELD(location); +} + +static void +_outRangeTableFunc(StringInfo str, const RangeTableFunc *node) +{ + WRITE_NODE_TYPE("RANGETABLEFUNC"); + + WRITE_BOOL_FIELD(lateral); + WRITE_NODE_FIELD(docexpr); + WRITE_NODE_FIELD(rowexpr); + WRITE_NODE_FIELD(namespaces); + WRITE_NODE_FIELD(columns); + WRITE_NODE_FIELD(alias); + WRITE_LOCATION_FIELD(location); +} + +static void +_outRangeTableFuncCol(StringInfo str, const RangeTableFuncCol *node) +{ + WRITE_NODE_TYPE("RANGETABLEFUNCCOL"); + + WRITE_STRING_FIELD(colname); + WRITE_NODE_FIELD(typeName); + WRITE_BOOL_FIELD(for_ordinality); + WRITE_BOOL_FIELD(is_not_null); + WRITE_NODE_FIELD(colexpr); + WRITE_NODE_FIELD(coldefexpr); + WRITE_LOCATION_FIELD(location); +} + +static void +_outConstraint(StringInfo str, const Constraint *node) +{ + WRITE_NODE_TYPE("CONSTRAINT"); + + WRITE_STRING_FIELD(conname); + WRITE_BOOL_FIELD(deferrable); + WRITE_BOOL_FIELD(initdeferred); + WRITE_LOCATION_FIELD(location); + + appendStringInfoString(str, " :contype "); + switch (node->contype) + { + case CONSTR_NULL: + appendStringInfoString(str, "NULL"); + break; + + case CONSTR_NOTNULL: + appendStringInfoString(str, "NOT_NULL"); + break; + + case CONSTR_DEFAULT: + appendStringInfoString(str, "DEFAULT"); + WRITE_NODE_FIELD(raw_expr); + WRITE_STRING_FIELD(cooked_expr); + break; + + case CONSTR_IDENTITY: + appendStringInfoString(str, "IDENTITY"); + WRITE_NODE_FIELD(options); + WRITE_CHAR_FIELD(generated_when); + break; + + case CONSTR_GENERATED: + appendStringInfoString(str, "GENERATED"); + WRITE_NODE_FIELD(raw_expr); + WRITE_STRING_FIELD(cooked_expr); + WRITE_CHAR_FIELD(generated_when); + break; + + case CONSTR_CHECK: + appendStringInfoString(str, "CHECK"); + WRITE_BOOL_FIELD(is_no_inherit); + WRITE_NODE_FIELD(raw_expr); + WRITE_STRING_FIELD(cooked_expr); + WRITE_BOOL_FIELD(skip_validation); + WRITE_BOOL_FIELD(initially_valid); + break; + + case CONSTR_PRIMARY: + appendStringInfoString(str, "PRIMARY_KEY"); + WRITE_NODE_FIELD(keys); + WRITE_NODE_FIELD(including); + WRITE_NODE_FIELD(options); + WRITE_STRING_FIELD(indexname); + WRITE_STRING_FIELD(indexspace); + WRITE_BOOL_FIELD(reset_default_tblspc); + /* access_method and where_clause not currently used */ + break; + + case CONSTR_UNIQUE: + appendStringInfoString(str, "UNIQUE"); + WRITE_BOOL_FIELD(nulls_not_distinct); + WRITE_NODE_FIELD(keys); + WRITE_NODE_FIELD(including); + WRITE_NODE_FIELD(options); + WRITE_STRING_FIELD(indexname); + WRITE_STRING_FIELD(indexspace); + WRITE_BOOL_FIELD(reset_default_tblspc); + /* access_method and where_clause not currently used */ + break; + + case CONSTR_EXCLUSION: + appendStringInfoString(str, "EXCLUSION"); + WRITE_NODE_FIELD(exclusions); + WRITE_NODE_FIELD(including); + WRITE_NODE_FIELD(options); + WRITE_STRING_FIELD(indexname); + WRITE_STRING_FIELD(indexspace); + WRITE_BOOL_FIELD(reset_default_tblspc); + WRITE_STRING_FIELD(access_method); + WRITE_NODE_FIELD(where_clause); + break; + + case CONSTR_FOREIGN: + appendStringInfoString(str, "FOREIGN_KEY"); + WRITE_NODE_FIELD(pktable); + WRITE_NODE_FIELD(fk_attrs); + WRITE_NODE_FIELD(pk_attrs); + WRITE_CHAR_FIELD(fk_matchtype); + WRITE_CHAR_FIELD(fk_upd_action); + WRITE_CHAR_FIELD(fk_del_action); + WRITE_NODE_FIELD(fk_del_set_cols); + WRITE_NODE_FIELD(old_conpfeqop); + WRITE_OID_FIELD(old_pktable_oid); + WRITE_BOOL_FIELD(skip_validation); + WRITE_BOOL_FIELD(initially_valid); + break; + + case CONSTR_ATTR_DEFERRABLE: + appendStringInfoString(str, "ATTR_DEFERRABLE"); + break; + + case CONSTR_ATTR_NOT_DEFERRABLE: + appendStringInfoString(str, "ATTR_NOT_DEFERRABLE"); + break; + + case CONSTR_ATTR_DEFERRED: + appendStringInfoString(str, "ATTR_DEFERRED"); + break; + + case CONSTR_ATTR_IMMEDIATE: + appendStringInfoString(str, "ATTR_IMMEDIATE"); + break; + + default: + appendStringInfo(str, "<unrecognized_constraint %d>", + (int) node->contype); + break; + } +} + +static void +_outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node) +{ + WRITE_NODE_TYPE("FOREIGNKEYCACHEINFO"); + + WRITE_OID_FIELD(conoid); + WRITE_OID_FIELD(conrelid); + WRITE_OID_FIELD(confrelid); + WRITE_INT_FIELD(nkeys); + WRITE_ATTRNUMBER_ARRAY(conkey, node->nkeys); + WRITE_ATTRNUMBER_ARRAY(confkey, node->nkeys); + WRITE_OID_ARRAY(conpfeqop, node->nkeys); +} + +static void +_outPartitionElem(StringInfo str, const PartitionElem *node) +{ + WRITE_NODE_TYPE("PARTITIONELEM"); + + WRITE_STRING_FIELD(name); + WRITE_NODE_FIELD(expr); + WRITE_NODE_FIELD(collation); + WRITE_NODE_FIELD(opclass); + WRITE_LOCATION_FIELD(location); +} + +static void +_outPartitionSpec(StringInfo str, const PartitionSpec *node) +{ + WRITE_NODE_TYPE("PARTITIONSPEC"); + + WRITE_STRING_FIELD(strategy); + WRITE_NODE_FIELD(partParams); + WRITE_LOCATION_FIELD(location); +} + +static void +_outPartitionBoundSpec(StringInfo str, const PartitionBoundSpec *node) +{ + WRITE_NODE_TYPE("PARTITIONBOUNDSPEC"); + + WRITE_CHAR_FIELD(strategy); + WRITE_BOOL_FIELD(is_default); + WRITE_INT_FIELD(modulus); + WRITE_INT_FIELD(remainder); + WRITE_NODE_FIELD(listdatums); + WRITE_NODE_FIELD(lowerdatums); + WRITE_NODE_FIELD(upperdatums); + WRITE_LOCATION_FIELD(location); +} + +static void +_outPartitionRangeDatum(StringInfo str, const PartitionRangeDatum *node) +{ + WRITE_NODE_TYPE("PARTITIONRANGEDATUM"); + + WRITE_ENUM_FIELD(kind, PartitionRangeDatumKind); + WRITE_NODE_FIELD(value); + WRITE_LOCATION_FIELD(location); +} + +/* + * outNode - + * converts a Node into ascii string and append it to 'str' + */ +void +outNode(StringInfo str, const void *obj) +{ + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + if (obj == NULL) + appendStringInfoString(str, "<>"); + else if (IsA(obj, List) || IsA(obj, IntList) || IsA(obj, OidList)) + _outList(str, obj); + /* nodeRead does not want to see { } around these! */ + else if (IsA(obj, Integer)) + _outInteger(str, (Integer *) obj); + else if (IsA(obj, Float)) + _outFloat(str, (Float *) obj); + else if (IsA(obj, Boolean)) + _outBoolean(str, (Boolean *) obj); + else if (IsA(obj, String)) + _outString(str, (String *) obj); + else if (IsA(obj, BitString)) + _outBitString(str, (BitString *) obj); + else + { + appendStringInfoChar(str, '{'); + switch (nodeTag(obj)) + { + case T_PlannedStmt: + _outPlannedStmt(str, obj); + break; + case T_Plan: + _outPlan(str, obj); + break; + case T_Result: + _outResult(str, obj); + break; + case T_ProjectSet: + _outProjectSet(str, obj); + break; + case T_ModifyTable: + _outModifyTable(str, obj); + break; + case T_Append: + _outAppend(str, obj); + break; + case T_MergeAppend: + _outMergeAppend(str, obj); + break; + case T_RecursiveUnion: + _outRecursiveUnion(str, obj); + break; + case T_BitmapAnd: + _outBitmapAnd(str, obj); + break; + case T_BitmapOr: + _outBitmapOr(str, obj); + break; + case T_Gather: + _outGather(str, obj); + break; + case T_GatherMerge: + _outGatherMerge(str, obj); + break; + case T_Scan: + _outScan(str, obj); + break; + case T_SeqScan: + _outSeqScan(str, obj); + break; + case T_SampleScan: + _outSampleScan(str, obj); + break; + case T_IndexScan: + _outIndexScan(str, obj); + break; + case T_IndexOnlyScan: + _outIndexOnlyScan(str, obj); + break; + case T_BitmapIndexScan: + _outBitmapIndexScan(str, obj); + break; + case T_BitmapHeapScan: + _outBitmapHeapScan(str, obj); + break; + case T_TidScan: + _outTidScan(str, obj); + break; + case T_TidRangeScan: + _outTidRangeScan(str, obj); + break; + case T_SubqueryScan: + _outSubqueryScan(str, obj); + break; + case T_FunctionScan: + _outFunctionScan(str, obj); + break; + case T_TableFuncScan: + _outTableFuncScan(str, obj); + break; + case T_ValuesScan: + _outValuesScan(str, obj); + break; + case T_CteScan: + _outCteScan(str, obj); + break; + case T_NamedTuplestoreScan: + _outNamedTuplestoreScan(str, obj); + break; + case T_WorkTableScan: + _outWorkTableScan(str, obj); + break; + case T_ForeignScan: + _outForeignScan(str, obj); + break; + case T_CustomScan: + _outCustomScan(str, obj); + break; + case T_Join: + _outJoin(str, obj); + break; + case T_NestLoop: + _outNestLoop(str, obj); + break; + case T_MergeJoin: + _outMergeJoin(str, obj); + break; + case T_HashJoin: + _outHashJoin(str, obj); + break; + case T_Agg: + _outAgg(str, obj); + break; + case T_WindowAgg: + _outWindowAgg(str, obj); + break; + case T_Group: + _outGroup(str, obj); + break; + case T_Material: + _outMaterial(str, obj); + break; + case T_Memoize: + _outMemoize(str, obj); + break; + case T_Sort: + _outSort(str, obj); + break; + case T_IncrementalSort: + _outIncrementalSort(str, obj); + break; + case T_Unique: + _outUnique(str, obj); + break; + case T_Hash: + _outHash(str, obj); + break; + case T_SetOp: + _outSetOp(str, obj); + break; + case T_LockRows: + _outLockRows(str, obj); + break; + case T_Limit: + _outLimit(str, obj); + break; + case T_NestLoopParam: + _outNestLoopParam(str, obj); + break; + case T_PlanRowMark: + _outPlanRowMark(str, obj); + break; + case T_PartitionPruneInfo: + _outPartitionPruneInfo(str, obj); + break; + case T_PartitionedRelPruneInfo: + _outPartitionedRelPruneInfo(str, obj); + break; + case T_PartitionPruneStepOp: + _outPartitionPruneStepOp(str, obj); + break; + case T_PartitionPruneStepCombine: + _outPartitionPruneStepCombine(str, obj); + break; + case T_PlanInvalItem: + _outPlanInvalItem(str, obj); + break; + case T_Alias: + _outAlias(str, obj); + break; + case T_RangeVar: + _outRangeVar(str, obj); + break; + case T_TableFunc: + _outTableFunc(str, obj); + break; + case T_IntoClause: + _outIntoClause(str, obj); + break; + case T_Var: + _outVar(str, obj); + break; + case T_Const: + _outConst(str, obj); + break; + case T_Param: + _outParam(str, obj); + break; + case T_Aggref: + _outAggref(str, obj); + break; + case T_GroupingFunc: + _outGroupingFunc(str, obj); + break; + case T_WindowFunc: + _outWindowFunc(str, obj); + break; + case T_SubscriptingRef: + _outSubscriptingRef(str, obj); + break; + case T_FuncExpr: + _outFuncExpr(str, obj); + break; + case T_NamedArgExpr: + _outNamedArgExpr(str, obj); + break; + case T_OpExpr: + _outOpExpr(str, obj); + break; + case T_DistinctExpr: + _outDistinctExpr(str, obj); + break; + case T_NullIfExpr: + _outNullIfExpr(str, obj); + break; + case T_ScalarArrayOpExpr: + _outScalarArrayOpExpr(str, obj); + break; + case T_BoolExpr: + _outBoolExpr(str, obj); + break; + case T_SubLink: + _outSubLink(str, obj); + break; + case T_SubPlan: + _outSubPlan(str, obj); + break; + case T_AlternativeSubPlan: + _outAlternativeSubPlan(str, obj); + break; + case T_FieldSelect: + _outFieldSelect(str, obj); + break; + case T_FieldStore: + _outFieldStore(str, obj); + break; + case T_RelabelType: + _outRelabelType(str, obj); + break; + case T_CoerceViaIO: + _outCoerceViaIO(str, obj); + break; + case T_ArrayCoerceExpr: + _outArrayCoerceExpr(str, obj); + break; + case T_ConvertRowtypeExpr: + _outConvertRowtypeExpr(str, obj); + break; + case T_CollateExpr: + _outCollateExpr(str, obj); + break; + case T_CaseExpr: + _outCaseExpr(str, obj); + break; + case T_CaseWhen: + _outCaseWhen(str, obj); + break; + case T_CaseTestExpr: + _outCaseTestExpr(str, obj); + break; + case T_ArrayExpr: + _outArrayExpr(str, obj); + break; + case T_RowExpr: + _outRowExpr(str, obj); + break; + case T_RowCompareExpr: + _outRowCompareExpr(str, obj); + break; + case T_CoalesceExpr: + _outCoalesceExpr(str, obj); + break; + case T_MinMaxExpr: + _outMinMaxExpr(str, obj); + break; + case T_SQLValueFunction: + _outSQLValueFunction(str, obj); + break; + case T_XmlExpr: + _outXmlExpr(str, obj); + break; + case T_NullTest: + _outNullTest(str, obj); + break; + case T_BooleanTest: + _outBooleanTest(str, obj); + break; + case T_CoerceToDomain: + _outCoerceToDomain(str, obj); + break; + case T_CoerceToDomainValue: + _outCoerceToDomainValue(str, obj); + break; + case T_SetToDefault: + _outSetToDefault(str, obj); + break; + case T_CurrentOfExpr: + _outCurrentOfExpr(str, obj); + break; + case T_NextValueExpr: + _outNextValueExpr(str, obj); + break; + case T_InferenceElem: + _outInferenceElem(str, obj); + break; + case T_TargetEntry: + _outTargetEntry(str, obj); + break; + case T_RangeTblRef: + _outRangeTblRef(str, obj); + break; + case T_JoinExpr: + _outJoinExpr(str, obj); + break; + case T_FromExpr: + _outFromExpr(str, obj); + break; + case T_OnConflictExpr: + _outOnConflictExpr(str, obj); + break; + case T_Path: + _outPath(str, obj); + break; + case T_IndexPath: + _outIndexPath(str, obj); + break; + case T_BitmapHeapPath: + _outBitmapHeapPath(str, obj); + break; + case T_BitmapAndPath: + _outBitmapAndPath(str, obj); + break; + case T_BitmapOrPath: + _outBitmapOrPath(str, obj); + break; + case T_TidPath: + _outTidPath(str, obj); + break; + case T_TidRangePath: + _outTidRangePath(str, obj); + break; + case T_SubqueryScanPath: + _outSubqueryScanPath(str, obj); + break; + case T_ForeignPath: + _outForeignPath(str, obj); + break; + case T_CustomPath: + _outCustomPath(str, obj); + break; + case T_AppendPath: + _outAppendPath(str, obj); + break; + case T_MergeAppendPath: + _outMergeAppendPath(str, obj); + break; + case T_GroupResultPath: + _outGroupResultPath(str, obj); + break; + case T_MaterialPath: + _outMaterialPath(str, obj); + break; + case T_MemoizePath: + _outMemoizePath(str, obj); + break; + case T_UniquePath: + _outUniquePath(str, obj); + break; + case T_GatherPath: + _outGatherPath(str, obj); + break; + case T_ProjectionPath: + _outProjectionPath(str, obj); + break; + case T_ProjectSetPath: + _outProjectSetPath(str, obj); + break; + case T_SortPath: + _outSortPath(str, obj); + break; + case T_IncrementalSortPath: + _outIncrementalSortPath(str, obj); + break; + case T_GroupPath: + _outGroupPath(str, obj); + break; + case T_UpperUniquePath: + _outUpperUniquePath(str, obj); + break; + case T_AggPath: + _outAggPath(str, obj); + break; + case T_GroupingSetsPath: + _outGroupingSetsPath(str, obj); + break; + case T_MinMaxAggPath: + _outMinMaxAggPath(str, obj); + break; + case T_WindowAggPath: + _outWindowAggPath(str, obj); + break; + case T_SetOpPath: + _outSetOpPath(str, obj); + break; + case T_RecursiveUnionPath: + _outRecursiveUnionPath(str, obj); + break; + case T_LockRowsPath: + _outLockRowsPath(str, obj); + break; + case T_ModifyTablePath: + _outModifyTablePath(str, obj); + break; + case T_LimitPath: + _outLimitPath(str, obj); + break; + case T_GatherMergePath: + _outGatherMergePath(str, obj); + break; + case T_NestPath: + _outNestPath(str, obj); + break; + case T_MergePath: + _outMergePath(str, obj); + break; + case T_HashPath: + _outHashPath(str, obj); + break; + case T_PlannerGlobal: + _outPlannerGlobal(str, obj); + break; + case T_PlannerInfo: + _outPlannerInfo(str, obj); + break; + case T_RelOptInfo: + _outRelOptInfo(str, obj); + break; + case T_IndexOptInfo: + _outIndexOptInfo(str, obj); + break; + case T_ForeignKeyOptInfo: + _outForeignKeyOptInfo(str, obj); + break; + case T_EquivalenceClass: + _outEquivalenceClass(str, obj); + break; + case T_EquivalenceMember: + _outEquivalenceMember(str, obj); + break; + case T_PathKey: + _outPathKey(str, obj); + break; + case T_PathTarget: + _outPathTarget(str, obj); + break; + case T_ParamPathInfo: + _outParamPathInfo(str, obj); + break; + case T_RestrictInfo: + _outRestrictInfo(str, obj); + break; + case T_IndexClause: + _outIndexClause(str, obj); + break; + case T_PlaceHolderVar: + _outPlaceHolderVar(str, obj); + break; + case T_SpecialJoinInfo: + _outSpecialJoinInfo(str, obj); + break; + case T_AppendRelInfo: + _outAppendRelInfo(str, obj); + break; + case T_RowIdentityVarInfo: + _outRowIdentityVarInfo(str, obj); + break; + case T_PlaceHolderInfo: + _outPlaceHolderInfo(str, obj); + break; + case T_MinMaxAggInfo: + _outMinMaxAggInfo(str, obj); + break; + case T_PlannerParamItem: + _outPlannerParamItem(str, obj); + break; + case T_RollupData: + _outRollupData(str, obj); + break; + case T_GroupingSetData: + _outGroupingSetData(str, obj); + break; + case T_StatisticExtInfo: + _outStatisticExtInfo(str, obj); + break; + case T_ExtensibleNode: + _outExtensibleNode(str, obj); + break; + case T_CreateStmt: + _outCreateStmt(str, obj); + break; + case T_CreateForeignTableStmt: + _outCreateForeignTableStmt(str, obj); + break; + case T_ImportForeignSchemaStmt: + _outImportForeignSchemaStmt(str, obj); + break; + case T_IndexStmt: + _outIndexStmt(str, obj); + break; + case T_CreateStatsStmt: + _outCreateStatsStmt(str, obj); + break; + case T_AlterStatsStmt: + _outAlterStatsStmt(str, obj); + break; + case T_NotifyStmt: + _outNotifyStmt(str, obj); + break; + case T_DeclareCursorStmt: + _outDeclareCursorStmt(str, obj); + break; + case T_SelectStmt: + _outSelectStmt(str, obj); + break; + case T_ReturnStmt: + _outReturnStmt(str, obj); + break; + case T_PLAssignStmt: + _outPLAssignStmt(str, obj); + break; + case T_ColumnDef: + _outColumnDef(str, obj); + break; + case T_TypeName: + _outTypeName(str, obj); + break; + case T_TypeCast: + _outTypeCast(str, obj); + break; + case T_CollateClause: + _outCollateClause(str, obj); + break; + case T_IndexElem: + _outIndexElem(str, obj); + break; + case T_StatsElem: + _outStatsElem(str, obj); + break; + case T_Query: + _outQuery(str, obj); + break; + case T_WithCheckOption: + _outWithCheckOption(str, obj); + break; + case T_SortGroupClause: + _outSortGroupClause(str, obj); + break; + case T_GroupingSet: + _outGroupingSet(str, obj); + break; + case T_WindowClause: + _outWindowClause(str, obj); + break; + case T_RowMarkClause: + _outRowMarkClause(str, obj); + break; + case T_WithClause: + _outWithClause(str, obj); + break; + case T_CTESearchClause: + _outCTESearchClause(str, obj); + break; + case T_CTECycleClause: + _outCTECycleClause(str, obj); + break; + case T_CommonTableExpr: + _outCommonTableExpr(str, obj); + break; + case T_MergeWhenClause: + _outMergeWhenClause(str, obj); + break; + case T_MergeAction: + _outMergeAction(str, obj); + break; + case T_SetOperationStmt: + _outSetOperationStmt(str, obj); + break; + case T_RangeTblEntry: + _outRangeTblEntry(str, obj); + break; + case T_RangeTblFunction: + _outRangeTblFunction(str, obj); + break; + case T_TableSampleClause: + _outTableSampleClause(str, obj); + break; + case T_A_Expr: + _outA_Expr(str, obj); + break; + case T_ColumnRef: + _outColumnRef(str, obj); + break; + case T_ParamRef: + _outParamRef(str, obj); + break; + case T_RawStmt: + _outRawStmt(str, obj); + break; + case T_A_Const: + _outA_Const(str, obj); + break; + case T_A_Star: + _outA_Star(str, obj); + break; + case T_A_Indices: + _outA_Indices(str, obj); + break; + case T_A_Indirection: + _outA_Indirection(str, obj); + break; + case T_A_ArrayExpr: + _outA_ArrayExpr(str, obj); + break; + case T_ResTarget: + _outResTarget(str, obj); + break; + case T_MultiAssignRef: + _outMultiAssignRef(str, obj); + break; + case T_SortBy: + _outSortBy(str, obj); + break; + case T_WindowDef: + _outWindowDef(str, obj); + break; + case T_RangeSubselect: + _outRangeSubselect(str, obj); + break; + case T_RangeFunction: + _outRangeFunction(str, obj); + break; + case T_RangeTableSample: + _outRangeTableSample(str, obj); + break; + case T_RangeTableFunc: + _outRangeTableFunc(str, obj); + break; + case T_RangeTableFuncCol: + _outRangeTableFuncCol(str, obj); + break; + case T_Constraint: + _outConstraint(str, obj); + break; + case T_FuncCall: + _outFuncCall(str, obj); + break; + case T_DefElem: + _outDefElem(str, obj); + break; + case T_TableLikeClause: + _outTableLikeClause(str, obj); + break; + case T_LockingClause: + _outLockingClause(str, obj); + break; + case T_XmlSerialize: + _outXmlSerialize(str, obj); + break; + case T_ForeignKeyCacheInfo: + _outForeignKeyCacheInfo(str, obj); + break; + case T_TriggerTransition: + _outTriggerTransition(str, obj); + break; + case T_PartitionElem: + _outPartitionElem(str, obj); + break; + case T_PartitionSpec: + _outPartitionSpec(str, obj); + break; + case T_PartitionBoundSpec: + _outPartitionBoundSpec(str, obj); + break; + case T_PartitionRangeDatum: + _outPartitionRangeDatum(str, obj); + break; + + default: + + /* + * This should be an ERROR, but it's too useful to be able to + * dump structures that outNode only understands part of. + */ + elog(WARNING, "could not dump unrecognized node type: %d", + (int) nodeTag(obj)); + break; + } + appendStringInfoChar(str, '}'); + } +} + +/* + * nodeToString - + * returns the ascii representation of the Node as a palloc'd string + */ +char * +nodeToString(const void *obj) +{ + StringInfoData str; + + /* see stringinfo.h for an explanation of this maneuver */ + initStringInfo(&str); + outNode(&str, obj); + return str.data; +} + +/* + * bmsToString - + * returns the ascii representation of the Bitmapset as a palloc'd string + */ +char * +bmsToString(const Bitmapset *bms) +{ + StringInfoData str; + + /* see stringinfo.h for an explanation of this maneuver */ + initStringInfo(&str); + outBitmapset(&str, bms); + return str.data; +} diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c new file mode 100644 index 0000000..0d5c91b --- /dev/null +++ b/src/backend/nodes/params.c @@ -0,0 +1,422 @@ +/*------------------------------------------------------------------------- + * + * params.c + * Support for finding the values associated with Param nodes. + * + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/nodes/params.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/xact.h" +#include "fmgr.h" +#include "mb/stringinfo_mb.h" +#include "nodes/params.h" +#include "parser/parse_node.h" +#include "storage/shmem.h" +#include "utils/datum.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" + + +static void paramlist_parser_setup(ParseState *pstate, void *arg); +static Node *paramlist_param_ref(ParseState *pstate, ParamRef *pref); + + +/* + * Allocate and initialize a new ParamListInfo structure. + * + * To make a new structure for the "dynamic" way (with hooks), pass 0 for + * numParams and set numParams manually. + * + * A default parserSetup function is supplied automatically. Callers may + * override it if they choose. (Note that most use-cases for ParamListInfos + * will never use the parserSetup function anyway.) + */ +ParamListInfo +makeParamList(int numParams) +{ + ParamListInfo retval; + Size size; + + size = offsetof(ParamListInfoData, params) + + numParams * sizeof(ParamExternData); + + retval = (ParamListInfo) palloc(size); + retval->paramFetch = NULL; + retval->paramFetchArg = NULL; + retval->paramCompile = NULL; + retval->paramCompileArg = NULL; + retval->parserSetup = paramlist_parser_setup; + retval->parserSetupArg = (void *) retval; + retval->paramValuesStr = NULL; + retval->numParams = numParams; + + return retval; +} + +/* + * Copy a ParamListInfo structure. + * + * The result is allocated in CurrentMemoryContext. + * + * Note: the intent of this function is to make a static, self-contained + * set of parameter values. If dynamic parameter hooks are present, we + * intentionally do not copy them into the result. Rather, we forcibly + * instantiate all available parameter values and copy the datum values. + * + * paramValuesStr is not copied, either. + */ +ParamListInfo +copyParamList(ParamListInfo from) +{ + ParamListInfo retval; + + if (from == NULL || from->numParams <= 0) + return NULL; + + retval = makeParamList(from->numParams); + + for (int i = 0; i < from->numParams; i++) + { + ParamExternData *oprm; + ParamExternData *nprm = &retval->params[i]; + ParamExternData prmdata; + int16 typLen; + bool typByVal; + + /* give hook a chance in case parameter is dynamic */ + if (from->paramFetch != NULL) + oprm = from->paramFetch(from, i + 1, false, &prmdata); + else + oprm = &from->params[i]; + + /* flat-copy the parameter info */ + *nprm = *oprm; + + /* need datumCopy in case it's a pass-by-reference datatype */ + if (nprm->isnull || !OidIsValid(nprm->ptype)) + continue; + get_typlenbyval(nprm->ptype, &typLen, &typByVal); + nprm->value = datumCopy(nprm->value, typByVal, typLen); + } + + return retval; +} + + +/* + * Set up to parse a query containing references to parameters + * sourced from a ParamListInfo. + */ +static void +paramlist_parser_setup(ParseState *pstate, void *arg) +{ + pstate->p_paramref_hook = paramlist_param_ref; + /* no need to use p_coerce_param_hook */ + pstate->p_ref_hook_state = arg; +} + +/* + * Transform a ParamRef using parameter type data from a ParamListInfo. + */ +static Node * +paramlist_param_ref(ParseState *pstate, ParamRef *pref) +{ + ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state; + int paramno = pref->number; + ParamExternData *prm; + ParamExternData prmdata; + Param *param; + + /* check parameter number is valid */ + if (paramno <= 0 || paramno > paramLI->numParams) + return NULL; + + /* give hook a chance in case parameter is dynamic */ + if (paramLI->paramFetch != NULL) + prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata); + else + prm = ¶mLI->params[paramno - 1]; + + if (!OidIsValid(prm->ptype)) + return NULL; + + param = makeNode(Param); + param->paramkind = PARAM_EXTERN; + param->paramid = paramno; + param->paramtype = prm->ptype; + param->paramtypmod = -1; + param->paramcollid = get_typcollation(param->paramtype); + param->location = pref->location; + + return (Node *) param; +} + +/* + * Estimate the amount of space required to serialize a ParamListInfo. + */ +Size +EstimateParamListSpace(ParamListInfo paramLI) +{ + int i; + Size sz = sizeof(int); + + if (paramLI == NULL || paramLI->numParams <= 0) + return sz; + + for (i = 0; i < paramLI->numParams; i++) + { + ParamExternData *prm; + ParamExternData prmdata; + Oid typeOid; + int16 typLen; + bool typByVal; + + /* give hook a chance in case parameter is dynamic */ + if (paramLI->paramFetch != NULL) + prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata); + else + prm = ¶mLI->params[i]; + + typeOid = prm->ptype; + + sz = add_size(sz, sizeof(Oid)); /* space for type OID */ + sz = add_size(sz, sizeof(uint16)); /* space for pflags */ + + /* space for datum/isnull */ + if (OidIsValid(typeOid)) + get_typlenbyval(typeOid, &typLen, &typByVal); + else + { + /* If no type OID, assume by-value, like copyParamList does. */ + typLen = sizeof(Datum); + typByVal = true; + } + sz = add_size(sz, + datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen)); + } + + return sz; +} + +/* + * Serialize a ParamListInfo structure into caller-provided storage. + * + * We write the number of parameters first, as a 4-byte integer, and then + * write details for each parameter in turn. The details for each parameter + * consist of a 4-byte type OID, 2 bytes of flags, and then the datum as + * serialized by datumSerialize(). The caller is responsible for ensuring + * that there is enough storage to store the number of bytes that will be + * written; use EstimateParamListSpace to find out how many will be needed. + * *start_address is updated to point to the byte immediately following those + * written. + * + * RestoreParamList can be used to recreate a ParamListInfo based on the + * serialized representation; this will be a static, self-contained copy + * just as copyParamList would create. + * + * paramValuesStr is not included. + */ +void +SerializeParamList(ParamListInfo paramLI, char **start_address) +{ + int nparams; + int i; + + /* Write number of parameters. */ + if (paramLI == NULL || paramLI->numParams <= 0) + nparams = 0; + else + nparams = paramLI->numParams; + memcpy(*start_address, &nparams, sizeof(int)); + *start_address += sizeof(int); + + /* Write each parameter in turn. */ + for (i = 0; i < nparams; i++) + { + ParamExternData *prm; + ParamExternData prmdata; + Oid typeOid; + int16 typLen; + bool typByVal; + + /* give hook a chance in case parameter is dynamic */ + if (paramLI->paramFetch != NULL) + prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata); + else + prm = ¶mLI->params[i]; + + typeOid = prm->ptype; + + /* Write type OID. */ + memcpy(*start_address, &typeOid, sizeof(Oid)); + *start_address += sizeof(Oid); + + /* Write flags. */ + memcpy(*start_address, &prm->pflags, sizeof(uint16)); + *start_address += sizeof(uint16); + + /* Write datum/isnull. */ + if (OidIsValid(typeOid)) + get_typlenbyval(typeOid, &typLen, &typByVal); + else + { + /* If no type OID, assume by-value, like copyParamList does. */ + typLen = sizeof(Datum); + typByVal = true; + } + datumSerialize(prm->value, prm->isnull, typByVal, typLen, + start_address); + } +} + +/* + * Copy a ParamListInfo structure. + * + * The result is allocated in CurrentMemoryContext. + * + * Note: the intent of this function is to make a static, self-contained + * set of parameter values. If dynamic parameter hooks are present, we + * intentionally do not copy them into the result. Rather, we forcibly + * instantiate all available parameter values and copy the datum values. + */ +ParamListInfo +RestoreParamList(char **start_address) +{ + ParamListInfo paramLI; + int nparams; + + memcpy(&nparams, *start_address, sizeof(int)); + *start_address += sizeof(int); + + paramLI = makeParamList(nparams); + + for (int i = 0; i < nparams; i++) + { + ParamExternData *prm = ¶mLI->params[i]; + + /* Read type OID. */ + memcpy(&prm->ptype, *start_address, sizeof(Oid)); + *start_address += sizeof(Oid); + + /* Read flags. */ + memcpy(&prm->pflags, *start_address, sizeof(uint16)); + *start_address += sizeof(uint16); + + /* Read datum/isnull. */ + prm->value = datumRestore(start_address, &prm->isnull); + } + + return paramLI; +} + +/* + * BuildParamLogString + * Return a string that represents the parameter list, for logging. + * + * If caller already knows textual representations for some parameters, it can + * pass an array of exactly params->numParams values as knownTextValues, which + * can contain NULLs for any unknown individual values. NULL can be given if + * no parameters are known. + * + * If maxlen is >= 0, that's the maximum number of bytes of any one + * parameter value to be printed; an ellipsis is added if the string is + * longer. (Added quotes are not considered in this calculation.) + */ +char * +BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen) +{ + MemoryContext tmpCxt, + oldCxt; + StringInfoData buf; + + /* + * NB: think not of returning params->paramValuesStr! It may have been + * generated with a different maxlen, and so be unsuitable. Besides that, + * this is the function used to create that string. + */ + + /* + * No work if the param fetch hook is in use. Also, it's not possible to + * do this in an aborted transaction. (It might be possible to improve on + * this last point when some knownTextValues exist, but it seems tricky.) + */ + if (params->paramFetch != NULL || + IsAbortedTransactionBlockState()) + return NULL; + + /* Initialize the output stringinfo, in caller's memory context */ + initStringInfo(&buf); + + /* Use a temporary context to call output functions, just in case */ + tmpCxt = AllocSetContextCreate(CurrentMemoryContext, + "BuildParamLogString", + ALLOCSET_DEFAULT_SIZES); + oldCxt = MemoryContextSwitchTo(tmpCxt); + + for (int paramno = 0; paramno < params->numParams; paramno++) + { + ParamExternData *param = ¶ms->params[paramno]; + + appendStringInfo(&buf, + "%s$%d = ", + paramno > 0 ? ", " : "", + paramno + 1); + + if (param->isnull || !OidIsValid(param->ptype)) + appendStringInfoString(&buf, "NULL"); + else + { + if (knownTextValues != NULL && knownTextValues[paramno] != NULL) + appendStringInfoStringQuoted(&buf, knownTextValues[paramno], + maxlen); + else + { + Oid typoutput; + bool typisvarlena; + char *pstring; + + getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena); + pstring = OidOutputFunctionCall(typoutput, param->value); + appendStringInfoStringQuoted(&buf, pstring, maxlen); + } + } + } + + MemoryContextSwitchTo(oldCxt); + MemoryContextDelete(tmpCxt); + + return buf.data; +} + +/* + * ParamsErrorCallback - callback for printing parameters in error context + * + * Note that this is a no-op unless BuildParamLogString has been called + * beforehand. + */ +void +ParamsErrorCallback(void *arg) +{ + ParamsErrorCbData *data = (ParamsErrorCbData *) arg; + + if (data == NULL || + data->params == NULL || + data->params->paramValuesStr == NULL) + return; + + if (data->portalName && data->portalName[0] != '\0') + errcontext("portal \"%s\" with parameters: %s", + data->portalName, data->params->paramValuesStr); + else + errcontext("unnamed portal with parameters: %s", + data->params->paramValuesStr); +} diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c new file mode 100644 index 0000000..a5c44ad --- /dev/null +++ b/src/backend/nodes/print.c @@ -0,0 +1,506 @@ +/*------------------------------------------------------------------------- + * + * print.c + * various print routines (used mostly for debugging) + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/print.c + * + * HISTORY + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Oct 26, 1994 file creation + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/printtup.h" +#include "lib/stringinfo.h" +#include "nodes/nodeFuncs.h" +#include "nodes/pathnodes.h" +#include "nodes/print.h" +#include "parser/parsetree.h" +#include "utils/lsyscache.h" + + +/* + * print + * print contents of Node to stdout + */ +void +print(const void *obj) +{ + char *s; + char *f; + + s = nodeToString(obj); + f = format_node_dump(s); + pfree(s); + printf("%s\n", f); + fflush(stdout); + pfree(f); +} + +/* + * pprint + * pretty-print contents of Node to stdout + */ +void +pprint(const void *obj) +{ + char *s; + char *f; + + s = nodeToString(obj); + f = pretty_format_node_dump(s); + pfree(s); + printf("%s\n", f); + fflush(stdout); + pfree(f); +} + +/* + * elog_node_display + * send pretty-printed contents of Node to postmaster log + */ +void +elog_node_display(int lev, const char *title, const void *obj, bool pretty) +{ + char *s; + char *f; + + s = nodeToString(obj); + if (pretty) + f = pretty_format_node_dump(s); + else + f = format_node_dump(s); + pfree(s); + ereport(lev, + (errmsg_internal("%s:", title), + errdetail_internal("%s", f))); + pfree(f); +} + +/* + * Format a nodeToString output for display on a terminal. + * + * The result is a palloc'd string. + * + * This version just tries to break at whitespace. + */ +char * +format_node_dump(const char *dump) +{ +#define LINELEN 78 + char line[LINELEN + 1]; + StringInfoData str; + int i; + int j; + int k; + + initStringInfo(&str); + i = 0; + for (;;) + { + for (j = 0; j < LINELEN && dump[i] != '\0'; i++, j++) + line[j] = dump[i]; + if (dump[i] == '\0') + break; + if (dump[i] == ' ') + { + /* ok to break at adjacent space */ + i++; + } + else + { + for (k = j - 1; k > 0; k--) + if (line[k] == ' ') + break; + if (k > 0) + { + /* back up; will reprint all after space */ + i -= (j - k - 1); + j = k; + } + } + line[j] = '\0'; + appendStringInfo(&str, "%s\n", line); + } + if (j > 0) + { + line[j] = '\0'; + appendStringInfo(&str, "%s\n", line); + } + return str.data; +#undef LINELEN +} + +/* + * Format a nodeToString output for display on a terminal. + * + * The result is a palloc'd string. + * + * This version tries to indent intelligently. + */ +char * +pretty_format_node_dump(const char *dump) +{ +#define INDENTSTOP 3 +#define MAXINDENT 60 +#define LINELEN 78 + char line[LINELEN + 1]; + StringInfoData str; + int indentLev; + int indentDist; + int i; + int j; + + initStringInfo(&str); + indentLev = 0; /* logical indent level */ + indentDist = 0; /* physical indent distance */ + i = 0; + for (;;) + { + for (j = 0; j < indentDist; j++) + line[j] = ' '; + for (; j < LINELEN && dump[i] != '\0'; i++, j++) + { + line[j] = dump[i]; + switch (line[j]) + { + case '}': + if (j != indentDist) + { + /* print data before the } */ + line[j] = '\0'; + appendStringInfo(&str, "%s\n", line); + } + /* print the } at indentDist */ + line[indentDist] = '}'; + line[indentDist + 1] = '\0'; + appendStringInfo(&str, "%s\n", line); + /* outdent */ + if (indentLev > 0) + { + indentLev--; + indentDist = Min(indentLev * INDENTSTOP, MAXINDENT); + } + j = indentDist - 1; + /* j will equal indentDist on next loop iteration */ + /* suppress whitespace just after } */ + while (dump[i + 1] == ' ') + i++; + break; + case ')': + /* force line break after ), unless another ) follows */ + if (dump[i + 1] != ')') + { + line[j + 1] = '\0'; + appendStringInfo(&str, "%s\n", line); + j = indentDist - 1; + while (dump[i + 1] == ' ') + i++; + } + break; + case '{': + /* force line break before { */ + if (j != indentDist) + { + line[j] = '\0'; + appendStringInfo(&str, "%s\n", line); + } + /* indent */ + indentLev++; + indentDist = Min(indentLev * INDENTSTOP, MAXINDENT); + for (j = 0; j < indentDist; j++) + line[j] = ' '; + line[j] = dump[i]; + break; + case ':': + /* force line break before : */ + if (j != indentDist) + { + line[j] = '\0'; + appendStringInfo(&str, "%s\n", line); + } + j = indentDist; + line[j] = dump[i]; + break; + } + } + line[j] = '\0'; + if (dump[i] == '\0') + break; + appendStringInfo(&str, "%s\n", line); + } + if (j > 0) + appendStringInfo(&str, "%s\n", line); + return str.data; +#undef INDENTSTOP +#undef MAXINDENT +#undef LINELEN +} + +/* + * print_rt + * print contents of range table + */ +void +print_rt(const List *rtable) +{ + const ListCell *l; + int i = 1; + + printf("resno\trefname \trelid\tinFromCl\n"); + printf("-----\t---------\t-----\t--------\n"); + foreach(l, rtable) + { + RangeTblEntry *rte = lfirst(l); + + switch (rte->rtekind) + { + case RTE_RELATION: + printf("%d\t%s\t%u\t%c", + i, rte->eref->aliasname, rte->relid, rte->relkind); + break; + case RTE_SUBQUERY: + printf("%d\t%s\t[subquery]", + i, rte->eref->aliasname); + break; + case RTE_JOIN: + printf("%d\t%s\t[join]", + i, rte->eref->aliasname); + break; + case RTE_FUNCTION: + printf("%d\t%s\t[rangefunction]", + i, rte->eref->aliasname); + break; + case RTE_TABLEFUNC: + printf("%d\t%s\t[table function]", + i, rte->eref->aliasname); + break; + case RTE_VALUES: + printf("%d\t%s\t[values list]", + i, rte->eref->aliasname); + break; + case RTE_CTE: + printf("%d\t%s\t[cte]", + i, rte->eref->aliasname); + break; + case RTE_NAMEDTUPLESTORE: + printf("%d\t%s\t[tuplestore]", + i, rte->eref->aliasname); + break; + case RTE_RESULT: + printf("%d\t%s\t[result]", + i, rte->eref->aliasname); + break; + default: + printf("%d\t%s\t[unknown rtekind]", + i, rte->eref->aliasname); + } + + printf("\t%s\t%s\n", + (rte->inh ? "inh" : ""), + (rte->inFromCl ? "inFromCl" : "")); + i++; + } +} + + +/* + * print_expr + * print an expression + */ +void +print_expr(const Node *expr, const List *rtable) +{ + if (expr == NULL) + { + printf("<>"); + return; + } + + if (IsA(expr, Var)) + { + const Var *var = (const Var *) expr; + char *relname, + *attname; + + switch (var->varno) + { + case INNER_VAR: + relname = "INNER"; + attname = "?"; + break; + case OUTER_VAR: + relname = "OUTER"; + attname = "?"; + break; + case INDEX_VAR: + relname = "INDEX"; + attname = "?"; + break; + default: + { + RangeTblEntry *rte; + + Assert(var->varno > 0 && + (int) var->varno <= list_length(rtable)); + rte = rt_fetch(var->varno, rtable); + relname = rte->eref->aliasname; + attname = get_rte_attribute_name(rte, var->varattno); + } + break; + } + printf("%s.%s", relname, attname); + } + else if (IsA(expr, Const)) + { + const Const *c = (const Const *) expr; + Oid typoutput; + bool typIsVarlena; + char *outputstr; + + if (c->constisnull) + { + printf("NULL"); + return; + } + + getTypeOutputInfo(c->consttype, + &typoutput, &typIsVarlena); + + outputstr = OidOutputFunctionCall(typoutput, c->constvalue); + printf("%s", outputstr); + pfree(outputstr); + } + else if (IsA(expr, OpExpr)) + { + const OpExpr *e = (const OpExpr *) expr; + char *opname; + + opname = get_opname(e->opno); + if (list_length(e->args) > 1) + { + print_expr(get_leftop((const Expr *) e), rtable); + printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)")); + print_expr(get_rightop((const Expr *) e), rtable); + } + else + { + printf("%s ", ((opname != NULL) ? opname : "(invalid operator)")); + print_expr(get_leftop((const Expr *) e), rtable); + } + } + else if (IsA(expr, FuncExpr)) + { + const FuncExpr *e = (const FuncExpr *) expr; + char *funcname; + ListCell *l; + + funcname = get_func_name(e->funcid); + printf("%s(", ((funcname != NULL) ? funcname : "(invalid function)")); + foreach(l, e->args) + { + print_expr(lfirst(l), rtable); + if (lnext(e->args, l)) + printf(","); + } + printf(")"); + } + else + printf("unknown expr"); +} + +/* + * print_pathkeys - + * pathkeys list of PathKeys + */ +void +print_pathkeys(const List *pathkeys, const List *rtable) +{ + const ListCell *i; + + printf("("); + foreach(i, pathkeys) + { + PathKey *pathkey = (PathKey *) lfirst(i); + EquivalenceClass *eclass; + ListCell *k; + bool first = true; + + eclass = pathkey->pk_eclass; + /* chase up, in case pathkey is non-canonical */ + while (eclass->ec_merged) + eclass = eclass->ec_merged; + + printf("("); + foreach(k, eclass->ec_members) + { + EquivalenceMember *mem = (EquivalenceMember *) lfirst(k); + + if (first) + first = false; + else + printf(", "); + print_expr((Node *) mem->em_expr, rtable); + } + printf(")"); + if (lnext(pathkeys, i)) + printf(", "); + } + printf(")\n"); +} + +/* + * print_tl + * print targetlist in a more legible way. + */ +void +print_tl(const List *tlist, const List *rtable) +{ + const ListCell *tl; + + printf("(\n"); + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + + printf("\t%d %s\t", tle->resno, + tle->resname ? tle->resname : "<null>"); + if (tle->ressortgroupref != 0) + printf("(%u):\t", tle->ressortgroupref); + else + printf(" :\t"); + print_expr((Node *) tle->expr, rtable); + printf("\n"); + } + printf(")\n"); +} + +/* + * print_slot + * print out the tuple with the given TupleTableSlot + */ +void +print_slot(TupleTableSlot *slot) +{ + if (TupIsNull(slot)) + { + printf("tuple is null.\n"); + return; + } + if (!slot->tts_tupleDescriptor) + { + printf("no tuple descriptor.\n"); + return; + } + + debugtup(slot, NULL); +} diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c new file mode 100644 index 0000000..1e61fde --- /dev/null +++ b/src/backend/nodes/read.c @@ -0,0 +1,468 @@ +/*------------------------------------------------------------------------- + * + * read.c + * routines to convert a string (legal ascii representation of node) back + * to nodes + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/read.c + * + * HISTORY + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Nov 2, 1994 file creation + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <ctype.h> + +#include "common/string.h" +#include "nodes/pg_list.h" +#include "nodes/readfuncs.h" +#include "nodes/value.h" + + +/* Static state for pg_strtok */ +static const char *pg_strtok_ptr = NULL; + +/* State flag that determines how readfuncs.c should treat location fields */ +#ifdef WRITE_READ_PARSE_PLAN_TREES +bool restore_location_fields = false; +#endif + + +/* + * stringToNode - + * builds a Node tree from its string representation (assumed valid) + * + * restore_loc_fields instructs readfuncs.c whether to restore location + * fields rather than set them to -1. This is currently only supported + * in builds with the WRITE_READ_PARSE_PLAN_TREES debugging flag set. + */ +static void * +stringToNodeInternal(const char *str, bool restore_loc_fields) +{ + void *retval; + const char *save_strtok; +#ifdef WRITE_READ_PARSE_PLAN_TREES + bool save_restore_location_fields; +#endif + + /* + * We save and restore the pre-existing state of pg_strtok. This makes the + * world safe for re-entrant invocation of stringToNode, without incurring + * a lot of notational overhead by having to pass the next-character + * pointer around through all the readfuncs.c code. + */ + save_strtok = pg_strtok_ptr; + + pg_strtok_ptr = str; /* point pg_strtok at the string to read */ + + /* + * If enabled, likewise save/restore the location field handling flag. + */ +#ifdef WRITE_READ_PARSE_PLAN_TREES + save_restore_location_fields = restore_location_fields; + restore_location_fields = restore_loc_fields; +#endif + + retval = nodeRead(NULL, 0); /* do the reading */ + + pg_strtok_ptr = save_strtok; + +#ifdef WRITE_READ_PARSE_PLAN_TREES + restore_location_fields = save_restore_location_fields; +#endif + + return retval; +} + +/* + * Externally visible entry points + */ +void * +stringToNode(const char *str) +{ + return stringToNodeInternal(str, false); +} + +#ifdef WRITE_READ_PARSE_PLAN_TREES + +void * +stringToNodeWithLocations(const char *str) +{ + return stringToNodeInternal(str, true); +} + +#endif + + +/***************************************************************************** + * + * the lisp token parser + * + *****************************************************************************/ + +/* + * pg_strtok --- retrieve next "token" from a string. + * + * Works kinda like strtok, except it never modifies the source string. + * (Instead of storing nulls into the string, the length of the token + * is returned to the caller.) + * Also, the rules about what is a token are hard-wired rather than being + * configured by passing a set of terminating characters. + * + * The string is assumed to have been initialized already by stringToNode. + * + * The rules for tokens are: + * * Whitespace (space, tab, newline) always separates tokens. + * * The characters '(', ')', '{', '}' form individual tokens even + * without any whitespace around them. + * * Otherwise, a token is all the characters up to the next whitespace + * or occurrence of one of the four special characters. + * * A backslash '\' can be used to quote whitespace or one of the four + * special characters, so that it is treated as a plain token character. + * Backslashes themselves must also be backslashed for consistency. + * Any other character can be, but need not be, backslashed as well. + * * If the resulting token is '<>' (with no backslash), it is returned + * as a non-NULL pointer to the token but with length == 0. Note that + * there is no other way to get a zero-length token. + * + * Returns a pointer to the start of the next token, and the length of the + * token (including any embedded backslashes!) in *length. If there are + * no more tokens, NULL and 0 are returned. + * + * NOTE: this routine doesn't remove backslashes; the caller must do so + * if necessary (see "debackslash"). + * + * NOTE: prior to release 7.0, this routine also had a special case to treat + * a token starting with '"' as extending to the next '"'. This code was + * broken, however, since it would fail to cope with a string containing an + * embedded '"'. I have therefore removed this special case, and instead + * introduced rules for using backslashes to quote characters. Higher-level + * code should add backslashes to a string constant to ensure it is treated + * as a single token. + */ +const char * +pg_strtok(int *length) +{ + const char *local_str; /* working pointer to string */ + const char *ret_str; /* start of token to return */ + + local_str = pg_strtok_ptr; + + while (*local_str == ' ' || *local_str == '\n' || *local_str == '\t') + local_str++; + + if (*local_str == '\0') + { + *length = 0; + pg_strtok_ptr = local_str; + return NULL; /* no more tokens */ + } + + /* + * Now pointing at start of next token. + */ + ret_str = local_str; + + if (*local_str == '(' || *local_str == ')' || + *local_str == '{' || *local_str == '}') + { + /* special 1-character token */ + local_str++; + } + else + { + /* Normal token, possibly containing backslashes */ + while (*local_str != '\0' && + *local_str != ' ' && *local_str != '\n' && + *local_str != '\t' && + *local_str != '(' && *local_str != ')' && + *local_str != '{' && *local_str != '}') + { + if (*local_str == '\\' && local_str[1] != '\0') + local_str += 2; + else + local_str++; + } + } + + *length = local_str - ret_str; + + /* Recognize special case for "empty" token */ + if (*length == 2 && ret_str[0] == '<' && ret_str[1] == '>') + *length = 0; + + pg_strtok_ptr = local_str; + + return ret_str; +} + +/* + * debackslash - + * create a palloc'd string holding the given token. + * any protective backslashes in the token are removed. + */ +char * +debackslash(const char *token, int length) +{ + char *result = palloc(length + 1); + char *ptr = result; + + while (length > 0) + { + if (*token == '\\' && length > 1) + token++, length--; + *ptr++ = *token++; + length--; + } + *ptr = '\0'; + return result; +} + +#define RIGHT_PAREN (1000000 + 1) +#define LEFT_PAREN (1000000 + 2) +#define LEFT_BRACE (1000000 + 3) +#define OTHER_TOKEN (1000000 + 4) + +/* + * nodeTokenType - + * returns the type of the node token contained in token. + * It returns one of the following valid NodeTags: + * T_Integer, T_Float, T_Boolean, T_String, T_BitString + * and some of its own: + * RIGHT_PAREN, LEFT_PAREN, LEFT_BRACE, OTHER_TOKEN + * + * Assumption: the ascii representation is legal + */ +static NodeTag +nodeTokenType(const char *token, int length) +{ + NodeTag retval; + const char *numptr; + int numlen; + + /* + * Check if the token is a number + */ + numptr = token; + numlen = length; + if (*numptr == '+' || *numptr == '-') + numptr++, numlen--; + if ((numlen > 0 && isdigit((unsigned char) *numptr)) || + (numlen > 1 && *numptr == '.' && isdigit((unsigned char) numptr[1]))) + { + /* + * Yes. Figure out whether it is integral or float; this requires + * both a syntax check and a range check. strtoint() can do both for + * us. We know the token will end at a character that strtoint will + * stop at, so we do not need to modify the string. + */ + char *endptr; + + errno = 0; + (void) strtoint(token, &endptr, 10); + if (endptr != token + length || errno == ERANGE) + return T_Float; + return T_Integer; + } + + /* + * these three cases do not need length checks, since pg_strtok() will + * always treat them as single-byte tokens + */ + else if (*token == '(') + retval = LEFT_PAREN; + else if (*token == ')') + retval = RIGHT_PAREN; + else if (*token == '{') + retval = LEFT_BRACE; + else if ((length == 4 && strncmp(token, "true", 4) == 0) || + (length == 5 && strncmp(token, "false", 5) == 0)) + retval = T_Boolean; + else if (*token == '"' && length > 1 && token[length - 1] == '"') + retval = T_String; + else if (*token == 'b') + retval = T_BitString; + else + retval = OTHER_TOKEN; + return retval; +} + +/* + * nodeRead - + * Slightly higher-level reader. + * + * This routine applies some semantic knowledge on top of the purely + * lexical tokenizer pg_strtok(). It can read + * * Value token nodes (integers, floats, booleans, or strings); + * * General nodes (via parseNodeString() from readfuncs.c); + * * Lists of the above; + * * Lists of integers or OIDs. + * The return value is declared void *, not Node *, to avoid having to + * cast it explicitly in callers that assign to fields of different types. + * + * External callers should always pass NULL/0 for the arguments. Internally + * a non-NULL token may be passed when the upper recursion level has already + * scanned the first token of a node's representation. + * + * We assume pg_strtok is already initialized with a string to read (hence + * this should only be invoked from within a stringToNode operation). + */ +void * +nodeRead(const char *token, int tok_len) +{ + Node *result; + NodeTag type; + + if (token == NULL) /* need to read a token? */ + { + token = pg_strtok(&tok_len); + + if (token == NULL) /* end of input */ + return NULL; + } + + type = nodeTokenType(token, tok_len); + + switch ((int) type) + { + case LEFT_BRACE: + result = parseNodeString(); + token = pg_strtok(&tok_len); + if (token == NULL || token[0] != '}') + elog(ERROR, "did not find '}' at end of input node"); + break; + case LEFT_PAREN: + { + List *l = NIL; + + /*---------- + * Could be an integer list: (i int int ...) + * or an OID list: (o int int ...) + * or a list of nodes/values: (node node ...) + *---------- + */ + token = pg_strtok(&tok_len); + if (token == NULL) + elog(ERROR, "unterminated List structure"); + if (tok_len == 1 && token[0] == 'i') + { + /* List of integers */ + for (;;) + { + int val; + char *endptr; + + token = pg_strtok(&tok_len); + if (token == NULL) + elog(ERROR, "unterminated List structure"); + if (token[0] == ')') + break; + val = (int) strtol(token, &endptr, 10); + if (endptr != token + tok_len) + elog(ERROR, "unrecognized integer: \"%.*s\"", + tok_len, token); + l = lappend_int(l, val); + } + } + else if (tok_len == 1 && token[0] == 'o') + { + /* List of OIDs */ + for (;;) + { + Oid val; + char *endptr; + + token = pg_strtok(&tok_len); + if (token == NULL) + elog(ERROR, "unterminated List structure"); + if (token[0] == ')') + break; + val = (Oid) strtoul(token, &endptr, 10); + if (endptr != token + tok_len) + elog(ERROR, "unrecognized OID: \"%.*s\"", + tok_len, token); + l = lappend_oid(l, val); + } + } + else + { + /* List of other node types */ + for (;;) + { + /* We have already scanned next token... */ + if (token[0] == ')') + break; + l = lappend(l, nodeRead(token, tok_len)); + token = pg_strtok(&tok_len); + if (token == NULL) + elog(ERROR, "unterminated List structure"); + } + } + result = (Node *) l; + break; + } + case RIGHT_PAREN: + elog(ERROR, "unexpected right parenthesis"); + result = NULL; /* keep compiler happy */ + break; + case OTHER_TOKEN: + if (tok_len == 0) + { + /* must be "<>" --- represents a null pointer */ + result = NULL; + } + else + { + elog(ERROR, "unrecognized token: \"%.*s\"", tok_len, token); + result = NULL; /* keep compiler happy */ + } + break; + case T_Integer: + + /* + * we know that the token terminates on a char atoi will stop at + */ + result = (Node *) makeInteger(atoi(token)); + break; + case T_Float: + { + char *fval = (char *) palloc(tok_len + 1); + + memcpy(fval, token, tok_len); + fval[tok_len] = '\0'; + result = (Node *) makeFloat(fval); + } + break; + case T_Boolean: + result = (Node *) makeBoolean(token[0] == 't'); + break; + case T_String: + /* need to remove leading and trailing quotes, and backslashes */ + result = (Node *) makeString(debackslash(token + 1, tok_len - 2)); + break; + case T_BitString: + { + char *val = palloc(tok_len); + + /* skip leading 'b' */ + memcpy(val, token + 1, tok_len - 1); + val[tok_len - 1] = '\0'; + result = (Node *) makeBitString(val); + break; + } + default: + elog(ERROR, "unrecognized node type: %d", (int) type); + result = NULL; /* keep compiler happy */ + break; + } + + return (void *) result; +} diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c new file mode 100644 index 0000000..c84e5af --- /dev/null +++ b/src/backend/nodes/readfuncs.c @@ -0,0 +1,3190 @@ +/*------------------------------------------------------------------------- + * + * readfuncs.c + * Reader functions for Postgres tree nodes. + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/nodes/readfuncs.c + * + * NOTES + * Path nodes do not have any readfuncs support, because we never + * have occasion to read them in. (There was once code here that + * claimed to read them, but it was broken as well as unused.) We + * never read executor state trees, either. + * + * Parse location fields are written out by outfuncs.c, but only for + * debugging use. When reading a location field, we normally discard + * the stored value and set the location field to -1 (ie, "unknown"). + * This is because nodes coming from a stored rule should not be thought + * to have a known location in the current query's text. + * However, if restore_location_fields is true, we do restore location + * fields from the string. This is currently intended only for use by the + * WRITE_READ_PARSE_PLAN_TREES test code, which doesn't want to cause + * any change in the node contents. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <math.h> + +#include "miscadmin.h" +#include "nodes/extensible.h" +#include "nodes/parsenodes.h" +#include "nodes/plannodes.h" +#include "nodes/readfuncs.h" + + +/* + * Macros to simplify reading of different kinds of fields. Use these + * wherever possible to reduce the chance for silly typos. Note that these + * hard-wire conventions about the names of the local variables in a Read + * routine. + */ + +/* Macros for declaring appropriate local variables */ + +/* A few guys need only local_node */ +#define READ_LOCALS_NO_FIELDS(nodeTypeName) \ + nodeTypeName *local_node = makeNode(nodeTypeName) + +/* And a few guys need only the pg_strtok support fields */ +#define READ_TEMP_LOCALS() \ + const char *token; \ + int length + +/* ... but most need both */ +#define READ_LOCALS(nodeTypeName) \ + READ_LOCALS_NO_FIELDS(nodeTypeName); \ + READ_TEMP_LOCALS() + +/* Read an integer field (anything written as ":fldname %d") */ +#define READ_INT_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = atoi(token) + +/* Read an unsigned integer field (anything written as ":fldname %u") */ +#define READ_UINT_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = atoui(token) + +/* Read an unsigned integer field (anything written using UINT64_FORMAT) */ +#define READ_UINT64_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = strtou64(token, NULL, 10) + +/* Read a long integer field (anything written as ":fldname %ld") */ +#define READ_LONG_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = atol(token) + +/* Read an OID field (don't hard-wire assumption that OID is same as uint) */ +#define READ_OID_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = atooid(token) + +/* Read a char field (ie, one ascii character) */ +#define READ_CHAR_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + /* avoid overhead of calling debackslash() for one char */ \ + local_node->fldname = (length == 0) ? '\0' : (token[0] == '\\' ? token[1] : token[0]) + +/* Read an enumerated-type field that was written as an integer code */ +#define READ_ENUM_FIELD(fldname, enumtype) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = (enumtype) atoi(token) + +/* Read a float field */ +#define READ_FLOAT_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = atof(token) + +/* Read a boolean field */ +#define READ_BOOL_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = strtobool(token) + +/* Read a character-string field */ +#define READ_STRING_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = nullable_string(token, length) + +/* Read a parse location field (and possibly throw away the value) */ +#ifdef WRITE_READ_PARSE_PLAN_TREES +#define READ_LOCATION_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + local_node->fldname = restore_location_fields ? atoi(token) : -1 +#else +#define READ_LOCATION_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + token = pg_strtok(&length); /* get field value */ \ + (void) token; /* in case not used elsewhere */ \ + local_node->fldname = -1 /* set field to "unknown" */ +#endif + +/* Read a Node field */ +#define READ_NODE_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + (void) token; /* in case not used elsewhere */ \ + local_node->fldname = nodeRead(NULL, 0) + +/* Read a bitmapset field */ +#define READ_BITMAPSET_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + (void) token; /* in case not used elsewhere */ \ + local_node->fldname = _readBitmapset() + +/* Read an attribute number array */ +#define READ_ATTRNUMBER_ARRAY(fldname, len) \ + token = pg_strtok(&length); /* skip :fldname */ \ + local_node->fldname = readAttrNumberCols(len) + +/* Read an oid array */ +#define READ_OID_ARRAY(fldname, len) \ + token = pg_strtok(&length); /* skip :fldname */ \ + local_node->fldname = readOidCols(len) + +/* Read an int array */ +#define READ_INT_ARRAY(fldname, len) \ + token = pg_strtok(&length); /* skip :fldname */ \ + local_node->fldname = readIntCols(len) + +/* Read a bool array */ +#define READ_BOOL_ARRAY(fldname, len) \ + token = pg_strtok(&length); /* skip :fldname */ \ + local_node->fldname = readBoolCols(len) + +/* Routine exit */ +#define READ_DONE() \ + return local_node + + +/* + * NOTE: use atoi() to read values written with %d, or atoui() to read + * values written with %u in outfuncs.c. An exception is OID values, + * for which use atooid(). (As of 7.1, outfuncs.c writes OIDs as %u, + * but this will probably change in the future.) + */ +#define atoui(x) ((unsigned int) strtoul((x), NULL, 10)) + +#define strtobool(x) ((*(x) == 't') ? true : false) + +#define nullable_string(token,length) \ + ((length) == 0 ? NULL : debackslash(token, length)) + + +/* + * _readBitmapset + */ +static Bitmapset * +_readBitmapset(void) +{ + Bitmapset *result = NULL; + + READ_TEMP_LOCALS(); + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "incomplete Bitmapset structure"); + if (length != 1 || token[0] != '(') + elog(ERROR, "unrecognized token: \"%.*s\"", length, token); + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "incomplete Bitmapset structure"); + if (length != 1 || token[0] != 'b') + elog(ERROR, "unrecognized token: \"%.*s\"", length, token); + + for (;;) + { + int val; + char *endptr; + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "unterminated Bitmapset structure"); + if (length == 1 && token[0] == ')') + break; + val = (int) strtol(token, &endptr, 10); + if (endptr != token + length) + elog(ERROR, "unrecognized integer: \"%.*s\"", length, token); + result = bms_add_member(result, val); + } + + return result; +} + +/* + * for use by extensions which define extensible nodes + */ +Bitmapset * +readBitmapset(void) +{ + return _readBitmapset(); +} + +/* + * _readQuery + */ +static Query * +_readQuery(void) +{ + READ_LOCALS(Query); + + READ_ENUM_FIELD(commandType, CmdType); + READ_ENUM_FIELD(querySource, QuerySource); + local_node->queryId = UINT64CONST(0); /* not saved in output format */ + READ_BOOL_FIELD(canSetTag); + READ_NODE_FIELD(utilityStmt); + READ_INT_FIELD(resultRelation); + READ_BOOL_FIELD(hasAggs); + READ_BOOL_FIELD(hasWindowFuncs); + READ_BOOL_FIELD(hasTargetSRFs); + READ_BOOL_FIELD(hasSubLinks); + READ_BOOL_FIELD(hasDistinctOn); + READ_BOOL_FIELD(hasRecursive); + READ_BOOL_FIELD(hasModifyingCTE); + READ_BOOL_FIELD(hasForUpdate); + READ_BOOL_FIELD(hasRowSecurity); + READ_BOOL_FIELD(isReturn); + READ_NODE_FIELD(cteList); + READ_NODE_FIELD(rtable); + READ_NODE_FIELD(jointree); + READ_NODE_FIELD(targetList); + READ_ENUM_FIELD(override, OverridingKind); + READ_NODE_FIELD(onConflict); + READ_NODE_FIELD(returningList); + READ_NODE_FIELD(groupClause); + READ_BOOL_FIELD(groupDistinct); + READ_NODE_FIELD(groupingSets); + READ_NODE_FIELD(havingQual); + READ_NODE_FIELD(windowClause); + READ_NODE_FIELD(distinctClause); + READ_NODE_FIELD(sortClause); + READ_NODE_FIELD(limitOffset); + READ_NODE_FIELD(limitCount); + READ_ENUM_FIELD(limitOption, LimitOption); + READ_NODE_FIELD(rowMarks); + READ_NODE_FIELD(setOperations); + READ_NODE_FIELD(constraintDeps); + READ_NODE_FIELD(withCheckOptions); + READ_NODE_FIELD(mergeActionList); + READ_BOOL_FIELD(mergeUseOuterJoin); + READ_LOCATION_FIELD(stmt_location); + READ_INT_FIELD(stmt_len); + + READ_DONE(); +} + +/* + * _readNotifyStmt + */ +static NotifyStmt * +_readNotifyStmt(void) +{ + READ_LOCALS(NotifyStmt); + + READ_STRING_FIELD(conditionname); + READ_STRING_FIELD(payload); + + READ_DONE(); +} + +/* + * _readDeclareCursorStmt + */ +static DeclareCursorStmt * +_readDeclareCursorStmt(void) +{ + READ_LOCALS(DeclareCursorStmt); + + READ_STRING_FIELD(portalname); + READ_INT_FIELD(options); + READ_NODE_FIELD(query); + + READ_DONE(); +} + +/* + * _readWithCheckOption + */ +static WithCheckOption * +_readWithCheckOption(void) +{ + READ_LOCALS(WithCheckOption); + + READ_ENUM_FIELD(kind, WCOKind); + READ_STRING_FIELD(relname); + READ_STRING_FIELD(polname); + READ_NODE_FIELD(qual); + READ_BOOL_FIELD(cascaded); + + READ_DONE(); +} + +/* + * _readSortGroupClause + */ +static SortGroupClause * +_readSortGroupClause(void) +{ + READ_LOCALS(SortGroupClause); + + READ_UINT_FIELD(tleSortGroupRef); + READ_OID_FIELD(eqop); + READ_OID_FIELD(sortop); + READ_BOOL_FIELD(nulls_first); + READ_BOOL_FIELD(hashable); + + READ_DONE(); +} + +/* + * _readGroupingSet + */ +static GroupingSet * +_readGroupingSet(void) +{ + READ_LOCALS(GroupingSet); + + READ_ENUM_FIELD(kind, GroupingSetKind); + READ_NODE_FIELD(content); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readWindowClause + */ +static WindowClause * +_readWindowClause(void) +{ + READ_LOCALS(WindowClause); + + READ_STRING_FIELD(name); + READ_STRING_FIELD(refname); + READ_NODE_FIELD(partitionClause); + READ_NODE_FIELD(orderClause); + READ_INT_FIELD(frameOptions); + READ_NODE_FIELD(startOffset); + READ_NODE_FIELD(endOffset); + READ_NODE_FIELD(runCondition); + READ_OID_FIELD(startInRangeFunc); + READ_OID_FIELD(endInRangeFunc); + READ_OID_FIELD(inRangeColl); + READ_BOOL_FIELD(inRangeAsc); + READ_BOOL_FIELD(inRangeNullsFirst); + READ_UINT_FIELD(winref); + READ_BOOL_FIELD(copiedOrder); + + READ_DONE(); +} + +/* + * _readRowMarkClause + */ +static RowMarkClause * +_readRowMarkClause(void) +{ + READ_LOCALS(RowMarkClause); + + READ_UINT_FIELD(rti); + READ_ENUM_FIELD(strength, LockClauseStrength); + READ_ENUM_FIELD(waitPolicy, LockWaitPolicy); + READ_BOOL_FIELD(pushedDown); + + READ_DONE(); +} + +/* + * _readCTESearchClause + */ +static CTESearchClause * +_readCTESearchClause(void) +{ + READ_LOCALS(CTESearchClause); + + READ_NODE_FIELD(search_col_list); + READ_BOOL_FIELD(search_breadth_first); + READ_STRING_FIELD(search_seq_column); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCTECycleClause + */ +static CTECycleClause * +_readCTECycleClause(void) +{ + READ_LOCALS(CTECycleClause); + + READ_NODE_FIELD(cycle_col_list); + READ_STRING_FIELD(cycle_mark_column); + READ_NODE_FIELD(cycle_mark_value); + READ_NODE_FIELD(cycle_mark_default); + READ_STRING_FIELD(cycle_path_column); + READ_LOCATION_FIELD(location); + READ_OID_FIELD(cycle_mark_type); + READ_INT_FIELD(cycle_mark_typmod); + READ_OID_FIELD(cycle_mark_collation); + READ_OID_FIELD(cycle_mark_neop); + + READ_DONE(); +} + +/* + * _readCommonTableExpr + */ +static CommonTableExpr * +_readCommonTableExpr(void) +{ + READ_LOCALS(CommonTableExpr); + + READ_STRING_FIELD(ctename); + READ_NODE_FIELD(aliascolnames); + READ_ENUM_FIELD(ctematerialized, CTEMaterialize); + READ_NODE_FIELD(ctequery); + READ_NODE_FIELD(search_clause); + READ_NODE_FIELD(cycle_clause); + READ_LOCATION_FIELD(location); + READ_BOOL_FIELD(cterecursive); + READ_INT_FIELD(cterefcount); + READ_NODE_FIELD(ctecolnames); + READ_NODE_FIELD(ctecoltypes); + READ_NODE_FIELD(ctecoltypmods); + READ_NODE_FIELD(ctecolcollations); + + READ_DONE(); +} + +/* + * _readMergeWhenClause + */ +static MergeWhenClause * +_readMergeWhenClause(void) +{ + READ_LOCALS(MergeWhenClause); + + READ_BOOL_FIELD(matched); + READ_ENUM_FIELD(commandType, CmdType); + READ_ENUM_FIELD(override, OverridingKind); + READ_NODE_FIELD(condition); + READ_NODE_FIELD(targetList); + READ_NODE_FIELD(values); + + READ_DONE(); +} + +/* + * _readMergeAction + */ +static MergeAction * +_readMergeAction(void) +{ + READ_LOCALS(MergeAction); + + READ_BOOL_FIELD(matched); + READ_ENUM_FIELD(commandType, CmdType); + READ_ENUM_FIELD(override, OverridingKind); + READ_NODE_FIELD(qual); + READ_NODE_FIELD(targetList); + READ_NODE_FIELD(updateColnos); + + READ_DONE(); +} + +/* + * _readSetOperationStmt + */ +static SetOperationStmt * +_readSetOperationStmt(void) +{ + READ_LOCALS(SetOperationStmt); + + READ_ENUM_FIELD(op, SetOperation); + READ_BOOL_FIELD(all); + READ_NODE_FIELD(larg); + READ_NODE_FIELD(rarg); + READ_NODE_FIELD(colTypes); + READ_NODE_FIELD(colTypmods); + READ_NODE_FIELD(colCollations); + READ_NODE_FIELD(groupClauses); + + READ_DONE(); +} + + +/* + * Stuff from primnodes.h. + */ + +static Alias * +_readAlias(void) +{ + READ_LOCALS(Alias); + + READ_STRING_FIELD(aliasname); + READ_NODE_FIELD(colnames); + + READ_DONE(); +} + +static RangeVar * +_readRangeVar(void) +{ + READ_LOCALS(RangeVar); + + local_node->catalogname = NULL; /* not currently saved in output format */ + + READ_STRING_FIELD(schemaname); + READ_STRING_FIELD(relname); + READ_BOOL_FIELD(inh); + READ_CHAR_FIELD(relpersistence); + READ_NODE_FIELD(alias); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readTableFunc + */ +static TableFunc * +_readTableFunc(void) +{ + READ_LOCALS(TableFunc); + + READ_NODE_FIELD(ns_uris); + READ_NODE_FIELD(ns_names); + READ_NODE_FIELD(docexpr); + READ_NODE_FIELD(rowexpr); + READ_NODE_FIELD(colnames); + READ_NODE_FIELD(coltypes); + READ_NODE_FIELD(coltypmods); + READ_NODE_FIELD(colcollations); + READ_NODE_FIELD(colexprs); + READ_NODE_FIELD(coldefexprs); + READ_BITMAPSET_FIELD(notnulls); + READ_INT_FIELD(ordinalitycol); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +static IntoClause * +_readIntoClause(void) +{ + READ_LOCALS(IntoClause); + + READ_NODE_FIELD(rel); + READ_NODE_FIELD(colNames); + READ_STRING_FIELD(accessMethod); + READ_NODE_FIELD(options); + READ_ENUM_FIELD(onCommit, OnCommitAction); + READ_STRING_FIELD(tableSpaceName); + READ_NODE_FIELD(viewQuery); + READ_BOOL_FIELD(skipData); + + READ_DONE(); +} + +/* + * _readVar + */ +static Var * +_readVar(void) +{ + READ_LOCALS(Var); + + READ_INT_FIELD(varno); + READ_INT_FIELD(varattno); + READ_OID_FIELD(vartype); + READ_INT_FIELD(vartypmod); + READ_OID_FIELD(varcollid); + READ_UINT_FIELD(varlevelsup); + READ_UINT_FIELD(varnosyn); + READ_INT_FIELD(varattnosyn); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readConst + */ +static Const * +_readConst(void) +{ + READ_LOCALS(Const); + + READ_OID_FIELD(consttype); + READ_INT_FIELD(consttypmod); + READ_OID_FIELD(constcollid); + READ_INT_FIELD(constlen); + READ_BOOL_FIELD(constbyval); + READ_BOOL_FIELD(constisnull); + READ_LOCATION_FIELD(location); + + token = pg_strtok(&length); /* skip :constvalue */ + if (local_node->constisnull) + token = pg_strtok(&length); /* skip "<>" */ + else + local_node->constvalue = readDatum(local_node->constbyval); + + READ_DONE(); +} + +/* + * _readParam + */ +static Param * +_readParam(void) +{ + READ_LOCALS(Param); + + READ_ENUM_FIELD(paramkind, ParamKind); + READ_INT_FIELD(paramid); + READ_OID_FIELD(paramtype); + READ_INT_FIELD(paramtypmod); + READ_OID_FIELD(paramcollid); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readAggref + */ +static Aggref * +_readAggref(void) +{ + READ_LOCALS(Aggref); + + READ_OID_FIELD(aggfnoid); + READ_OID_FIELD(aggtype); + READ_OID_FIELD(aggcollid); + READ_OID_FIELD(inputcollid); + READ_OID_FIELD(aggtranstype); + READ_NODE_FIELD(aggargtypes); + READ_NODE_FIELD(aggdirectargs); + READ_NODE_FIELD(args); + READ_NODE_FIELD(aggorder); + READ_NODE_FIELD(aggdistinct); + READ_NODE_FIELD(aggfilter); + READ_BOOL_FIELD(aggstar); + READ_BOOL_FIELD(aggvariadic); + READ_CHAR_FIELD(aggkind); + READ_UINT_FIELD(agglevelsup); + READ_ENUM_FIELD(aggsplit, AggSplit); + READ_INT_FIELD(aggno); + READ_INT_FIELD(aggtransno); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readGroupingFunc + */ +static GroupingFunc * +_readGroupingFunc(void) +{ + READ_LOCALS(GroupingFunc); + + READ_NODE_FIELD(args); + READ_NODE_FIELD(refs); + READ_NODE_FIELD(cols); + READ_UINT_FIELD(agglevelsup); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readWindowFunc + */ +static WindowFunc * +_readWindowFunc(void) +{ + READ_LOCALS(WindowFunc); + + READ_OID_FIELD(winfnoid); + READ_OID_FIELD(wintype); + READ_OID_FIELD(wincollid); + READ_OID_FIELD(inputcollid); + READ_NODE_FIELD(args); + READ_NODE_FIELD(aggfilter); + READ_UINT_FIELD(winref); + READ_BOOL_FIELD(winstar); + READ_BOOL_FIELD(winagg); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readSubscriptingRef + */ +static SubscriptingRef * +_readSubscriptingRef(void) +{ + READ_LOCALS(SubscriptingRef); + + READ_OID_FIELD(refcontainertype); + READ_OID_FIELD(refelemtype); + READ_OID_FIELD(refrestype); + READ_INT_FIELD(reftypmod); + READ_OID_FIELD(refcollid); + READ_NODE_FIELD(refupperindexpr); + READ_NODE_FIELD(reflowerindexpr); + READ_NODE_FIELD(refexpr); + READ_NODE_FIELD(refassgnexpr); + + READ_DONE(); +} + +/* + * _readFuncExpr + */ +static FuncExpr * +_readFuncExpr(void) +{ + READ_LOCALS(FuncExpr); + + READ_OID_FIELD(funcid); + READ_OID_FIELD(funcresulttype); + READ_BOOL_FIELD(funcretset); + READ_BOOL_FIELD(funcvariadic); + READ_ENUM_FIELD(funcformat, CoercionForm); + READ_OID_FIELD(funccollid); + READ_OID_FIELD(inputcollid); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readNamedArgExpr + */ +static NamedArgExpr * +_readNamedArgExpr(void) +{ + READ_LOCALS(NamedArgExpr); + + READ_NODE_FIELD(arg); + READ_STRING_FIELD(name); + READ_INT_FIELD(argnumber); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readOpExpr + */ +static OpExpr * +_readOpExpr(void) +{ + READ_LOCALS(OpExpr); + + READ_OID_FIELD(opno); + READ_OID_FIELD(opfuncid); + READ_OID_FIELD(opresulttype); + READ_BOOL_FIELD(opretset); + READ_OID_FIELD(opcollid); + READ_OID_FIELD(inputcollid); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readDistinctExpr + */ +static DistinctExpr * +_readDistinctExpr(void) +{ + READ_LOCALS(DistinctExpr); + + READ_OID_FIELD(opno); + READ_OID_FIELD(opfuncid); + READ_OID_FIELD(opresulttype); + READ_BOOL_FIELD(opretset); + READ_OID_FIELD(opcollid); + READ_OID_FIELD(inputcollid); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readNullIfExpr + */ +static NullIfExpr * +_readNullIfExpr(void) +{ + READ_LOCALS(NullIfExpr); + + READ_OID_FIELD(opno); + READ_OID_FIELD(opfuncid); + READ_OID_FIELD(opresulttype); + READ_BOOL_FIELD(opretset); + READ_OID_FIELD(opcollid); + READ_OID_FIELD(inputcollid); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readScalarArrayOpExpr + */ +static ScalarArrayOpExpr * +_readScalarArrayOpExpr(void) +{ + READ_LOCALS(ScalarArrayOpExpr); + + READ_OID_FIELD(opno); + READ_OID_FIELD(opfuncid); + READ_OID_FIELD(hashfuncid); + READ_OID_FIELD(negfuncid); + READ_BOOL_FIELD(useOr); + READ_OID_FIELD(inputcollid); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readBoolExpr + */ +static BoolExpr * +_readBoolExpr(void) +{ + READ_LOCALS(BoolExpr); + + /* do-it-yourself enum representation */ + token = pg_strtok(&length); /* skip :boolop */ + token = pg_strtok(&length); /* get field value */ + if (strncmp(token, "and", 3) == 0) + local_node->boolop = AND_EXPR; + else if (strncmp(token, "or", 2) == 0) + local_node->boolop = OR_EXPR; + else if (strncmp(token, "not", 3) == 0) + local_node->boolop = NOT_EXPR; + else + elog(ERROR, "unrecognized boolop \"%.*s\"", length, token); + + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readSubLink + */ +static SubLink * +_readSubLink(void) +{ + READ_LOCALS(SubLink); + + READ_ENUM_FIELD(subLinkType, SubLinkType); + READ_INT_FIELD(subLinkId); + READ_NODE_FIELD(testexpr); + READ_NODE_FIELD(operName); + READ_NODE_FIELD(subselect); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readSubPlan is not needed since it doesn't appear in stored rules. + */ + +/* + * _readFieldSelect + */ +static FieldSelect * +_readFieldSelect(void) +{ + READ_LOCALS(FieldSelect); + + READ_NODE_FIELD(arg); + READ_INT_FIELD(fieldnum); + READ_OID_FIELD(resulttype); + READ_INT_FIELD(resulttypmod); + READ_OID_FIELD(resultcollid); + + READ_DONE(); +} + +/* + * _readFieldStore + */ +static FieldStore * +_readFieldStore(void) +{ + READ_LOCALS(FieldStore); + + READ_NODE_FIELD(arg); + READ_NODE_FIELD(newvals); + READ_NODE_FIELD(fieldnums); + READ_OID_FIELD(resulttype); + + READ_DONE(); +} + +/* + * _readRelabelType + */ +static RelabelType * +_readRelabelType(void) +{ + READ_LOCALS(RelabelType); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(resulttype); + READ_INT_FIELD(resulttypmod); + READ_OID_FIELD(resultcollid); + READ_ENUM_FIELD(relabelformat, CoercionForm); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCoerceViaIO + */ +static CoerceViaIO * +_readCoerceViaIO(void) +{ + READ_LOCALS(CoerceViaIO); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(resulttype); + READ_OID_FIELD(resultcollid); + READ_ENUM_FIELD(coerceformat, CoercionForm); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readArrayCoerceExpr + */ +static ArrayCoerceExpr * +_readArrayCoerceExpr(void) +{ + READ_LOCALS(ArrayCoerceExpr); + + READ_NODE_FIELD(arg); + READ_NODE_FIELD(elemexpr); + READ_OID_FIELD(resulttype); + READ_INT_FIELD(resulttypmod); + READ_OID_FIELD(resultcollid); + READ_ENUM_FIELD(coerceformat, CoercionForm); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readConvertRowtypeExpr + */ +static ConvertRowtypeExpr * +_readConvertRowtypeExpr(void) +{ + READ_LOCALS(ConvertRowtypeExpr); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(resulttype); + READ_ENUM_FIELD(convertformat, CoercionForm); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCollateExpr + */ +static CollateExpr * +_readCollateExpr(void) +{ + READ_LOCALS(CollateExpr); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(collOid); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCaseExpr + */ +static CaseExpr * +_readCaseExpr(void) +{ + READ_LOCALS(CaseExpr); + + READ_OID_FIELD(casetype); + READ_OID_FIELD(casecollid); + READ_NODE_FIELD(arg); + READ_NODE_FIELD(args); + READ_NODE_FIELD(defresult); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCaseWhen + */ +static CaseWhen * +_readCaseWhen(void) +{ + READ_LOCALS(CaseWhen); + + READ_NODE_FIELD(expr); + READ_NODE_FIELD(result); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCaseTestExpr + */ +static CaseTestExpr * +_readCaseTestExpr(void) +{ + READ_LOCALS(CaseTestExpr); + + READ_OID_FIELD(typeId); + READ_INT_FIELD(typeMod); + READ_OID_FIELD(collation); + + READ_DONE(); +} + +/* + * _readArrayExpr + */ +static ArrayExpr * +_readArrayExpr(void) +{ + READ_LOCALS(ArrayExpr); + + READ_OID_FIELD(array_typeid); + READ_OID_FIELD(array_collid); + READ_OID_FIELD(element_typeid); + READ_NODE_FIELD(elements); + READ_BOOL_FIELD(multidims); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readRowExpr + */ +static RowExpr * +_readRowExpr(void) +{ + READ_LOCALS(RowExpr); + + READ_NODE_FIELD(args); + READ_OID_FIELD(row_typeid); + READ_ENUM_FIELD(row_format, CoercionForm); + READ_NODE_FIELD(colnames); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readRowCompareExpr + */ +static RowCompareExpr * +_readRowCompareExpr(void) +{ + READ_LOCALS(RowCompareExpr); + + READ_ENUM_FIELD(rctype, RowCompareType); + READ_NODE_FIELD(opnos); + READ_NODE_FIELD(opfamilies); + READ_NODE_FIELD(inputcollids); + READ_NODE_FIELD(largs); + READ_NODE_FIELD(rargs); + + READ_DONE(); +} + +/* + * _readCoalesceExpr + */ +static CoalesceExpr * +_readCoalesceExpr(void) +{ + READ_LOCALS(CoalesceExpr); + + READ_OID_FIELD(coalescetype); + READ_OID_FIELD(coalescecollid); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readMinMaxExpr + */ +static MinMaxExpr * +_readMinMaxExpr(void) +{ + READ_LOCALS(MinMaxExpr); + + READ_OID_FIELD(minmaxtype); + READ_OID_FIELD(minmaxcollid); + READ_OID_FIELD(inputcollid); + READ_ENUM_FIELD(op, MinMaxOp); + READ_NODE_FIELD(args); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readSQLValueFunction + */ +static SQLValueFunction * +_readSQLValueFunction(void) +{ + READ_LOCALS(SQLValueFunction); + + READ_ENUM_FIELD(op, SQLValueFunctionOp); + READ_OID_FIELD(type); + READ_INT_FIELD(typmod); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readXmlExpr + */ +static XmlExpr * +_readXmlExpr(void) +{ + READ_LOCALS(XmlExpr); + + READ_ENUM_FIELD(op, XmlExprOp); + READ_STRING_FIELD(name); + READ_NODE_FIELD(named_args); + READ_NODE_FIELD(arg_names); + READ_NODE_FIELD(args); + READ_ENUM_FIELD(xmloption, XmlOptionType); + READ_OID_FIELD(type); + READ_INT_FIELD(typmod); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readNullTest + */ +static NullTest * +_readNullTest(void) +{ + READ_LOCALS(NullTest); + + READ_NODE_FIELD(arg); + READ_ENUM_FIELD(nulltesttype, NullTestType); + READ_BOOL_FIELD(argisrow); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readBooleanTest + */ +static BooleanTest * +_readBooleanTest(void) +{ + READ_LOCALS(BooleanTest); + + READ_NODE_FIELD(arg); + READ_ENUM_FIELD(booltesttype, BoolTestType); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCoerceToDomain + */ +static CoerceToDomain * +_readCoerceToDomain(void) +{ + READ_LOCALS(CoerceToDomain); + + READ_NODE_FIELD(arg); + READ_OID_FIELD(resulttype); + READ_INT_FIELD(resulttypmod); + READ_OID_FIELD(resultcollid); + READ_ENUM_FIELD(coercionformat, CoercionForm); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCoerceToDomainValue + */ +static CoerceToDomainValue * +_readCoerceToDomainValue(void) +{ + READ_LOCALS(CoerceToDomainValue); + + READ_OID_FIELD(typeId); + READ_INT_FIELD(typeMod); + READ_OID_FIELD(collation); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readSetToDefault + */ +static SetToDefault * +_readSetToDefault(void) +{ + READ_LOCALS(SetToDefault); + + READ_OID_FIELD(typeId); + READ_INT_FIELD(typeMod); + READ_OID_FIELD(collation); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readCurrentOfExpr + */ +static CurrentOfExpr * +_readCurrentOfExpr(void) +{ + READ_LOCALS(CurrentOfExpr); + + READ_UINT_FIELD(cvarno); + READ_STRING_FIELD(cursor_name); + READ_INT_FIELD(cursor_param); + + READ_DONE(); +} + +/* + * _readNextValueExpr + */ +static NextValueExpr * +_readNextValueExpr(void) +{ + READ_LOCALS(NextValueExpr); + + READ_OID_FIELD(seqid); + READ_OID_FIELD(typeId); + + READ_DONE(); +} + +/* + * _readInferenceElem + */ +static InferenceElem * +_readInferenceElem(void) +{ + READ_LOCALS(InferenceElem); + + READ_NODE_FIELD(expr); + READ_OID_FIELD(infercollid); + READ_OID_FIELD(inferopclass); + + READ_DONE(); +} + +/* + * _readTargetEntry + */ +static TargetEntry * +_readTargetEntry(void) +{ + READ_LOCALS(TargetEntry); + + READ_NODE_FIELD(expr); + READ_INT_FIELD(resno); + READ_STRING_FIELD(resname); + READ_UINT_FIELD(ressortgroupref); + READ_OID_FIELD(resorigtbl); + READ_INT_FIELD(resorigcol); + READ_BOOL_FIELD(resjunk); + + READ_DONE(); +} + +/* + * _readRangeTblRef + */ +static RangeTblRef * +_readRangeTblRef(void) +{ + READ_LOCALS(RangeTblRef); + + READ_INT_FIELD(rtindex); + + READ_DONE(); +} + +/* + * _readJoinExpr + */ +static JoinExpr * +_readJoinExpr(void) +{ + READ_LOCALS(JoinExpr); + + READ_ENUM_FIELD(jointype, JoinType); + READ_BOOL_FIELD(isNatural); + READ_NODE_FIELD(larg); + READ_NODE_FIELD(rarg); + READ_NODE_FIELD(usingClause); + READ_NODE_FIELD(join_using_alias); + READ_NODE_FIELD(quals); + READ_NODE_FIELD(alias); + READ_INT_FIELD(rtindex); + + READ_DONE(); +} + +/* + * _readFromExpr + */ +static FromExpr * +_readFromExpr(void) +{ + READ_LOCALS(FromExpr); + + READ_NODE_FIELD(fromlist); + READ_NODE_FIELD(quals); + + READ_DONE(); +} + +/* + * _readOnConflictExpr + */ +static OnConflictExpr * +_readOnConflictExpr(void) +{ + READ_LOCALS(OnConflictExpr); + + READ_ENUM_FIELD(action, OnConflictAction); + READ_NODE_FIELD(arbiterElems); + READ_NODE_FIELD(arbiterWhere); + READ_OID_FIELD(constraint); + READ_NODE_FIELD(onConflictSet); + READ_NODE_FIELD(onConflictWhere); + READ_INT_FIELD(exclRelIndex); + READ_NODE_FIELD(exclRelTlist); + + READ_DONE(); +} + +/* + * Stuff from pathnodes.h. + * + * Mostly we don't need to read planner nodes back in again, but some + * of these also end up in plan trees. + */ + +/* + * _readAppendRelInfo + */ +static AppendRelInfo * +_readAppendRelInfo(void) +{ + READ_LOCALS(AppendRelInfo); + + READ_UINT_FIELD(parent_relid); + READ_UINT_FIELD(child_relid); + READ_OID_FIELD(parent_reltype); + READ_OID_FIELD(child_reltype); + READ_NODE_FIELD(translated_vars); + READ_INT_FIELD(num_child_cols); + READ_ATTRNUMBER_ARRAY(parent_colnos, local_node->num_child_cols); + READ_OID_FIELD(parent_reloid); + + READ_DONE(); +} + +/* + * Stuff from parsenodes.h. + */ + +/* + * _readRangeTblEntry + */ +static RangeTblEntry * +_readRangeTblEntry(void) +{ + READ_LOCALS(RangeTblEntry); + + /* put alias + eref first to make dump more legible */ + READ_NODE_FIELD(alias); + READ_NODE_FIELD(eref); + READ_ENUM_FIELD(rtekind, RTEKind); + + switch (local_node->rtekind) + { + case RTE_RELATION: + READ_OID_FIELD(relid); + READ_CHAR_FIELD(relkind); + READ_INT_FIELD(rellockmode); + READ_NODE_FIELD(tablesample); + break; + case RTE_SUBQUERY: + READ_NODE_FIELD(subquery); + READ_BOOL_FIELD(security_barrier); + break; + case RTE_JOIN: + READ_ENUM_FIELD(jointype, JoinType); + READ_INT_FIELD(joinmergedcols); + READ_NODE_FIELD(joinaliasvars); + READ_NODE_FIELD(joinleftcols); + READ_NODE_FIELD(joinrightcols); + READ_NODE_FIELD(join_using_alias); + break; + case RTE_FUNCTION: + READ_NODE_FIELD(functions); + READ_BOOL_FIELD(funcordinality); + break; + case RTE_TABLEFUNC: + READ_NODE_FIELD(tablefunc); + /* The RTE must have a copy of the column type info, if any */ + if (local_node->tablefunc) + { + TableFunc *tf = local_node->tablefunc; + + local_node->coltypes = tf->coltypes; + local_node->coltypmods = tf->coltypmods; + local_node->colcollations = tf->colcollations; + } + break; + case RTE_VALUES: + READ_NODE_FIELD(values_lists); + READ_NODE_FIELD(coltypes); + READ_NODE_FIELD(coltypmods); + READ_NODE_FIELD(colcollations); + break; + case RTE_CTE: + READ_STRING_FIELD(ctename); + READ_UINT_FIELD(ctelevelsup); + READ_BOOL_FIELD(self_reference); + READ_NODE_FIELD(coltypes); + READ_NODE_FIELD(coltypmods); + READ_NODE_FIELD(colcollations); + break; + case RTE_NAMEDTUPLESTORE: + READ_STRING_FIELD(enrname); + READ_FLOAT_FIELD(enrtuples); + READ_OID_FIELD(relid); + READ_NODE_FIELD(coltypes); + READ_NODE_FIELD(coltypmods); + READ_NODE_FIELD(colcollations); + break; + case RTE_RESULT: + /* no extra fields */ + break; + default: + elog(ERROR, "unrecognized RTE kind: %d", + (int) local_node->rtekind); + break; + } + + READ_BOOL_FIELD(lateral); + READ_BOOL_FIELD(inh); + READ_BOOL_FIELD(inFromCl); + READ_UINT_FIELD(requiredPerms); + READ_OID_FIELD(checkAsUser); + READ_BITMAPSET_FIELD(selectedCols); + READ_BITMAPSET_FIELD(insertedCols); + READ_BITMAPSET_FIELD(updatedCols); + READ_BITMAPSET_FIELD(extraUpdatedCols); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +/* + * _readRangeTblFunction + */ +static RangeTblFunction * +_readRangeTblFunction(void) +{ + READ_LOCALS(RangeTblFunction); + + READ_NODE_FIELD(funcexpr); + READ_INT_FIELD(funccolcount); + READ_NODE_FIELD(funccolnames); + READ_NODE_FIELD(funccoltypes); + READ_NODE_FIELD(funccoltypmods); + READ_NODE_FIELD(funccolcollations); + READ_BITMAPSET_FIELD(funcparams); + + READ_DONE(); +} + +/* + * _readTableSampleClause + */ +static TableSampleClause * +_readTableSampleClause(void) +{ + READ_LOCALS(TableSampleClause); + + READ_OID_FIELD(tsmhandler); + READ_NODE_FIELD(args); + READ_NODE_FIELD(repeatable); + + READ_DONE(); +} + +/* + * _readDefElem + */ +static DefElem * +_readDefElem(void) +{ + READ_LOCALS(DefElem); + + READ_STRING_FIELD(defnamespace); + READ_STRING_FIELD(defname); + READ_NODE_FIELD(arg); + READ_ENUM_FIELD(defaction, DefElemAction); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * Stuff from plannodes.h. + */ + +/* + * _readPlannedStmt + */ +static PlannedStmt * +_readPlannedStmt(void) +{ + READ_LOCALS(PlannedStmt); + + READ_ENUM_FIELD(commandType, CmdType); + READ_UINT64_FIELD(queryId); + READ_BOOL_FIELD(hasReturning); + READ_BOOL_FIELD(hasModifyingCTE); + READ_BOOL_FIELD(canSetTag); + READ_BOOL_FIELD(transientPlan); + READ_BOOL_FIELD(dependsOnRole); + READ_BOOL_FIELD(parallelModeNeeded); + READ_INT_FIELD(jitFlags); + READ_NODE_FIELD(planTree); + READ_NODE_FIELD(rtable); + READ_NODE_FIELD(resultRelations); + READ_NODE_FIELD(appendRelations); + READ_NODE_FIELD(subplans); + READ_BITMAPSET_FIELD(rewindPlanIDs); + READ_NODE_FIELD(rowMarks); + READ_NODE_FIELD(relationOids); + READ_NODE_FIELD(invalItems); + READ_NODE_FIELD(paramExecTypes); + READ_NODE_FIELD(utilityStmt); + READ_LOCATION_FIELD(stmt_location); + READ_INT_FIELD(stmt_len); + + READ_DONE(); +} + +/* + * ReadCommonPlan + * Assign the basic stuff of all nodes that inherit from Plan + */ +static void +ReadCommonPlan(Plan *local_node) +{ + READ_TEMP_LOCALS(); + + READ_FLOAT_FIELD(startup_cost); + READ_FLOAT_FIELD(total_cost); + READ_FLOAT_FIELD(plan_rows); + READ_INT_FIELD(plan_width); + READ_BOOL_FIELD(parallel_aware); + READ_BOOL_FIELD(parallel_safe); + READ_BOOL_FIELD(async_capable); + READ_INT_FIELD(plan_node_id); + READ_NODE_FIELD(targetlist); + READ_NODE_FIELD(qual); + READ_NODE_FIELD(lefttree); + READ_NODE_FIELD(righttree); + READ_NODE_FIELD(initPlan); + READ_BITMAPSET_FIELD(extParam); + READ_BITMAPSET_FIELD(allParam); +} + +/* + * _readPlan + */ +static Plan * +_readPlan(void) +{ + READ_LOCALS_NO_FIELDS(Plan); + + ReadCommonPlan(local_node); + + READ_DONE(); +} + +/* + * _readResult + */ +static Result * +_readResult(void) +{ + READ_LOCALS(Result); + + ReadCommonPlan(&local_node->plan); + + READ_NODE_FIELD(resconstantqual); + + READ_DONE(); +} + +/* + * _readProjectSet + */ +static ProjectSet * +_readProjectSet(void) +{ + READ_LOCALS_NO_FIELDS(ProjectSet); + + ReadCommonPlan(&local_node->plan); + + READ_DONE(); +} + +/* + * _readModifyTable + */ +static ModifyTable * +_readModifyTable(void) +{ + READ_LOCALS(ModifyTable); + + ReadCommonPlan(&local_node->plan); + + READ_ENUM_FIELD(operation, CmdType); + READ_BOOL_FIELD(canSetTag); + READ_UINT_FIELD(nominalRelation); + READ_UINT_FIELD(rootRelation); + READ_BOOL_FIELD(partColsUpdated); + READ_NODE_FIELD(resultRelations); + READ_NODE_FIELD(updateColnosLists); + READ_NODE_FIELD(withCheckOptionLists); + READ_NODE_FIELD(returningLists); + READ_NODE_FIELD(fdwPrivLists); + READ_BITMAPSET_FIELD(fdwDirectModifyPlans); + READ_NODE_FIELD(rowMarks); + READ_INT_FIELD(epqParam); + READ_ENUM_FIELD(onConflictAction, OnConflictAction); + READ_NODE_FIELD(arbiterIndexes); + READ_NODE_FIELD(onConflictSet); + READ_NODE_FIELD(onConflictCols); + READ_NODE_FIELD(onConflictWhere); + READ_UINT_FIELD(exclRelRTI); + READ_NODE_FIELD(exclRelTlist); + READ_NODE_FIELD(mergeActionLists); + + READ_DONE(); +} + +/* + * _readAppend + */ +static Append * +_readAppend(void) +{ + READ_LOCALS(Append); + + ReadCommonPlan(&local_node->plan); + + READ_BITMAPSET_FIELD(apprelids); + READ_NODE_FIELD(appendplans); + READ_INT_FIELD(nasyncplans); + READ_INT_FIELD(first_partial_plan); + READ_NODE_FIELD(part_prune_info); + + READ_DONE(); +} + +/* + * _readMergeAppend + */ +static MergeAppend * +_readMergeAppend(void) +{ + READ_LOCALS(MergeAppend); + + ReadCommonPlan(&local_node->plan); + + READ_BITMAPSET_FIELD(apprelids); + READ_NODE_FIELD(mergeplans); + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(sortColIdx, local_node->numCols); + READ_OID_ARRAY(sortOperators, local_node->numCols); + READ_OID_ARRAY(collations, local_node->numCols); + READ_BOOL_ARRAY(nullsFirst, local_node->numCols); + READ_NODE_FIELD(part_prune_info); + + READ_DONE(); +} + +/* + * _readRecursiveUnion + */ +static RecursiveUnion * +_readRecursiveUnion(void) +{ + READ_LOCALS(RecursiveUnion); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(wtParam); + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(dupColIdx, local_node->numCols); + READ_OID_ARRAY(dupOperators, local_node->numCols); + READ_OID_ARRAY(dupCollations, local_node->numCols); + READ_LONG_FIELD(numGroups); + + READ_DONE(); +} + +/* + * _readBitmapAnd + */ +static BitmapAnd * +_readBitmapAnd(void) +{ + READ_LOCALS(BitmapAnd); + + ReadCommonPlan(&local_node->plan); + + READ_NODE_FIELD(bitmapplans); + + READ_DONE(); +} + +/* + * _readBitmapOr + */ +static BitmapOr * +_readBitmapOr(void) +{ + READ_LOCALS(BitmapOr); + + ReadCommonPlan(&local_node->plan); + + READ_BOOL_FIELD(isshared); + READ_NODE_FIELD(bitmapplans); + + READ_DONE(); +} + +/* + * ReadCommonScan + * Assign the basic stuff of all nodes that inherit from Scan + */ +static void +ReadCommonScan(Scan *local_node) +{ + READ_TEMP_LOCALS(); + + ReadCommonPlan(&local_node->plan); + + READ_UINT_FIELD(scanrelid); +} + +/* + * _readScan + */ +static Scan * +_readScan(void) +{ + READ_LOCALS_NO_FIELDS(Scan); + + ReadCommonScan(local_node); + + READ_DONE(); +} + +/* + * _readSeqScan + */ +static SeqScan * +_readSeqScan(void) +{ + READ_LOCALS_NO_FIELDS(SeqScan); + + ReadCommonScan(&local_node->scan); + + READ_DONE(); +} + +/* + * _readSampleScan + */ +static SampleScan * +_readSampleScan(void) +{ + READ_LOCALS(SampleScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(tablesample); + + READ_DONE(); +} + +/* + * _readIndexScan + */ +static IndexScan * +_readIndexScan(void) +{ + READ_LOCALS(IndexScan); + + ReadCommonScan(&local_node->scan); + + READ_OID_FIELD(indexid); + READ_NODE_FIELD(indexqual); + READ_NODE_FIELD(indexqualorig); + READ_NODE_FIELD(indexorderby); + READ_NODE_FIELD(indexorderbyorig); + READ_NODE_FIELD(indexorderbyops); + READ_ENUM_FIELD(indexorderdir, ScanDirection); + + READ_DONE(); +} + +/* + * _readIndexOnlyScan + */ +static IndexOnlyScan * +_readIndexOnlyScan(void) +{ + READ_LOCALS(IndexOnlyScan); + + ReadCommonScan(&local_node->scan); + + READ_OID_FIELD(indexid); + READ_NODE_FIELD(indexqual); + READ_NODE_FIELD(recheckqual); + READ_NODE_FIELD(indexorderby); + READ_NODE_FIELD(indextlist); + READ_ENUM_FIELD(indexorderdir, ScanDirection); + + READ_DONE(); +} + +/* + * _readBitmapIndexScan + */ +static BitmapIndexScan * +_readBitmapIndexScan(void) +{ + READ_LOCALS(BitmapIndexScan); + + ReadCommonScan(&local_node->scan); + + READ_OID_FIELD(indexid); + READ_BOOL_FIELD(isshared); + READ_NODE_FIELD(indexqual); + READ_NODE_FIELD(indexqualorig); + + READ_DONE(); +} + +/* + * _readBitmapHeapScan + */ +static BitmapHeapScan * +_readBitmapHeapScan(void) +{ + READ_LOCALS(BitmapHeapScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(bitmapqualorig); + + READ_DONE(); +} + +/* + * _readTidScan + */ +static TidScan * +_readTidScan(void) +{ + READ_LOCALS(TidScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(tidquals); + + READ_DONE(); +} + +/* + * _readTidRangeScan + */ +static TidRangeScan * +_readTidRangeScan(void) +{ + READ_LOCALS(TidRangeScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(tidrangequals); + + READ_DONE(); +} + +/* + * _readSubqueryScan + */ +static SubqueryScan * +_readSubqueryScan(void) +{ + READ_LOCALS(SubqueryScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(subplan); + READ_ENUM_FIELD(scanstatus, SubqueryScanStatus); + + READ_DONE(); +} + +/* + * _readFunctionScan + */ +static FunctionScan * +_readFunctionScan(void) +{ + READ_LOCALS(FunctionScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(functions); + READ_BOOL_FIELD(funcordinality); + + READ_DONE(); +} + +/* + * _readValuesScan + */ +static ValuesScan * +_readValuesScan(void) +{ + READ_LOCALS(ValuesScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(values_lists); + + READ_DONE(); +} + +/* + * _readTableFuncScan + */ +static TableFuncScan * +_readTableFuncScan(void) +{ + READ_LOCALS(TableFuncScan); + + ReadCommonScan(&local_node->scan); + + READ_NODE_FIELD(tablefunc); + + READ_DONE(); +} + +/* + * _readCteScan + */ +static CteScan * +_readCteScan(void) +{ + READ_LOCALS(CteScan); + + ReadCommonScan(&local_node->scan); + + READ_INT_FIELD(ctePlanId); + READ_INT_FIELD(cteParam); + + READ_DONE(); +} + +/* + * _readNamedTuplestoreScan + */ +static NamedTuplestoreScan * +_readNamedTuplestoreScan(void) +{ + READ_LOCALS(NamedTuplestoreScan); + + ReadCommonScan(&local_node->scan); + + READ_STRING_FIELD(enrname); + + READ_DONE(); +} + +/* + * _readWorkTableScan + */ +static WorkTableScan * +_readWorkTableScan(void) +{ + READ_LOCALS(WorkTableScan); + + ReadCommonScan(&local_node->scan); + + READ_INT_FIELD(wtParam); + + READ_DONE(); +} + +/* + * _readForeignScan + */ +static ForeignScan * +_readForeignScan(void) +{ + READ_LOCALS(ForeignScan); + + ReadCommonScan(&local_node->scan); + + READ_ENUM_FIELD(operation, CmdType); + READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(fs_server); + READ_NODE_FIELD(fdw_exprs); + READ_NODE_FIELD(fdw_private); + READ_NODE_FIELD(fdw_scan_tlist); + READ_NODE_FIELD(fdw_recheck_quals); + READ_BITMAPSET_FIELD(fs_relids); + READ_BOOL_FIELD(fsSystemCol); + + READ_DONE(); +} + +/* + * _readCustomScan + */ +static CustomScan * +_readCustomScan(void) +{ + READ_LOCALS(CustomScan); + char *custom_name; + const CustomScanMethods *methods; + + ReadCommonScan(&local_node->scan); + + READ_UINT_FIELD(flags); + READ_NODE_FIELD(custom_plans); + READ_NODE_FIELD(custom_exprs); + READ_NODE_FIELD(custom_private); + READ_NODE_FIELD(custom_scan_tlist); + READ_BITMAPSET_FIELD(custom_relids); + + /* Lookup CustomScanMethods by CustomName */ + token = pg_strtok(&length); /* skip methods: */ + token = pg_strtok(&length); /* CustomName */ + custom_name = nullable_string(token, length); + methods = GetCustomScanMethods(custom_name, false); + local_node->methods = methods; + + READ_DONE(); +} + +/* + * ReadCommonJoin + * Assign the basic stuff of all nodes that inherit from Join + */ +static void +ReadCommonJoin(Join *local_node) +{ + READ_TEMP_LOCALS(); + + ReadCommonPlan(&local_node->plan); + + READ_ENUM_FIELD(jointype, JoinType); + READ_BOOL_FIELD(inner_unique); + READ_NODE_FIELD(joinqual); +} + +/* + * _readJoin + */ +static Join * +_readJoin(void) +{ + READ_LOCALS_NO_FIELDS(Join); + + ReadCommonJoin(local_node); + + READ_DONE(); +} + +/* + * _readNestLoop + */ +static NestLoop * +_readNestLoop(void) +{ + READ_LOCALS(NestLoop); + + ReadCommonJoin(&local_node->join); + + READ_NODE_FIELD(nestParams); + + READ_DONE(); +} + +/* + * _readMergeJoin + */ +static MergeJoin * +_readMergeJoin(void) +{ + int numCols; + + READ_LOCALS(MergeJoin); + + ReadCommonJoin(&local_node->join); + + READ_BOOL_FIELD(skip_mark_restore); + READ_NODE_FIELD(mergeclauses); + + numCols = list_length(local_node->mergeclauses); + + READ_OID_ARRAY(mergeFamilies, numCols); + READ_OID_ARRAY(mergeCollations, numCols); + READ_INT_ARRAY(mergeStrategies, numCols); + READ_BOOL_ARRAY(mergeNullsFirst, numCols); + + READ_DONE(); +} + +/* + * _readHashJoin + */ +static HashJoin * +_readHashJoin(void) +{ + READ_LOCALS(HashJoin); + + ReadCommonJoin(&local_node->join); + + READ_NODE_FIELD(hashclauses); + READ_NODE_FIELD(hashoperators); + READ_NODE_FIELD(hashcollations); + READ_NODE_FIELD(hashkeys); + + READ_DONE(); +} + +/* + * _readMaterial + */ +static Material * +_readMaterial(void) +{ + READ_LOCALS_NO_FIELDS(Material); + + ReadCommonPlan(&local_node->plan); + + READ_DONE(); +} + +/* + * _readMemoize + */ +static Memoize * +_readMemoize(void) +{ + READ_LOCALS(Memoize); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(numKeys); + READ_OID_ARRAY(hashOperators, local_node->numKeys); + READ_OID_ARRAY(collations, local_node->numKeys); + READ_NODE_FIELD(param_exprs); + READ_BOOL_FIELD(singlerow); + READ_BOOL_FIELD(binary_mode); + READ_UINT_FIELD(est_entries); + READ_BITMAPSET_FIELD(keyparamids); + + READ_DONE(); +} + +/* + * ReadCommonSort + * Assign the basic stuff of all nodes that inherit from Sort + */ +static void +ReadCommonSort(Sort *local_node) +{ + READ_TEMP_LOCALS(); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(sortColIdx, local_node->numCols); + READ_OID_ARRAY(sortOperators, local_node->numCols); + READ_OID_ARRAY(collations, local_node->numCols); + READ_BOOL_ARRAY(nullsFirst, local_node->numCols); +} + +/* + * _readSort + */ +static Sort * +_readSort(void) +{ + READ_LOCALS_NO_FIELDS(Sort); + + ReadCommonSort(local_node); + + READ_DONE(); +} + +/* + * _readIncrementalSort + */ +static IncrementalSort * +_readIncrementalSort(void) +{ + READ_LOCALS(IncrementalSort); + + ReadCommonSort(&local_node->sort); + + READ_INT_FIELD(nPresortedCols); + + READ_DONE(); +} + +/* + * _readGroup + */ +static Group * +_readGroup(void) +{ + READ_LOCALS(Group); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(grpColIdx, local_node->numCols); + READ_OID_ARRAY(grpOperators, local_node->numCols); + READ_OID_ARRAY(grpCollations, local_node->numCols); + + READ_DONE(); +} + +/* + * _readAgg + */ +static Agg * +_readAgg(void) +{ + READ_LOCALS(Agg); + + ReadCommonPlan(&local_node->plan); + + READ_ENUM_FIELD(aggstrategy, AggStrategy); + READ_ENUM_FIELD(aggsplit, AggSplit); + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(grpColIdx, local_node->numCols); + READ_OID_ARRAY(grpOperators, local_node->numCols); + READ_OID_ARRAY(grpCollations, local_node->numCols); + READ_LONG_FIELD(numGroups); + READ_UINT64_FIELD(transitionSpace); + READ_BITMAPSET_FIELD(aggParams); + READ_NODE_FIELD(groupingSets); + READ_NODE_FIELD(chain); + + READ_DONE(); +} + +/* + * _readWindowAgg + */ +static WindowAgg * +_readWindowAgg(void) +{ + READ_LOCALS(WindowAgg); + + ReadCommonPlan(&local_node->plan); + + READ_UINT_FIELD(winref); + READ_INT_FIELD(partNumCols); + READ_ATTRNUMBER_ARRAY(partColIdx, local_node->partNumCols); + READ_OID_ARRAY(partOperators, local_node->partNumCols); + READ_OID_ARRAY(partCollations, local_node->partNumCols); + READ_INT_FIELD(ordNumCols); + READ_ATTRNUMBER_ARRAY(ordColIdx, local_node->ordNumCols); + READ_OID_ARRAY(ordOperators, local_node->ordNumCols); + READ_OID_ARRAY(ordCollations, local_node->ordNumCols); + READ_INT_FIELD(frameOptions); + READ_NODE_FIELD(startOffset); + READ_NODE_FIELD(endOffset); + READ_NODE_FIELD(runCondition); + READ_NODE_FIELD(runConditionOrig); + READ_OID_FIELD(startInRangeFunc); + READ_OID_FIELD(endInRangeFunc); + READ_OID_FIELD(inRangeColl); + READ_BOOL_FIELD(inRangeAsc); + READ_BOOL_FIELD(inRangeNullsFirst); + READ_BOOL_FIELD(topWindow); + + READ_DONE(); +} + +/* + * _readUnique + */ +static Unique * +_readUnique(void) +{ + READ_LOCALS(Unique); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(uniqColIdx, local_node->numCols); + READ_OID_ARRAY(uniqOperators, local_node->numCols); + READ_OID_ARRAY(uniqCollations, local_node->numCols); + + READ_DONE(); +} + +/* + * _readGather + */ +static Gather * +_readGather(void) +{ + READ_LOCALS(Gather); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(num_workers); + READ_INT_FIELD(rescan_param); + READ_BOOL_FIELD(single_copy); + READ_BOOL_FIELD(invisible); + READ_BITMAPSET_FIELD(initParam); + + READ_DONE(); +} + +/* + * _readGatherMerge + */ +static GatherMerge * +_readGatherMerge(void) +{ + READ_LOCALS(GatherMerge); + + ReadCommonPlan(&local_node->plan); + + READ_INT_FIELD(num_workers); + READ_INT_FIELD(rescan_param); + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(sortColIdx, local_node->numCols); + READ_OID_ARRAY(sortOperators, local_node->numCols); + READ_OID_ARRAY(collations, local_node->numCols); + READ_BOOL_ARRAY(nullsFirst, local_node->numCols); + READ_BITMAPSET_FIELD(initParam); + + READ_DONE(); +} + +/* + * _readHash + */ +static Hash * +_readHash(void) +{ + READ_LOCALS(Hash); + + ReadCommonPlan(&local_node->plan); + + READ_NODE_FIELD(hashkeys); + READ_OID_FIELD(skewTable); + READ_INT_FIELD(skewColumn); + READ_BOOL_FIELD(skewInherit); + READ_FLOAT_FIELD(rows_total); + + READ_DONE(); +} + +/* + * _readSetOp + */ +static SetOp * +_readSetOp(void) +{ + READ_LOCALS(SetOp); + + ReadCommonPlan(&local_node->plan); + + READ_ENUM_FIELD(cmd, SetOpCmd); + READ_ENUM_FIELD(strategy, SetOpStrategy); + READ_INT_FIELD(numCols); + READ_ATTRNUMBER_ARRAY(dupColIdx, local_node->numCols); + READ_OID_ARRAY(dupOperators, local_node->numCols); + READ_OID_ARRAY(dupCollations, local_node->numCols); + READ_INT_FIELD(flagColIdx); + READ_INT_FIELD(firstFlag); + READ_LONG_FIELD(numGroups); + + READ_DONE(); +} + +/* + * _readLockRows + */ +static LockRows * +_readLockRows(void) +{ + READ_LOCALS(LockRows); + + ReadCommonPlan(&local_node->plan); + + READ_NODE_FIELD(rowMarks); + READ_INT_FIELD(epqParam); + + READ_DONE(); +} + +/* + * _readLimit + */ +static Limit * +_readLimit(void) +{ + READ_LOCALS(Limit); + + ReadCommonPlan(&local_node->plan); + + READ_NODE_FIELD(limitOffset); + READ_NODE_FIELD(limitCount); + READ_ENUM_FIELD(limitOption, LimitOption); + READ_INT_FIELD(uniqNumCols); + READ_ATTRNUMBER_ARRAY(uniqColIdx, local_node->uniqNumCols); + READ_OID_ARRAY(uniqOperators, local_node->uniqNumCols); + READ_OID_ARRAY(uniqCollations, local_node->uniqNumCols); + + READ_DONE(); +} + +/* + * _readNestLoopParam + */ +static NestLoopParam * +_readNestLoopParam(void) +{ + READ_LOCALS(NestLoopParam); + + READ_INT_FIELD(paramno); + READ_NODE_FIELD(paramval); + + READ_DONE(); +} + +/* + * _readPlanRowMark + */ +static PlanRowMark * +_readPlanRowMark(void) +{ + READ_LOCALS(PlanRowMark); + + READ_UINT_FIELD(rti); + READ_UINT_FIELD(prti); + READ_UINT_FIELD(rowmarkId); + READ_ENUM_FIELD(markType, RowMarkType); + READ_INT_FIELD(allMarkTypes); + READ_ENUM_FIELD(strength, LockClauseStrength); + READ_ENUM_FIELD(waitPolicy, LockWaitPolicy); + READ_BOOL_FIELD(isParent); + + READ_DONE(); +} + +static PartitionPruneInfo * +_readPartitionPruneInfo(void) +{ + READ_LOCALS(PartitionPruneInfo); + + READ_NODE_FIELD(prune_infos); + READ_BITMAPSET_FIELD(other_subplans); + + READ_DONE(); +} + +static PartitionedRelPruneInfo * +_readPartitionedRelPruneInfo(void) +{ + READ_LOCALS(PartitionedRelPruneInfo); + + READ_UINT_FIELD(rtindex); + READ_BITMAPSET_FIELD(present_parts); + READ_INT_FIELD(nparts); + READ_INT_ARRAY(subplan_map, local_node->nparts); + READ_INT_ARRAY(subpart_map, local_node->nparts); + READ_OID_ARRAY(relid_map, local_node->nparts); + READ_NODE_FIELD(initial_pruning_steps); + READ_NODE_FIELD(exec_pruning_steps); + READ_BITMAPSET_FIELD(execparamids); + + READ_DONE(); +} + +static PartitionPruneStepOp * +_readPartitionPruneStepOp(void) +{ + READ_LOCALS(PartitionPruneStepOp); + + READ_INT_FIELD(step.step_id); + READ_INT_FIELD(opstrategy); + READ_NODE_FIELD(exprs); + READ_NODE_FIELD(cmpfns); + READ_BITMAPSET_FIELD(nullkeys); + + READ_DONE(); +} + +static PartitionPruneStepCombine * +_readPartitionPruneStepCombine(void) +{ + READ_LOCALS(PartitionPruneStepCombine); + + READ_INT_FIELD(step.step_id); + READ_ENUM_FIELD(combineOp, PartitionPruneCombineOp); + READ_NODE_FIELD(source_stepids); + + READ_DONE(); +} + +/* + * _readPlanInvalItem + */ +static PlanInvalItem * +_readPlanInvalItem(void) +{ + READ_LOCALS(PlanInvalItem); + + READ_INT_FIELD(cacheId); + READ_UINT_FIELD(hashValue); + + READ_DONE(); +} + +/* + * _readSubPlan + */ +static SubPlan * +_readSubPlan(void) +{ + READ_LOCALS(SubPlan); + + READ_ENUM_FIELD(subLinkType, SubLinkType); + READ_NODE_FIELD(testexpr); + READ_NODE_FIELD(paramIds); + READ_INT_FIELD(plan_id); + READ_STRING_FIELD(plan_name); + READ_OID_FIELD(firstColType); + READ_INT_FIELD(firstColTypmod); + READ_OID_FIELD(firstColCollation); + READ_BOOL_FIELD(useHashTable); + READ_BOOL_FIELD(unknownEqFalse); + READ_BOOL_FIELD(parallel_safe); + READ_NODE_FIELD(setParam); + READ_NODE_FIELD(parParam); + READ_NODE_FIELD(args); + READ_FLOAT_FIELD(startup_cost); + READ_FLOAT_FIELD(per_call_cost); + + READ_DONE(); +} + +/* + * _readAlternativeSubPlan + */ +static AlternativeSubPlan * +_readAlternativeSubPlan(void) +{ + READ_LOCALS(AlternativeSubPlan); + + READ_NODE_FIELD(subplans); + + READ_DONE(); +} + +/* + * _readExtensibleNode + */ +static ExtensibleNode * +_readExtensibleNode(void) +{ + const ExtensibleNodeMethods *methods; + ExtensibleNode *local_node; + const char *extnodename; + + READ_TEMP_LOCALS(); + + token = pg_strtok(&length); /* skip :extnodename */ + token = pg_strtok(&length); /* get extnodename */ + + extnodename = nullable_string(token, length); + if (!extnodename) + elog(ERROR, "extnodename has to be supplied"); + methods = GetExtensibleNodeMethods(extnodename, false); + + local_node = (ExtensibleNode *) newNode(methods->node_size, + T_ExtensibleNode); + local_node->extnodename = extnodename; + + /* deserialize the private fields */ + methods->nodeRead(local_node); + + READ_DONE(); +} + +/* + * _readPartitionBoundSpec + */ +static PartitionBoundSpec * +_readPartitionBoundSpec(void) +{ + READ_LOCALS(PartitionBoundSpec); + + READ_CHAR_FIELD(strategy); + READ_BOOL_FIELD(is_default); + READ_INT_FIELD(modulus); + READ_INT_FIELD(remainder); + READ_NODE_FIELD(listdatums); + READ_NODE_FIELD(lowerdatums); + READ_NODE_FIELD(upperdatums); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * _readPartitionRangeDatum + */ +static PartitionRangeDatum * +_readPartitionRangeDatum(void) +{ + READ_LOCALS(PartitionRangeDatum); + + READ_ENUM_FIELD(kind, PartitionRangeDatumKind); + READ_NODE_FIELD(value); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* + * parseNodeString + * + * Given a character string representing a node tree, parseNodeString creates + * the internal node structure. + * + * The string to be read must already have been loaded into pg_strtok(). + */ +Node * +parseNodeString(void) +{ + void *return_value; + + READ_TEMP_LOCALS(); + + /* Guard against stack overflow due to overly complex expressions */ + check_stack_depth(); + + token = pg_strtok(&length); + +#define MATCH(tokname, namelen) \ + (length == namelen && memcmp(token, tokname, namelen) == 0) + + if (MATCH("QUERY", 5)) + return_value = _readQuery(); + else if (MATCH("WITHCHECKOPTION", 15)) + return_value = _readWithCheckOption(); + else if (MATCH("SORTGROUPCLAUSE", 15)) + return_value = _readSortGroupClause(); + else if (MATCH("GROUPINGSET", 11)) + return_value = _readGroupingSet(); + else if (MATCH("WINDOWCLAUSE", 12)) + return_value = _readWindowClause(); + else if (MATCH("ROWMARKCLAUSE", 13)) + return_value = _readRowMarkClause(); + else if (MATCH("CTESEARCHCLAUSE", 15)) + return_value = _readCTESearchClause(); + else if (MATCH("CTECYCLECLAUSE", 14)) + return_value = _readCTECycleClause(); + else if (MATCH("COMMONTABLEEXPR", 15)) + return_value = _readCommonTableExpr(); + else if (MATCH("MERGEWHENCLAUSE", 15)) + return_value = _readMergeWhenClause(); + else if (MATCH("MERGEACTION", 11)) + return_value = _readMergeAction(); + else if (MATCH("SETOPERATIONSTMT", 16)) + return_value = _readSetOperationStmt(); + else if (MATCH("ALIAS", 5)) + return_value = _readAlias(); + else if (MATCH("RANGEVAR", 8)) + return_value = _readRangeVar(); + else if (MATCH("INTOCLAUSE", 10)) + return_value = _readIntoClause(); + else if (MATCH("TABLEFUNC", 9)) + return_value = _readTableFunc(); + else if (MATCH("VAR", 3)) + return_value = _readVar(); + else if (MATCH("CONST", 5)) + return_value = _readConst(); + else if (MATCH("PARAM", 5)) + return_value = _readParam(); + else if (MATCH("AGGREF", 6)) + return_value = _readAggref(); + else if (MATCH("GROUPINGFUNC", 12)) + return_value = _readGroupingFunc(); + else if (MATCH("WINDOWFUNC", 10)) + return_value = _readWindowFunc(); + else if (MATCH("SUBSCRIPTINGREF", 15)) + return_value = _readSubscriptingRef(); + else if (MATCH("FUNCEXPR", 8)) + return_value = _readFuncExpr(); + else if (MATCH("NAMEDARGEXPR", 12)) + return_value = _readNamedArgExpr(); + else if (MATCH("OPEXPR", 6)) + return_value = _readOpExpr(); + else if (MATCH("DISTINCTEXPR", 12)) + return_value = _readDistinctExpr(); + else if (MATCH("NULLIFEXPR", 10)) + return_value = _readNullIfExpr(); + else if (MATCH("SCALARARRAYOPEXPR", 17)) + return_value = _readScalarArrayOpExpr(); + else if (MATCH("BOOLEXPR", 8)) + return_value = _readBoolExpr(); + else if (MATCH("SUBLINK", 7)) + return_value = _readSubLink(); + else if (MATCH("FIELDSELECT", 11)) + return_value = _readFieldSelect(); + else if (MATCH("FIELDSTORE", 10)) + return_value = _readFieldStore(); + else if (MATCH("RELABELTYPE", 11)) + return_value = _readRelabelType(); + else if (MATCH("COERCEVIAIO", 11)) + return_value = _readCoerceViaIO(); + else if (MATCH("ARRAYCOERCEEXPR", 15)) + return_value = _readArrayCoerceExpr(); + else if (MATCH("CONVERTROWTYPEEXPR", 18)) + return_value = _readConvertRowtypeExpr(); + else if (MATCH("COLLATEEXPR", 11)) + return_value = _readCollateExpr(); + else if (MATCH("CASEEXPR", 8)) + return_value = _readCaseExpr(); + else if (MATCH("CASEWHEN", 8)) + return_value = _readCaseWhen(); + else if (MATCH("CASETESTEXPR", 12)) + return_value = _readCaseTestExpr(); + else if (MATCH("ARRAYEXPR", 9)) + return_value = _readArrayExpr(); + else if (MATCH("ROWEXPR", 7)) + return_value = _readRowExpr(); + else if (MATCH("ROWCOMPAREEXPR", 14)) + return_value = _readRowCompareExpr(); + else if (MATCH("COALESCEEXPR", 12)) + return_value = _readCoalesceExpr(); + else if (MATCH("MINMAXEXPR", 10)) + return_value = _readMinMaxExpr(); + else if (MATCH("SQLVALUEFUNCTION", 16)) + return_value = _readSQLValueFunction(); + else if (MATCH("XMLEXPR", 7)) + return_value = _readXmlExpr(); + else if (MATCH("NULLTEST", 8)) + return_value = _readNullTest(); + else if (MATCH("BOOLEANTEST", 11)) + return_value = _readBooleanTest(); + else if (MATCH("COERCETODOMAIN", 14)) + return_value = _readCoerceToDomain(); + else if (MATCH("COERCETODOMAINVALUE", 19)) + return_value = _readCoerceToDomainValue(); + else if (MATCH("SETTODEFAULT", 12)) + return_value = _readSetToDefault(); + else if (MATCH("CURRENTOFEXPR", 13)) + return_value = _readCurrentOfExpr(); + else if (MATCH("NEXTVALUEEXPR", 13)) + return_value = _readNextValueExpr(); + else if (MATCH("INFERENCEELEM", 13)) + return_value = _readInferenceElem(); + else if (MATCH("TARGETENTRY", 11)) + return_value = _readTargetEntry(); + else if (MATCH("RANGETBLREF", 11)) + return_value = _readRangeTblRef(); + else if (MATCH("JOINEXPR", 8)) + return_value = _readJoinExpr(); + else if (MATCH("FROMEXPR", 8)) + return_value = _readFromExpr(); + else if (MATCH("ONCONFLICTEXPR", 14)) + return_value = _readOnConflictExpr(); + else if (MATCH("APPENDRELINFO", 13)) + return_value = _readAppendRelInfo(); + else if (MATCH("RANGETBLENTRY", 13)) + return_value = _readRangeTblEntry(); + else if (MATCH("RANGETBLFUNCTION", 16)) + return_value = _readRangeTblFunction(); + else if (MATCH("TABLESAMPLECLAUSE", 17)) + return_value = _readTableSampleClause(); + else if (MATCH("NOTIFYSTMT", 10)) + return_value = _readNotifyStmt(); + else if (MATCH("DEFELEM", 7)) + return_value = _readDefElem(); + else if (MATCH("DECLARECURSORSTMT", 17)) + return_value = _readDeclareCursorStmt(); + else if (MATCH("PLANNEDSTMT", 11)) + return_value = _readPlannedStmt(); + else if (MATCH("PLAN", 4)) + return_value = _readPlan(); + else if (MATCH("RESULT", 6)) + return_value = _readResult(); + else if (MATCH("PROJECTSET", 10)) + return_value = _readProjectSet(); + else if (MATCH("MODIFYTABLE", 11)) + return_value = _readModifyTable(); + else if (MATCH("APPEND", 6)) + return_value = _readAppend(); + else if (MATCH("MERGEAPPEND", 11)) + return_value = _readMergeAppend(); + else if (MATCH("RECURSIVEUNION", 14)) + return_value = _readRecursiveUnion(); + else if (MATCH("BITMAPAND", 9)) + return_value = _readBitmapAnd(); + else if (MATCH("BITMAPOR", 8)) + return_value = _readBitmapOr(); + else if (MATCH("SCAN", 4)) + return_value = _readScan(); + else if (MATCH("SEQSCAN", 7)) + return_value = _readSeqScan(); + else if (MATCH("SAMPLESCAN", 10)) + return_value = _readSampleScan(); + else if (MATCH("INDEXSCAN", 9)) + return_value = _readIndexScan(); + else if (MATCH("INDEXONLYSCAN", 13)) + return_value = _readIndexOnlyScan(); + else if (MATCH("BITMAPINDEXSCAN", 15)) + return_value = _readBitmapIndexScan(); + else if (MATCH("BITMAPHEAPSCAN", 14)) + return_value = _readBitmapHeapScan(); + else if (MATCH("TIDSCAN", 7)) + return_value = _readTidScan(); + else if (MATCH("TIDRANGESCAN", 12)) + return_value = _readTidRangeScan(); + else if (MATCH("SUBQUERYSCAN", 12)) + return_value = _readSubqueryScan(); + else if (MATCH("FUNCTIONSCAN", 12)) + return_value = _readFunctionScan(); + else if (MATCH("VALUESSCAN", 10)) + return_value = _readValuesScan(); + else if (MATCH("TABLEFUNCSCAN", 13)) + return_value = _readTableFuncScan(); + else if (MATCH("CTESCAN", 7)) + return_value = _readCteScan(); + else if (MATCH("NAMEDTUPLESTORESCAN", 19)) + return_value = _readNamedTuplestoreScan(); + else if (MATCH("WORKTABLESCAN", 13)) + return_value = _readWorkTableScan(); + else if (MATCH("FOREIGNSCAN", 11)) + return_value = _readForeignScan(); + else if (MATCH("CUSTOMSCAN", 10)) + return_value = _readCustomScan(); + else if (MATCH("JOIN", 4)) + return_value = _readJoin(); + else if (MATCH("NESTLOOP", 8)) + return_value = _readNestLoop(); + else if (MATCH("MERGEJOIN", 9)) + return_value = _readMergeJoin(); + else if (MATCH("HASHJOIN", 8)) + return_value = _readHashJoin(); + else if (MATCH("MATERIAL", 8)) + return_value = _readMaterial(); + else if (MATCH("MEMOIZE", 7)) + return_value = _readMemoize(); + else if (MATCH("SORT", 4)) + return_value = _readSort(); + else if (MATCH("INCREMENTALSORT", 15)) + return_value = _readIncrementalSort(); + else if (MATCH("GROUP", 5)) + return_value = _readGroup(); + else if (MATCH("AGG", 3)) + return_value = _readAgg(); + else if (MATCH("WINDOWAGG", 9)) + return_value = _readWindowAgg(); + else if (MATCH("UNIQUE", 6)) + return_value = _readUnique(); + else if (MATCH("GATHER", 6)) + return_value = _readGather(); + else if (MATCH("GATHERMERGE", 11)) + return_value = _readGatherMerge(); + else if (MATCH("HASH", 4)) + return_value = _readHash(); + else if (MATCH("SETOP", 5)) + return_value = _readSetOp(); + else if (MATCH("LOCKROWS", 8)) + return_value = _readLockRows(); + else if (MATCH("LIMIT", 5)) + return_value = _readLimit(); + else if (MATCH("NESTLOOPPARAM", 13)) + return_value = _readNestLoopParam(); + else if (MATCH("PLANROWMARK", 11)) + return_value = _readPlanRowMark(); + else if (MATCH("PARTITIONPRUNEINFO", 18)) + return_value = _readPartitionPruneInfo(); + else if (MATCH("PARTITIONEDRELPRUNEINFO", 23)) + return_value = _readPartitionedRelPruneInfo(); + else if (MATCH("PARTITIONPRUNESTEPOP", 20)) + return_value = _readPartitionPruneStepOp(); + else if (MATCH("PARTITIONPRUNESTEPCOMBINE", 25)) + return_value = _readPartitionPruneStepCombine(); + else if (MATCH("PLANINVALITEM", 13)) + return_value = _readPlanInvalItem(); + else if (MATCH("SUBPLAN", 7)) + return_value = _readSubPlan(); + else if (MATCH("ALTERNATIVESUBPLAN", 18)) + return_value = _readAlternativeSubPlan(); + else if (MATCH("EXTENSIBLENODE", 14)) + return_value = _readExtensibleNode(); + else if (MATCH("PARTITIONBOUNDSPEC", 18)) + return_value = _readPartitionBoundSpec(); + else if (MATCH("PARTITIONRANGEDATUM", 19)) + return_value = _readPartitionRangeDatum(); + else + { + elog(ERROR, "badly formatted node string \"%.32s\"...", token); + return_value = NULL; /* keep compiler quiet */ + } + + return (Node *) return_value; +} + + +/* + * readDatum + * + * Given a string representation of a constant, recreate the appropriate + * Datum. The string representation embeds length info, but not byValue, + * so we must be told that. + */ +Datum +readDatum(bool typbyval) +{ + Size length, + i; + int tokenLength; + const char *token; + Datum res; + char *s; + + /* + * read the actual length of the value + */ + token = pg_strtok(&tokenLength); + length = atoui(token); + + token = pg_strtok(&tokenLength); /* read the '[' */ + if (token == NULL || token[0] != '[') + elog(ERROR, "expected \"[\" to start datum, but got \"%s\"; length = %zu", + token ? token : "[NULL]", length); + + if (typbyval) + { + if (length > (Size) sizeof(Datum)) + elog(ERROR, "byval datum but length = %zu", length); + res = (Datum) 0; + s = (char *) (&res); + for (i = 0; i < (Size) sizeof(Datum); i++) + { + token = pg_strtok(&tokenLength); + s[i] = (char) atoi(token); + } + } + else if (length <= 0) + res = (Datum) NULL; + else + { + s = (char *) palloc(length); + for (i = 0; i < length; i++) + { + token = pg_strtok(&tokenLength); + s[i] = (char) atoi(token); + } + res = PointerGetDatum(s); + } + + token = pg_strtok(&tokenLength); /* read the ']' */ + if (token == NULL || token[0] != ']') + elog(ERROR, "expected \"]\" to end datum, but got \"%s\"; length = %zu", + token ? token : "[NULL]", length); + + return res; +} + +/* + * readAttrNumberCols + */ +AttrNumber * +readAttrNumberCols(int numCols) +{ + int tokenLength, + i; + const char *token; + AttrNumber *attr_vals; + + if (numCols <= 0) + return NULL; + + attr_vals = (AttrNumber *) palloc(numCols * sizeof(AttrNumber)); + for (i = 0; i < numCols; i++) + { + token = pg_strtok(&tokenLength); + attr_vals[i] = atoi(token); + } + + return attr_vals; +} + +/* + * readOidCols + */ +Oid * +readOidCols(int numCols) +{ + int tokenLength, + i; + const char *token; + Oid *oid_vals; + + if (numCols <= 0) + return NULL; + + oid_vals = (Oid *) palloc(numCols * sizeof(Oid)); + for (i = 0; i < numCols; i++) + { + token = pg_strtok(&tokenLength); + oid_vals[i] = atooid(token); + } + + return oid_vals; +} + +/* + * readIntCols + */ +int * +readIntCols(int numCols) +{ + int tokenLength, + i; + const char *token; + int *int_vals; + + if (numCols <= 0) + return NULL; + + int_vals = (int *) palloc(numCols * sizeof(int)); + for (i = 0; i < numCols; i++) + { + token = pg_strtok(&tokenLength); + int_vals[i] = atoi(token); + } + + return int_vals; +} + +/* + * readBoolCols + */ +bool * +readBoolCols(int numCols) +{ + int tokenLength, + i; + const char *token; + bool *bool_vals; + + if (numCols <= 0) + return NULL; + + bool_vals = (bool *) palloc(numCols * sizeof(bool)); + for (i = 0; i < numCols; i++) + { + token = pg_strtok(&tokenLength); + bool_vals[i] = strtobool(token); + } + + return bool_vals; +} diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c new file mode 100644 index 0000000..a7a6b26 --- /dev/null +++ b/src/backend/nodes/tidbitmap.c @@ -0,0 +1,1561 @@ +/*------------------------------------------------------------------------- + * + * tidbitmap.c + * PostgreSQL tuple-id (TID) bitmap package + * + * This module provides bitmap data structures that are spiritually + * similar to Bitmapsets, but are specially adapted to store sets of + * tuple identifiers (TIDs), or ItemPointers. In particular, the division + * of an ItemPointer into BlockNumber and OffsetNumber is catered for. + * Also, since we wish to be able to store very large tuple sets in + * memory with this data structure, we support "lossy" storage, in which + * we no longer remember individual tuple offsets on a page but only the + * fact that a particular page needs to be visited. + * + * The "lossy" storage uses one bit per disk page, so at the standard 8K + * BLCKSZ, we can represent all pages in 64Gb of disk space in about 1Mb + * of memory. People pushing around tables of that size should have a + * couple of Mb to spare, so we don't worry about providing a second level + * of lossiness. In theory we could fall back to page ranges at some + * point, but for now that seems useless complexity. + * + * We also support the notion of candidate matches, or rechecking. This + * means we know that a search need visit only some tuples on a page, + * but we are not certain that all of those tuples are real matches. + * So the eventual heap scan must recheck the quals for these tuples only, + * rather than rechecking the quals for all tuples on the page as in the + * lossy-bitmap case. Rechecking can be specified when TIDs are inserted + * into a bitmap, and it can also happen internally when we AND a lossy + * and a non-lossy page. + * + * + * Copyright (c) 2003-2022, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/backend/nodes/tidbitmap.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <limits.h> + +#include "access/htup_details.h" +#include "common/hashfn.h" +#include "nodes/bitmapset.h" +#include "nodes/tidbitmap.h" +#include "storage/lwlock.h" +#include "utils/dsa.h" + +/* + * The maximum number of tuples per page is not large (typically 256 with + * 8K pages, or 1024 with 32K pages). So there's not much point in making + * the per-page bitmaps variable size. We just legislate that the size + * is this: + */ +#define MAX_TUPLES_PER_PAGE MaxHeapTuplesPerPage + +/* + * When we have to switch over to lossy storage, we use a data structure + * with one bit per page, where all pages having the same number DIV + * PAGES_PER_CHUNK are aggregated into one chunk. When a chunk is present + * and has the bit set for a given page, there must not be a per-page entry + * for that page in the page table. + * + * We actually store both exact pages and lossy chunks in the same hash + * table, using identical data structures. (This is because the memory + * management for hashtables doesn't easily/efficiently allow space to be + * transferred easily from one hashtable to another.) Therefore it's best + * if PAGES_PER_CHUNK is the same as MAX_TUPLES_PER_PAGE, or at least not + * too different. But we also want PAGES_PER_CHUNK to be a power of 2 to + * avoid expensive integer remainder operations. So, define it like this: + */ +#define PAGES_PER_CHUNK (BLCKSZ / 32) + +/* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */ + +#define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD) +#define BITNUM(x) ((x) % BITS_PER_BITMAPWORD) + +/* number of active words for an exact page: */ +#define WORDS_PER_PAGE ((MAX_TUPLES_PER_PAGE - 1) / BITS_PER_BITMAPWORD + 1) +/* number of active words for a lossy chunk: */ +#define WORDS_PER_CHUNK ((PAGES_PER_CHUNK - 1) / BITS_PER_BITMAPWORD + 1) + +/* + * The hashtable entries are represented by this data structure. For + * an exact page, blockno is the page number and bit k of the bitmap + * represents tuple offset k+1. For a lossy chunk, blockno is the first + * page in the chunk (this must be a multiple of PAGES_PER_CHUNK) and + * bit k represents page blockno+k. Note that it is not possible to + * have exact storage for the first page of a chunk if we are using + * lossy storage for any page in the chunk's range, since the same + * hashtable entry has to serve both purposes. + * + * recheck is used only on exact pages --- it indicates that although + * only the stated tuples need be checked, the full index qual condition + * must be checked for each (ie, these are candidate matches). + */ +typedef struct PagetableEntry +{ + BlockNumber blockno; /* page number (hashtable key) */ + char status; /* hash entry status */ + bool ischunk; /* T = lossy storage, F = exact */ + bool recheck; /* should the tuples be rechecked? */ + bitmapword words[Max(WORDS_PER_PAGE, WORDS_PER_CHUNK)]; +} PagetableEntry; + +/* + * Holds array of pagetable entries. + */ +typedef struct PTEntryArray +{ + pg_atomic_uint32 refcount; /* no. of iterator attached */ + PagetableEntry ptentry[FLEXIBLE_ARRAY_MEMBER]; +} PTEntryArray; + +/* + * We want to avoid the overhead of creating the hashtable, which is + * comparatively large, when not necessary. Particularly when we are using a + * bitmap scan on the inside of a nestloop join: a bitmap may well live only + * long enough to accumulate one entry in such cases. We therefore avoid + * creating an actual hashtable until we need two pagetable entries. When + * just one pagetable entry is needed, we store it in a fixed field of + * TIDBitMap. (NOTE: we don't get rid of the hashtable if the bitmap later + * shrinks down to zero or one page again. So, status can be TBM_HASH even + * when nentries is zero or one.) + */ +typedef enum +{ + TBM_EMPTY, /* no hashtable, nentries == 0 */ + TBM_ONE_PAGE, /* entry1 contains the single entry */ + TBM_HASH /* pagetable is valid, entry1 is not */ +} TBMStatus; + +/* + * Current iterating state of the TBM. + */ +typedef enum +{ + TBM_NOT_ITERATING, /* not yet converted to page and chunk array */ + TBM_ITERATING_PRIVATE, /* converted to local page and chunk array */ + TBM_ITERATING_SHARED /* converted to shared page and chunk array */ +} TBMIteratingState; + +/* + * Here is the representation for a whole TIDBitMap: + */ +struct TIDBitmap +{ + NodeTag type; /* to make it a valid Node */ + MemoryContext mcxt; /* memory context containing me */ + TBMStatus status; /* see codes above */ + struct pagetable_hash *pagetable; /* hash table of PagetableEntry's */ + int nentries; /* number of entries in pagetable */ + int maxentries; /* limit on same to meet maxbytes */ + int npages; /* number of exact entries in pagetable */ + int nchunks; /* number of lossy entries in pagetable */ + TBMIteratingState iterating; /* tbm_begin_iterate called? */ + uint32 lossify_start; /* offset to start lossifying hashtable at */ + PagetableEntry entry1; /* used when status == TBM_ONE_PAGE */ + /* these are valid when iterating is true: */ + PagetableEntry **spages; /* sorted exact-page list, or NULL */ + PagetableEntry **schunks; /* sorted lossy-chunk list, or NULL */ + dsa_pointer dsapagetable; /* dsa_pointer to the element array */ + dsa_pointer dsapagetableold; /* dsa_pointer to the old element array */ + dsa_pointer ptpages; /* dsa_pointer to the page array */ + dsa_pointer ptchunks; /* dsa_pointer to the chunk array */ + dsa_area *dsa; /* reference to per-query dsa area */ +}; + +/* + * When iterating over a bitmap in sorted order, a TBMIterator is used to + * track our progress. There can be several iterators scanning the same + * bitmap concurrently. Note that the bitmap becomes read-only as soon as + * any iterator is created. + */ +struct TBMIterator +{ + TIDBitmap *tbm; /* TIDBitmap we're iterating over */ + int spageptr; /* next spages index */ + int schunkptr; /* next schunks index */ + int schunkbit; /* next bit to check in current schunk */ + TBMIterateResult output; /* MUST BE LAST (because variable-size) */ +}; + +/* + * Holds the shared members of the iterator so that multiple processes + * can jointly iterate. + */ +typedef struct TBMSharedIteratorState +{ + int nentries; /* number of entries in pagetable */ + int maxentries; /* limit on same to meet maxbytes */ + int npages; /* number of exact entries in pagetable */ + int nchunks; /* number of lossy entries in pagetable */ + dsa_pointer pagetable; /* dsa pointers to head of pagetable data */ + dsa_pointer spages; /* dsa pointer to page array */ + dsa_pointer schunks; /* dsa pointer to chunk array */ + LWLock lock; /* lock to protect below members */ + int spageptr; /* next spages index */ + int schunkptr; /* next schunks index */ + int schunkbit; /* next bit to check in current schunk */ +} TBMSharedIteratorState; + +/* + * pagetable iteration array. + */ +typedef struct PTIterationArray +{ + pg_atomic_uint32 refcount; /* no. of iterator attached */ + int index[FLEXIBLE_ARRAY_MEMBER]; /* index array */ +} PTIterationArray; + +/* + * same as TBMIterator, but it is used for joint iteration, therefore this + * also holds a reference to the shared state. + */ +struct TBMSharedIterator +{ + TBMSharedIteratorState *state; /* shared state */ + PTEntryArray *ptbase; /* pagetable element array */ + PTIterationArray *ptpages; /* sorted exact page index list */ + PTIterationArray *ptchunks; /* sorted lossy page index list */ + TBMIterateResult output; /* MUST BE LAST (because variable-size) */ +}; + +/* Local function prototypes */ +static void tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage); +static bool tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, + const TIDBitmap *b); +static const PagetableEntry *tbm_find_pageentry(const TIDBitmap *tbm, + BlockNumber pageno); +static PagetableEntry *tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno); +static bool tbm_page_is_lossy(const TIDBitmap *tbm, BlockNumber pageno); +static void tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno); +static void tbm_lossify(TIDBitmap *tbm); +static int tbm_comparator(const void *left, const void *right); +static int tbm_shared_comparator(const void *left, const void *right, + void *arg); + +/* define hashtable mapping block numbers to PagetableEntry's */ +#define SH_USE_NONDEFAULT_ALLOCATOR +#define SH_PREFIX pagetable +#define SH_ELEMENT_TYPE PagetableEntry +#define SH_KEY_TYPE BlockNumber +#define SH_KEY blockno +#define SH_HASH_KEY(tb, key) murmurhash32(key) +#define SH_EQUAL(tb, a, b) a == b +#define SH_SCOPE static inline +#define SH_DEFINE +#define SH_DECLARE +#include "lib/simplehash.h" + + +/* + * tbm_create - create an initially-empty bitmap + * + * The bitmap will live in the memory context that is CurrentMemoryContext + * at the time of this call. It will be limited to (approximately) maxbytes + * total memory consumption. If the DSA passed to this function is not NULL + * then the memory for storing elements of the underlying page table will + * be allocated from the DSA. + */ +TIDBitmap * +tbm_create(long maxbytes, dsa_area *dsa) +{ + TIDBitmap *tbm; + + /* Create the TIDBitmap struct and zero all its fields */ + tbm = makeNode(TIDBitmap); + + tbm->mcxt = CurrentMemoryContext; + tbm->status = TBM_EMPTY; + + tbm->maxentries = (int) tbm_calculate_entries(maxbytes); + tbm->lossify_start = 0; + tbm->dsa = dsa; + tbm->dsapagetable = InvalidDsaPointer; + tbm->dsapagetableold = InvalidDsaPointer; + tbm->ptpages = InvalidDsaPointer; + tbm->ptchunks = InvalidDsaPointer; + + return tbm; +} + +/* + * Actually create the hashtable. Since this is a moderately expensive + * proposition, we don't do it until we have to. + */ +static void +tbm_create_pagetable(TIDBitmap *tbm) +{ + Assert(tbm->status != TBM_HASH); + Assert(tbm->pagetable == NULL); + + tbm->pagetable = pagetable_create(tbm->mcxt, 128, tbm); + + /* If entry1 is valid, push it into the hashtable */ + if (tbm->status == TBM_ONE_PAGE) + { + PagetableEntry *page; + bool found; + char oldstatus; + + page = pagetable_insert(tbm->pagetable, + tbm->entry1.blockno, + &found); + Assert(!found); + oldstatus = page->status; + memcpy(page, &tbm->entry1, sizeof(PagetableEntry)); + page->status = oldstatus; + } + + tbm->status = TBM_HASH; +} + +/* + * tbm_free - free a TIDBitmap + */ +void +tbm_free(TIDBitmap *tbm) +{ + if (tbm->pagetable) + pagetable_destroy(tbm->pagetable); + if (tbm->spages) + pfree(tbm->spages); + if (tbm->schunks) + pfree(tbm->schunks); + pfree(tbm); +} + +/* + * tbm_free_shared_area - free shared state + * + * Free shared iterator state, Also free shared pagetable and iterator arrays + * memory if they are not referred by any of the shared iterator i.e recount + * is becomes 0. + */ +void +tbm_free_shared_area(dsa_area *dsa, dsa_pointer dp) +{ + TBMSharedIteratorState *istate = dsa_get_address(dsa, dp); + PTEntryArray *ptbase; + PTIterationArray *ptpages; + PTIterationArray *ptchunks; + + if (DsaPointerIsValid(istate->pagetable)) + { + ptbase = dsa_get_address(dsa, istate->pagetable); + if (pg_atomic_sub_fetch_u32(&ptbase->refcount, 1) == 0) + dsa_free(dsa, istate->pagetable); + } + if (DsaPointerIsValid(istate->spages)) + { + ptpages = dsa_get_address(dsa, istate->spages); + if (pg_atomic_sub_fetch_u32(&ptpages->refcount, 1) == 0) + dsa_free(dsa, istate->spages); + } + if (DsaPointerIsValid(istate->schunks)) + { + ptchunks = dsa_get_address(dsa, istate->schunks); + if (pg_atomic_sub_fetch_u32(&ptchunks->refcount, 1) == 0) + dsa_free(dsa, istate->schunks); + } + + dsa_free(dsa, dp); +} + +/* + * tbm_add_tuples - add some tuple IDs to a TIDBitmap + * + * If recheck is true, then the recheck flag will be set in the + * TBMIterateResult when any of these tuples are reported out. + */ +void +tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, + bool recheck) +{ + BlockNumber currblk = InvalidBlockNumber; + PagetableEntry *page = NULL; /* only valid when currblk is valid */ + int i; + + Assert(tbm->iterating == TBM_NOT_ITERATING); + for (i = 0; i < ntids; i++) + { + BlockNumber blk = ItemPointerGetBlockNumber(tids + i); + OffsetNumber off = ItemPointerGetOffsetNumber(tids + i); + int wordnum, + bitnum; + + /* safety check to ensure we don't overrun bit array bounds */ + if (off < 1 || off > MAX_TUPLES_PER_PAGE) + elog(ERROR, "tuple offset out of range: %u", off); + + /* + * Look up target page unless we already did. This saves cycles when + * the input includes consecutive tuples on the same page, which is + * common enough to justify an extra test here. + */ + if (blk != currblk) + { + if (tbm_page_is_lossy(tbm, blk)) + page = NULL; /* remember page is lossy */ + else + page = tbm_get_pageentry(tbm, blk); + currblk = blk; + } + + if (page == NULL) + continue; /* whole page is already marked */ + + if (page->ischunk) + { + /* The page is a lossy chunk header, set bit for itself */ + wordnum = bitnum = 0; + } + else + { + /* Page is exact, so set bit for individual tuple */ + wordnum = WORDNUM(off - 1); + bitnum = BITNUM(off - 1); + } + page->words[wordnum] |= ((bitmapword) 1 << bitnum); + page->recheck |= recheck; + + if (tbm->nentries > tbm->maxentries) + { + tbm_lossify(tbm); + /* Page could have been converted to lossy, so force new lookup */ + currblk = InvalidBlockNumber; + } + } +} + +/* + * tbm_add_page - add a whole page to a TIDBitmap + * + * This causes the whole page to be reported (with the recheck flag) + * when the TIDBitmap is scanned. + */ +void +tbm_add_page(TIDBitmap *tbm, BlockNumber pageno) +{ + /* Enter the page in the bitmap, or mark it lossy if already present */ + tbm_mark_page_lossy(tbm, pageno); + /* If we went over the memory limit, lossify some more pages */ + if (tbm->nentries > tbm->maxentries) + tbm_lossify(tbm); +} + +/* + * tbm_union - set union + * + * a is modified in-place, b is not changed + */ +void +tbm_union(TIDBitmap *a, const TIDBitmap *b) +{ + Assert(!a->iterating); + /* Nothing to do if b is empty */ + if (b->nentries == 0) + return; + /* Scan through chunks and pages in b, merge into a */ + if (b->status == TBM_ONE_PAGE) + tbm_union_page(a, &b->entry1); + else + { + pagetable_iterator i; + PagetableEntry *bpage; + + Assert(b->status == TBM_HASH); + pagetable_start_iterate(b->pagetable, &i); + while ((bpage = pagetable_iterate(b->pagetable, &i)) != NULL) + tbm_union_page(a, bpage); + } +} + +/* Process one page of b during a union op */ +static void +tbm_union_page(TIDBitmap *a, const PagetableEntry *bpage) +{ + PagetableEntry *apage; + int wordnum; + + if (bpage->ischunk) + { + /* Scan b's chunk, mark each indicated page lossy in a */ + for (wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++) + { + bitmapword w = bpage->words[wordnum]; + + if (w != 0) + { + BlockNumber pg; + + pg = bpage->blockno + (wordnum * BITS_PER_BITMAPWORD); + while (w != 0) + { + if (w & 1) + tbm_mark_page_lossy(a, pg); + pg++; + w >>= 1; + } + } + } + } + else if (tbm_page_is_lossy(a, bpage->blockno)) + { + /* page is already lossy in a, nothing to do */ + return; + } + else + { + apage = tbm_get_pageentry(a, bpage->blockno); + if (apage->ischunk) + { + /* The page is a lossy chunk header, set bit for itself */ + apage->words[0] |= ((bitmapword) 1 << 0); + } + else + { + /* Both pages are exact, merge at the bit level */ + for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++) + apage->words[wordnum] |= bpage->words[wordnum]; + apage->recheck |= bpage->recheck; + } + } + + if (a->nentries > a->maxentries) + tbm_lossify(a); +} + +/* + * tbm_intersect - set intersection + * + * a is modified in-place, b is not changed + */ +void +tbm_intersect(TIDBitmap *a, const TIDBitmap *b) +{ + Assert(!a->iterating); + /* Nothing to do if a is empty */ + if (a->nentries == 0) + return; + /* Scan through chunks and pages in a, try to match to b */ + if (a->status == TBM_ONE_PAGE) + { + if (tbm_intersect_page(a, &a->entry1, b)) + { + /* Page is now empty, remove it from a */ + Assert(!a->entry1.ischunk); + a->npages--; + a->nentries--; + Assert(a->nentries == 0); + a->status = TBM_EMPTY; + } + } + else + { + pagetable_iterator i; + PagetableEntry *apage; + + Assert(a->status == TBM_HASH); + pagetable_start_iterate(a->pagetable, &i); + while ((apage = pagetable_iterate(a->pagetable, &i)) != NULL) + { + if (tbm_intersect_page(a, apage, b)) + { + /* Page or chunk is now empty, remove it from a */ + if (apage->ischunk) + a->nchunks--; + else + a->npages--; + a->nentries--; + if (!pagetable_delete(a->pagetable, apage->blockno)) + elog(ERROR, "hash table corrupted"); + } + } + } +} + +/* + * Process one page of a during an intersection op + * + * Returns true if apage is now empty and should be deleted from a + */ +static bool +tbm_intersect_page(TIDBitmap *a, PagetableEntry *apage, const TIDBitmap *b) +{ + const PagetableEntry *bpage; + int wordnum; + + if (apage->ischunk) + { + /* Scan each bit in chunk, try to clear */ + bool candelete = true; + + for (wordnum = 0; wordnum < WORDS_PER_CHUNK; wordnum++) + { + bitmapword w = apage->words[wordnum]; + + if (w != 0) + { + bitmapword neww = w; + BlockNumber pg; + int bitnum; + + pg = apage->blockno + (wordnum * BITS_PER_BITMAPWORD); + bitnum = 0; + while (w != 0) + { + if (w & 1) + { + if (!tbm_page_is_lossy(b, pg) && + tbm_find_pageentry(b, pg) == NULL) + { + /* Page is not in b at all, lose lossy bit */ + neww &= ~((bitmapword) 1 << bitnum); + } + } + pg++; + bitnum++; + w >>= 1; + } + apage->words[wordnum] = neww; + if (neww != 0) + candelete = false; + } + } + return candelete; + } + else if (tbm_page_is_lossy(b, apage->blockno)) + { + /* + * Some of the tuples in 'a' might not satisfy the quals for 'b', but + * because the page 'b' is lossy, we don't know which ones. Therefore + * we mark 'a' as requiring rechecks, to indicate that at most those + * tuples set in 'a' are matches. + */ + apage->recheck = true; + return false; + } + else + { + bool candelete = true; + + bpage = tbm_find_pageentry(b, apage->blockno); + if (bpage != NULL) + { + /* Both pages are exact, merge at the bit level */ + Assert(!bpage->ischunk); + for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++) + { + apage->words[wordnum] &= bpage->words[wordnum]; + if (apage->words[wordnum] != 0) + candelete = false; + } + apage->recheck |= bpage->recheck; + } + /* If there is no matching b page, we can just delete the a page */ + return candelete; + } +} + +/* + * tbm_is_empty - is a TIDBitmap completely empty? + */ +bool +tbm_is_empty(const TIDBitmap *tbm) +{ + return (tbm->nentries == 0); +} + +/* + * tbm_begin_iterate - prepare to iterate through a TIDBitmap + * + * The TBMIterator struct is created in the caller's memory context. + * For a clean shutdown of the iteration, call tbm_end_iterate; but it's + * okay to just allow the memory context to be released, too. It is caller's + * responsibility not to touch the TBMIterator anymore once the TIDBitmap + * is freed. + * + * NB: after this is called, it is no longer allowed to modify the contents + * of the bitmap. However, you can call this multiple times to scan the + * contents repeatedly, including parallel scans. + */ +TBMIterator * +tbm_begin_iterate(TIDBitmap *tbm) +{ + TBMIterator *iterator; + + Assert(tbm->iterating != TBM_ITERATING_SHARED); + + /* + * Create the TBMIterator struct, with enough trailing space to serve the + * needs of the TBMIterateResult sub-struct. + */ + iterator = (TBMIterator *) palloc(sizeof(TBMIterator) + + MAX_TUPLES_PER_PAGE * sizeof(OffsetNumber)); + iterator->tbm = tbm; + + /* + * Initialize iteration pointers. + */ + iterator->spageptr = 0; + iterator->schunkptr = 0; + iterator->schunkbit = 0; + + /* + * If we have a hashtable, create and fill the sorted page lists, unless + * we already did that for a previous iterator. Note that the lists are + * attached to the bitmap not the iterator, so they can be used by more + * than one iterator. + */ + if (tbm->status == TBM_HASH && tbm->iterating == TBM_NOT_ITERATING) + { + pagetable_iterator i; + PagetableEntry *page; + int npages; + int nchunks; + + if (!tbm->spages && tbm->npages > 0) + tbm->spages = (PagetableEntry **) + MemoryContextAlloc(tbm->mcxt, + tbm->npages * sizeof(PagetableEntry *)); + if (!tbm->schunks && tbm->nchunks > 0) + tbm->schunks = (PagetableEntry **) + MemoryContextAlloc(tbm->mcxt, + tbm->nchunks * sizeof(PagetableEntry *)); + + npages = nchunks = 0; + pagetable_start_iterate(tbm->pagetable, &i); + while ((page = pagetable_iterate(tbm->pagetable, &i)) != NULL) + { + if (page->ischunk) + tbm->schunks[nchunks++] = page; + else + tbm->spages[npages++] = page; + } + Assert(npages == tbm->npages); + Assert(nchunks == tbm->nchunks); + if (npages > 1) + qsort(tbm->spages, npages, sizeof(PagetableEntry *), + tbm_comparator); + if (nchunks > 1) + qsort(tbm->schunks, nchunks, sizeof(PagetableEntry *), + tbm_comparator); + } + + tbm->iterating = TBM_ITERATING_PRIVATE; + + return iterator; +} + +/* + * tbm_prepare_shared_iterate - prepare shared iteration state for a TIDBitmap. + * + * The necessary shared state will be allocated from the DSA passed to + * tbm_create, so that multiple processes can attach to it and iterate jointly. + * + * This will convert the pagetable hash into page and chunk array of the index + * into pagetable array. + */ +dsa_pointer +tbm_prepare_shared_iterate(TIDBitmap *tbm) +{ + dsa_pointer dp; + TBMSharedIteratorState *istate; + PTEntryArray *ptbase = NULL; + PTIterationArray *ptpages = NULL; + PTIterationArray *ptchunks = NULL; + + Assert(tbm->dsa != NULL); + Assert(tbm->iterating != TBM_ITERATING_PRIVATE); + + /* + * Allocate TBMSharedIteratorState from DSA to hold the shared members and + * lock, this will also be used by multiple worker for shared iterate. + */ + dp = dsa_allocate0(tbm->dsa, sizeof(TBMSharedIteratorState)); + istate = dsa_get_address(tbm->dsa, dp); + + /* + * If we're not already iterating, create and fill the sorted page lists. + * (If we are, the sorted page lists are already stored in the TIDBitmap, + * and we can just reuse them.) + */ + if (tbm->iterating == TBM_NOT_ITERATING) + { + pagetable_iterator i; + PagetableEntry *page; + int idx; + int npages; + int nchunks; + + /* + * Allocate the page and chunk array memory from the DSA to share + * across multiple processes. + */ + if (tbm->npages) + { + tbm->ptpages = dsa_allocate(tbm->dsa, sizeof(PTIterationArray) + + tbm->npages * sizeof(int)); + ptpages = dsa_get_address(tbm->dsa, tbm->ptpages); + pg_atomic_init_u32(&ptpages->refcount, 0); + } + if (tbm->nchunks) + { + tbm->ptchunks = dsa_allocate(tbm->dsa, sizeof(PTIterationArray) + + tbm->nchunks * sizeof(int)); + ptchunks = dsa_get_address(tbm->dsa, tbm->ptchunks); + pg_atomic_init_u32(&ptchunks->refcount, 0); + } + + /* + * If TBM status is TBM_HASH then iterate over the pagetable and + * convert it to page and chunk arrays. But if it's in the + * TBM_ONE_PAGE mode then directly allocate the space for one entry + * from the DSA. + */ + npages = nchunks = 0; + if (tbm->status == TBM_HASH) + { + ptbase = dsa_get_address(tbm->dsa, tbm->dsapagetable); + + pagetable_start_iterate(tbm->pagetable, &i); + while ((page = pagetable_iterate(tbm->pagetable, &i)) != NULL) + { + idx = page - ptbase->ptentry; + if (page->ischunk) + ptchunks->index[nchunks++] = idx; + else + ptpages->index[npages++] = idx; + } + + Assert(npages == tbm->npages); + Assert(nchunks == tbm->nchunks); + } + else if (tbm->status == TBM_ONE_PAGE) + { + /* + * In one page mode allocate the space for one pagetable entry, + * initialize it, and directly store its index (i.e. 0) in the + * page array. + */ + tbm->dsapagetable = dsa_allocate(tbm->dsa, sizeof(PTEntryArray) + + sizeof(PagetableEntry)); + ptbase = dsa_get_address(tbm->dsa, tbm->dsapagetable); + memcpy(ptbase->ptentry, &tbm->entry1, sizeof(PagetableEntry)); + ptpages->index[0] = 0; + } + + if (ptbase != NULL) + pg_atomic_init_u32(&ptbase->refcount, 0); + if (npages > 1) + qsort_arg((void *) (ptpages->index), npages, sizeof(int), + tbm_shared_comparator, (void *) ptbase->ptentry); + if (nchunks > 1) + qsort_arg((void *) (ptchunks->index), nchunks, sizeof(int), + tbm_shared_comparator, (void *) ptbase->ptentry); + } + + /* + * Store the TBM members in the shared state so that we can share them + * across multiple processes. + */ + istate->nentries = tbm->nentries; + istate->maxentries = tbm->maxentries; + istate->npages = tbm->npages; + istate->nchunks = tbm->nchunks; + istate->pagetable = tbm->dsapagetable; + istate->spages = tbm->ptpages; + istate->schunks = tbm->ptchunks; + + ptbase = dsa_get_address(tbm->dsa, tbm->dsapagetable); + ptpages = dsa_get_address(tbm->dsa, tbm->ptpages); + ptchunks = dsa_get_address(tbm->dsa, tbm->ptchunks); + + /* + * For every shared iterator, referring to pagetable and iterator array, + * increase the refcount by 1 so that while freeing the shared iterator we + * don't free pagetable and iterator array until its refcount becomes 0. + */ + if (ptbase != NULL) + pg_atomic_add_fetch_u32(&ptbase->refcount, 1); + if (ptpages != NULL) + pg_atomic_add_fetch_u32(&ptpages->refcount, 1); + if (ptchunks != NULL) + pg_atomic_add_fetch_u32(&ptchunks->refcount, 1); + + /* Initialize the iterator lock */ + LWLockInitialize(&istate->lock, LWTRANCHE_SHARED_TIDBITMAP); + + /* Initialize the shared iterator state */ + istate->schunkbit = 0; + istate->schunkptr = 0; + istate->spageptr = 0; + + tbm->iterating = TBM_ITERATING_SHARED; + + return dp; +} + +/* + * tbm_extract_page_tuple - extract the tuple offsets from a page + * + * The extracted offsets are stored into TBMIterateResult. + */ +static inline int +tbm_extract_page_tuple(PagetableEntry *page, TBMIterateResult *output) +{ + int wordnum; + int ntuples = 0; + + for (wordnum = 0; wordnum < WORDS_PER_PAGE; wordnum++) + { + bitmapword w = page->words[wordnum]; + + if (w != 0) + { + int off = wordnum * BITS_PER_BITMAPWORD + 1; + + while (w != 0) + { + if (w & 1) + output->offsets[ntuples++] = (OffsetNumber) off; + off++; + w >>= 1; + } + } + } + + return ntuples; +} + +/* + * tbm_advance_schunkbit - Advance the schunkbit + */ +static inline void +tbm_advance_schunkbit(PagetableEntry *chunk, int *schunkbitp) +{ + int schunkbit = *schunkbitp; + + while (schunkbit < PAGES_PER_CHUNK) + { + int wordnum = WORDNUM(schunkbit); + int bitnum = BITNUM(schunkbit); + + if ((chunk->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + break; + schunkbit++; + } + + *schunkbitp = schunkbit; +} + +/* + * tbm_iterate - scan through next page of a TIDBitmap + * + * Returns a TBMIterateResult representing one page, or NULL if there are + * no more pages to scan. Pages are guaranteed to be delivered in numerical + * order. If result->ntuples < 0, then the bitmap is "lossy" and failed to + * remember the exact tuples to look at on this page --- the caller must + * examine all tuples on the page and check if they meet the intended + * condition. If result->recheck is true, only the indicated tuples need + * be examined, but the condition must be rechecked anyway. (For ease of + * testing, recheck is always set true when ntuples < 0.) + */ +TBMIterateResult * +tbm_iterate(TBMIterator *iterator) +{ + TIDBitmap *tbm = iterator->tbm; + TBMIterateResult *output = &(iterator->output); + + Assert(tbm->iterating == TBM_ITERATING_PRIVATE); + + /* + * If lossy chunk pages remain, make sure we've advanced schunkptr/ + * schunkbit to the next set bit. + */ + while (iterator->schunkptr < tbm->nchunks) + { + PagetableEntry *chunk = tbm->schunks[iterator->schunkptr]; + int schunkbit = iterator->schunkbit; + + tbm_advance_schunkbit(chunk, &schunkbit); + if (schunkbit < PAGES_PER_CHUNK) + { + iterator->schunkbit = schunkbit; + break; + } + /* advance to next chunk */ + iterator->schunkptr++; + iterator->schunkbit = 0; + } + + /* + * If both chunk and per-page data remain, must output the numerically + * earlier page. + */ + if (iterator->schunkptr < tbm->nchunks) + { + PagetableEntry *chunk = tbm->schunks[iterator->schunkptr]; + BlockNumber chunk_blockno; + + chunk_blockno = chunk->blockno + iterator->schunkbit; + if (iterator->spageptr >= tbm->npages || + chunk_blockno < tbm->spages[iterator->spageptr]->blockno) + { + /* Return a lossy page indicator from the chunk */ + output->blockno = chunk_blockno; + output->ntuples = -1; + output->recheck = true; + iterator->schunkbit++; + return output; + } + } + + if (iterator->spageptr < tbm->npages) + { + PagetableEntry *page; + int ntuples; + + /* In TBM_ONE_PAGE state, we don't allocate an spages[] array */ + if (tbm->status == TBM_ONE_PAGE) + page = &tbm->entry1; + else + page = tbm->spages[iterator->spageptr]; + + /* scan bitmap to extract individual offset numbers */ + ntuples = tbm_extract_page_tuple(page, output); + output->blockno = page->blockno; + output->ntuples = ntuples; + output->recheck = page->recheck; + iterator->spageptr++; + return output; + } + + /* Nothing more in the bitmap */ + return NULL; +} + +/* + * tbm_shared_iterate - scan through next page of a TIDBitmap + * + * As above, but this will iterate using an iterator which is shared + * across multiple processes. We need to acquire the iterator LWLock, + * before accessing the shared members. + */ +TBMIterateResult * +tbm_shared_iterate(TBMSharedIterator *iterator) +{ + TBMIterateResult *output = &iterator->output; + TBMSharedIteratorState *istate = iterator->state; + PagetableEntry *ptbase = NULL; + int *idxpages = NULL; + int *idxchunks = NULL; + + if (iterator->ptbase != NULL) + ptbase = iterator->ptbase->ptentry; + if (iterator->ptpages != NULL) + idxpages = iterator->ptpages->index; + if (iterator->ptchunks != NULL) + idxchunks = iterator->ptchunks->index; + + /* Acquire the LWLock before accessing the shared members */ + LWLockAcquire(&istate->lock, LW_EXCLUSIVE); + + /* + * If lossy chunk pages remain, make sure we've advanced schunkptr/ + * schunkbit to the next set bit. + */ + while (istate->schunkptr < istate->nchunks) + { + PagetableEntry *chunk = &ptbase[idxchunks[istate->schunkptr]]; + int schunkbit = istate->schunkbit; + + tbm_advance_schunkbit(chunk, &schunkbit); + if (schunkbit < PAGES_PER_CHUNK) + { + istate->schunkbit = schunkbit; + break; + } + /* advance to next chunk */ + istate->schunkptr++; + istate->schunkbit = 0; + } + + /* + * If both chunk and per-page data remain, must output the numerically + * earlier page. + */ + if (istate->schunkptr < istate->nchunks) + { + PagetableEntry *chunk = &ptbase[idxchunks[istate->schunkptr]]; + BlockNumber chunk_blockno; + + chunk_blockno = chunk->blockno + istate->schunkbit; + + if (istate->spageptr >= istate->npages || + chunk_blockno < ptbase[idxpages[istate->spageptr]].blockno) + { + /* Return a lossy page indicator from the chunk */ + output->blockno = chunk_blockno; + output->ntuples = -1; + output->recheck = true; + istate->schunkbit++; + + LWLockRelease(&istate->lock); + return output; + } + } + + if (istate->spageptr < istate->npages) + { + PagetableEntry *page = &ptbase[idxpages[istate->spageptr]]; + int ntuples; + + /* scan bitmap to extract individual offset numbers */ + ntuples = tbm_extract_page_tuple(page, output); + output->blockno = page->blockno; + output->ntuples = ntuples; + output->recheck = page->recheck; + istate->spageptr++; + + LWLockRelease(&istate->lock); + + return output; + } + + LWLockRelease(&istate->lock); + + /* Nothing more in the bitmap */ + return NULL; +} + +/* + * tbm_end_iterate - finish an iteration over a TIDBitmap + * + * Currently this is just a pfree, but it might do more someday. (For + * instance, it could be useful to count open iterators and allow the + * bitmap to return to read/write status when there are no more iterators.) + */ +void +tbm_end_iterate(TBMIterator *iterator) +{ + pfree(iterator); +} + +/* + * tbm_end_shared_iterate - finish a shared iteration over a TIDBitmap + * + * This doesn't free any of the shared state associated with the iterator, + * just our backend-private state. + */ +void +tbm_end_shared_iterate(TBMSharedIterator *iterator) +{ + pfree(iterator); +} + +/* + * tbm_find_pageentry - find a PagetableEntry for the pageno + * + * Returns NULL if there is no non-lossy entry for the pageno. + */ +static const PagetableEntry * +tbm_find_pageentry(const TIDBitmap *tbm, BlockNumber pageno) +{ + const PagetableEntry *page; + + if (tbm->nentries == 0) /* in case pagetable doesn't exist */ + return NULL; + + if (tbm->status == TBM_ONE_PAGE) + { + page = &tbm->entry1; + if (page->blockno != pageno) + return NULL; + Assert(!page->ischunk); + return page; + } + + page = pagetable_lookup(tbm->pagetable, pageno); + if (page == NULL) + return NULL; + if (page->ischunk) + return NULL; /* don't want a lossy chunk header */ + return page; +} + +/* + * tbm_get_pageentry - find or create a PagetableEntry for the pageno + * + * If new, the entry is marked as an exact (non-chunk) entry. + * + * This may cause the table to exceed the desired memory size. It is + * up to the caller to call tbm_lossify() at the next safe point if so. + */ +static PagetableEntry * +tbm_get_pageentry(TIDBitmap *tbm, BlockNumber pageno) +{ + PagetableEntry *page; + bool found; + + if (tbm->status == TBM_EMPTY) + { + /* Use the fixed slot */ + page = &tbm->entry1; + found = false; + tbm->status = TBM_ONE_PAGE; + } + else + { + if (tbm->status == TBM_ONE_PAGE) + { + page = &tbm->entry1; + if (page->blockno == pageno) + return page; + /* Time to switch from one page to a hashtable */ + tbm_create_pagetable(tbm); + } + + /* Look up or create an entry */ + page = pagetable_insert(tbm->pagetable, pageno, &found); + } + + /* Initialize it if not present before */ + if (!found) + { + char oldstatus = page->status; + + MemSet(page, 0, sizeof(PagetableEntry)); + page->status = oldstatus; + page->blockno = pageno; + /* must count it too */ + tbm->nentries++; + tbm->npages++; + } + + return page; +} + +/* + * tbm_page_is_lossy - is the page marked as lossily stored? + */ +static bool +tbm_page_is_lossy(const TIDBitmap *tbm, BlockNumber pageno) +{ + PagetableEntry *page; + BlockNumber chunk_pageno; + int bitno; + + /* we can skip the lookup if there are no lossy chunks */ + if (tbm->nchunks == 0) + return false; + Assert(tbm->status == TBM_HASH); + + bitno = pageno % PAGES_PER_CHUNK; + chunk_pageno = pageno - bitno; + + page = pagetable_lookup(tbm->pagetable, chunk_pageno); + + if (page != NULL && page->ischunk) + { + int wordnum = WORDNUM(bitno); + int bitnum = BITNUM(bitno); + + if ((page->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) + return true; + } + return false; +} + +/* + * tbm_mark_page_lossy - mark the page number as lossily stored + * + * This may cause the table to exceed the desired memory size. It is + * up to the caller to call tbm_lossify() at the next safe point if so. + */ +static void +tbm_mark_page_lossy(TIDBitmap *tbm, BlockNumber pageno) +{ + PagetableEntry *page; + bool found; + BlockNumber chunk_pageno; + int bitno; + int wordnum; + int bitnum; + + /* We force the bitmap into hashtable mode whenever it's lossy */ + if (tbm->status != TBM_HASH) + tbm_create_pagetable(tbm); + + bitno = pageno % PAGES_PER_CHUNK; + chunk_pageno = pageno - bitno; + + /* + * Remove any extant non-lossy entry for the page. If the page is its own + * chunk header, however, we skip this and handle the case below. + */ + if (bitno != 0) + { + if (pagetable_delete(tbm->pagetable, pageno)) + { + /* It was present, so adjust counts */ + tbm->nentries--; + tbm->npages--; /* assume it must have been non-lossy */ + } + } + + /* Look up or create entry for chunk-header page */ + page = pagetable_insert(tbm->pagetable, chunk_pageno, &found); + + /* Initialize it if not present before */ + if (!found) + { + char oldstatus = page->status; + + MemSet(page, 0, sizeof(PagetableEntry)); + page->status = oldstatus; + page->blockno = chunk_pageno; + page->ischunk = true; + /* must count it too */ + tbm->nentries++; + tbm->nchunks++; + } + else if (!page->ischunk) + { + char oldstatus = page->status; + + /* chunk header page was formerly non-lossy, make it lossy */ + MemSet(page, 0, sizeof(PagetableEntry)); + page->status = oldstatus; + page->blockno = chunk_pageno; + page->ischunk = true; + /* we assume it had some tuple bit(s) set, so mark it lossy */ + page->words[0] = ((bitmapword) 1 << 0); + /* adjust counts */ + tbm->nchunks++; + tbm->npages--; + } + + /* Now set the original target page's bit */ + wordnum = WORDNUM(bitno); + bitnum = BITNUM(bitno); + page->words[wordnum] |= ((bitmapword) 1 << bitnum); +} + +/* + * tbm_lossify - lose some information to get back under the memory limit + */ +static void +tbm_lossify(TIDBitmap *tbm) +{ + pagetable_iterator i; + PagetableEntry *page; + + /* + * XXX Really stupid implementation: this just lossifies pages in + * essentially random order. We should be paying some attention to the + * number of bits set in each page, instead. + * + * Since we are called as soon as nentries exceeds maxentries, we should + * push nentries down to significantly less than maxentries, or else we'll + * just end up doing this again very soon. We shoot for maxentries/2. + */ + Assert(tbm->iterating == TBM_NOT_ITERATING); + Assert(tbm->status == TBM_HASH); + + pagetable_start_iterate_at(tbm->pagetable, &i, tbm->lossify_start); + while ((page = pagetable_iterate(tbm->pagetable, &i)) != NULL) + { + if (page->ischunk) + continue; /* already a chunk header */ + + /* + * If the page would become a chunk header, we won't save anything by + * converting it to lossy, so skip it. + */ + if ((page->blockno % PAGES_PER_CHUNK) == 0) + continue; + + /* This does the dirty work ... */ + tbm_mark_page_lossy(tbm, page->blockno); + + if (tbm->nentries <= tbm->maxentries / 2) + { + /* + * We have made enough room. Remember where to start lossifying + * next round, so we evenly iterate over the hashtable. + */ + tbm->lossify_start = i.cur; + break; + } + + /* + * Note: tbm_mark_page_lossy may have inserted a lossy chunk into the + * hashtable and may have deleted the non-lossy chunk. We can + * continue the same hash table scan, since failure to visit one + * element or visiting the newly inserted element, isn't fatal. + */ + } + + /* + * With a big bitmap and small work_mem, it's possible that we cannot get + * under maxentries. Again, if that happens, we'd end up uselessly + * calling tbm_lossify over and over. To prevent this from becoming a + * performance sink, force maxentries up to at least double the current + * number of entries. (In essence, we're admitting inability to fit + * within work_mem when we do this.) Note that this test will not fire if + * we broke out of the loop early; and if we didn't, the current number of + * entries is simply not reducible any further. + */ + if (tbm->nentries > tbm->maxentries / 2) + tbm->maxentries = Min(tbm->nentries, (INT_MAX - 1) / 2) * 2; +} + +/* + * qsort comparator to handle PagetableEntry pointers. + */ +static int +tbm_comparator(const void *left, const void *right) +{ + BlockNumber l = (*((PagetableEntry *const *) left))->blockno; + BlockNumber r = (*((PagetableEntry *const *) right))->blockno; + + if (l < r) + return -1; + else if (l > r) + return 1; + return 0; +} + +/* + * As above, but this will get index into PagetableEntry array. Therefore, + * it needs to get actual PagetableEntry using the index before comparing the + * blockno. + */ +static int +tbm_shared_comparator(const void *left, const void *right, void *arg) +{ + PagetableEntry *base = (PagetableEntry *) arg; + PagetableEntry *lpage = &base[*(int *) left]; + PagetableEntry *rpage = &base[*(int *) right]; + + if (lpage->blockno < rpage->blockno) + return -1; + else if (lpage->blockno > rpage->blockno) + return 1; + return 0; +} + +/* + * tbm_attach_shared_iterate + * + * Allocate a backend-private iterator and attach the shared iterator state + * to it so that multiple processed can iterate jointly. + * + * We also converts the DSA pointers to local pointers and store them into + * our private iterator. + */ +TBMSharedIterator * +tbm_attach_shared_iterate(dsa_area *dsa, dsa_pointer dp) +{ + TBMSharedIterator *iterator; + TBMSharedIteratorState *istate; + + /* + * Create the TBMSharedIterator struct, with enough trailing space to + * serve the needs of the TBMIterateResult sub-struct. + */ + iterator = (TBMSharedIterator *) palloc0(sizeof(TBMSharedIterator) + + MAX_TUPLES_PER_PAGE * sizeof(OffsetNumber)); + + istate = (TBMSharedIteratorState *) dsa_get_address(dsa, dp); + + iterator->state = istate; + + iterator->ptbase = dsa_get_address(dsa, istate->pagetable); + + if (istate->npages) + iterator->ptpages = dsa_get_address(dsa, istate->spages); + if (istate->nchunks) + iterator->ptchunks = dsa_get_address(dsa, istate->schunks); + + return iterator; +} + +/* + * pagetable_allocate + * + * Callback function for allocating the memory for hashtable elements. + * Allocate memory for hashtable elements, using DSA if available. + */ +static inline void * +pagetable_allocate(pagetable_hash *pagetable, Size size) +{ + TIDBitmap *tbm = (TIDBitmap *) pagetable->private_data; + PTEntryArray *ptbase; + + if (tbm->dsa == NULL) + return MemoryContextAllocExtended(pagetable->ctx, size, + MCXT_ALLOC_HUGE | MCXT_ALLOC_ZERO); + + /* + * Save the dsapagetable reference in dsapagetableold before allocating + * new memory so that pagetable_free can free the old entry. + */ + tbm->dsapagetableold = tbm->dsapagetable; + tbm->dsapagetable = dsa_allocate_extended(tbm->dsa, + sizeof(PTEntryArray) + size, + DSA_ALLOC_HUGE | DSA_ALLOC_ZERO); + ptbase = dsa_get_address(tbm->dsa, tbm->dsapagetable); + + return ptbase->ptentry; +} + +/* + * pagetable_free + * + * Callback function for freeing hash table elements. + */ +static inline void +pagetable_free(pagetable_hash *pagetable, void *pointer) +{ + TIDBitmap *tbm = (TIDBitmap *) pagetable->private_data; + + /* pfree the input pointer if DSA is not available */ + if (tbm->dsa == NULL) + pfree(pointer); + else if (DsaPointerIsValid(tbm->dsapagetableold)) + { + dsa_free(tbm->dsa, tbm->dsapagetableold); + tbm->dsapagetableold = InvalidDsaPointer; + } +} + +/* + * tbm_calculate_entries + * + * Estimate number of hashtable entries we can have within maxbytes. + */ +long +tbm_calculate_entries(double maxbytes) +{ + long nbuckets; + + /* + * Estimate number of hashtable entries we can have within maxbytes. This + * estimates the hash cost as sizeof(PagetableEntry), which is good enough + * for our purpose. Also count an extra Pointer per entry for the arrays + * created during iteration readout. + */ + nbuckets = maxbytes / + (sizeof(PagetableEntry) + sizeof(Pointer) + sizeof(Pointer)); + nbuckets = Min(nbuckets, INT_MAX - 1); /* safety limit */ + nbuckets = Max(nbuckets, 16); /* sanity limit */ + + return nbuckets; +} diff --git a/src/backend/nodes/value.c b/src/backend/nodes/value.c new file mode 100644 index 0000000..5774a68 --- /dev/null +++ b/src/backend/nodes/value.c @@ -0,0 +1,83 @@ +/*------------------------------------------------------------------------- + * + * value.c + * implementation of value nodes + * + * + * Copyright (c) 2003-2022, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/backend/nodes/value.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/value.h" + +/* + * makeInteger + */ +Integer * +makeInteger(int i) +{ + Integer *v = makeNode(Integer); + + v->ival = i; + return v; +} + +/* + * makeFloat + * + * Caller is responsible for passing a palloc'd string. + */ +Float * +makeFloat(char *numericStr) +{ + Float *v = makeNode(Float); + + v->fval = numericStr; + return v; +} + +/* + * makeBoolean + */ +Boolean * +makeBoolean(bool val) +{ + Boolean *v = makeNode(Boolean); + + v->boolval = val; + return v; +} + +/* + * makeString + * + * Caller is responsible for passing a palloc'd string. + */ +String * +makeString(char *str) +{ + String *v = makeNode(String); + + v->sval = str; + return v; +} + +/* + * makeBitString + * + * Caller is responsible for passing a palloc'd string. + */ +BitString * +makeBitString(char *str) +{ + BitString *v = makeNode(BitString); + + v->bsval = str; + return v; +} |