summaryrefslogtreecommitdiffstats
path: root/src/backend/nodes
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:19:15 +0000
commit6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch)
tree657d8194422a5daccecfd42d654b8a245ef7b4c8 /src/backend/nodes
parentInitial commit. (diff)
downloadpostgresql-13-upstream.tar.xz
postgresql-13-upstream.zip
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/nodes')
-rw-r--r--src/backend/nodes/Makefile32
-rw-r--r--src/backend/nodes/README80
-rw-r--r--src/backend/nodes/bitmapset.c1192
-rw-r--r--src/backend/nodes/copyfuncs.c5725
-rw-r--r--src/backend/nodes/equalfuncs.c3771
-rw-r--r--src/backend/nodes/extensible.c143
-rw-r--r--src/backend/nodes/list.c1508
-rw-r--r--src/backend/nodes/makefuncs.c814
-rw-r--r--src/backend/nodes/nodeFuncs.c4052
-rw-r--r--src/backend/nodes/nodes.c31
-rw-r--r--src/backend/nodes/outfuncs.c4403
-rw-r--r--src/backend/nodes/params.c364
-rw-r--r--src/backend/nodes/print.c507
-rw-r--r--src/backend/nodes/read.c462
-rw-r--r--src/backend/nodes/readfuncs.c3048
-rw-r--r--src/backend/nodes/tidbitmap.c1561
-rw-r--r--src/backend/nodes/value.c75
17 files changed, 27768 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..dcd66d7
--- /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..2719ea4
--- /dev/null
+++ b/src/backend/nodes/bitmapset.c
@@ -0,0 +1,1192 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, 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?
+ */
+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..256ab54
--- /dev/null
+++ b/src/backend/nodes/copyfuncs.c
@@ -0,0 +1,5725 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, 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 a pointer to a simple palloc'd object of size sz */
+#define COPY_POINTER_FIELD(fldname, sz) \
+ do { \
+ Size _size = (sz); \
+ 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(rootResultRelations);
+ 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(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_SCALAR_FIELD(resultRelIndex);
+ COPY_SCALAR_FIELD(rootResultRelIndex);
+ COPY_NODE_FIELD(plans);
+ 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(onConflictWhere);
+ COPY_SCALAR_FIELD(exclRelRTI);
+ COPY_NODE_FIELD(exclRelTlist);
+
+ 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(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);
+ if (from->numCols > 0)
+ {
+ 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(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;
+}
+
+/*
+ * _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);
+
+ 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(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);
+ if (numCols > 0)
+ {
+ 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;
+}
+
+
+/*
+ * 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);
+ if (from->numCols > 0)
+ {
+ 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);
+ if (from->partNumCols > 0)
+ {
+ 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);
+ if (from->ordNumCols > 0)
+ {
+ 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_SCALAR_FIELD(startInRangeFunc);
+ COPY_SCALAR_FIELD(endInRangeFunc);
+ COPY_SCALAR_FIELD(inRangeColl);
+ COPY_SCALAR_FIELD(inRangeAsc);
+ COPY_SCALAR_FIELD(inRangeNullsFirst);
+
+ 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_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(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(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(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(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);
+
+ 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(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_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 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_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 A_Expr *
+_copyAExpr(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 *
+_copyAConst(const A_Const *from)
+{
+ A_Const *newnode = makeNode(A_Const);
+
+ /* This part must duplicate _copyValue */
+ COPY_SCALAR_FIELD(val.type);
+ switch (from->val.type)
+ {
+ case T_Integer:
+ COPY_SCALAR_FIELD(val.val.ival);
+ break;
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ COPY_STRING_FIELD(val.val.str);
+ break;
+ case T_Null:
+ /* nothing to do */
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) from->val.type);
+ 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_SCALAR_FIELD(agg_within_group);
+ COPY_SCALAR_FIELD(agg_star);
+ COPY_SCALAR_FIELD(agg_distinct);
+ COPY_SCALAR_FIELD(func_variadic);
+ COPY_NODE_FIELD(over);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
+static A_Star *
+_copyAStar(const A_Star *from)
+{
+ A_Star *newnode = makeNode(A_Star);
+
+ return newnode;
+}
+
+static A_Indices *
+_copyAIndices(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 ColumnDef *
+_copyColumnDef(const ColumnDef *from)
+{
+ ColumnDef *newnode = makeNode(ColumnDef);
+
+ COPY_STRING_FIELD(colname);
+ COPY_NODE_FIELD(typeName);
+ 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_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(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_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_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_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 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_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 AlterTableStmt *
+_copyAlterTableStmt(const AlterTableStmt *from)
+{
+ AlterTableStmt *newnode = makeNode(AlterTableStmt);
+
+ COPY_NODE_FIELD(relation);
+ COPY_NODE_FIELD(cmds);
+ COPY_SCALAR_FIELD(relkind);
+ 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);
+
+ 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_SCALAR_FIELD(behavior);
+
+ return newnode;
+}
+
+static ObjectWithArgs *
+_copyObjectWithArgs(const ObjectWithArgs *from)
+{
+ ObjectWithArgs *newnode = makeNode(ObjectWithArgs);
+
+ COPY_NODE_FIELD(objname);
+ COPY_NODE_FIELD(objargs);
+ 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);
+
+ return newnode;
+}
+
+static ClusterStmt *
+_copyClusterStmt(const ClusterStmt *from)
+{
+ ClusterStmt *newnode = makeNode(ClusterStmt);
+
+ COPY_NODE_FIELD(relation);
+ COPY_STRING_FIELD(indexname);
+ COPY_SCALAR_FIELD(options);
+
+ 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(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(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);
+
+ 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 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(relkind);
+ 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_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_SCALAR_FIELD(isconstraint);
+ 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_SCALAR_FIELD(options);
+ COPY_SCALAR_FIELD(concurrent);
+
+ 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);
+
+ return newnode;
+}
+
+static CreatePublicationStmt *
+_copyCreatePublicationStmt(const CreatePublicationStmt *from)
+{
+ CreatePublicationStmt *newnode = makeNode(CreatePublicationStmt);
+
+ COPY_STRING_FIELD(pubname);
+ COPY_NODE_FIELD(options);
+ COPY_NODE_FIELD(tables);
+ 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(tables);
+ COPY_SCALAR_FIELD(for_all_tables);
+ COPY_SCALAR_FIELD(tableAction);
+
+ 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 Value *
+_copyValue(const Value *from)
+{
+ Value *newnode = makeNode(Value);
+
+ /* See also _copyAConst when changing this code! */
+
+ COPY_SCALAR_FIELD(type);
+ switch (from->type)
+ {
+ case T_Integer:
+ COPY_SCALAR_FIELD(val.ival);
+ break;
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ COPY_STRING_FIELD(val.str);
+ break;
+ case T_Null:
+ /* nothing to do */
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) from->type);
+ break;
+ }
+ 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_SCALAR_FIELD might work for these, but let's not assume that */
+ memcpy(newnode->conkey, from->conkey, sizeof(newnode->conkey));
+ memcpy(newnode->confkey, from->confkey, sizeof(newnode->confkey));
+ memcpy(newnode->conpfeqop, from->conpfeqop, sizeof(newnode->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_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_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:
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ case T_Null:
+ retval = _copyValue(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_SelectStmt:
+ retval = _copySelectStmt(from);
+ break;
+ case T_SetOperationStmt:
+ retval = _copySetOperationStmt(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_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 = _copyAExpr(from);
+ break;
+ case T_ColumnRef:
+ retval = _copyColumnRef(from);
+ break;
+ case T_ParamRef:
+ retval = _copyParamRef(from);
+ break;
+ case T_A_Const:
+ retval = _copyAConst(from);
+ break;
+ case T_FuncCall:
+ retval = _copyFuncCall(from);
+ break;
+ case T_A_Star:
+ retval = _copyAStar(from);
+ break;
+ case T_A_Indices:
+ retval = _copyAIndices(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_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_CommonTableExpr:
+ retval = _copyCommonTableExpr(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;
+
+ /*
+ * 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..8dc50ae
--- /dev/null
+++ b/src/backend/nodes/equalfuncs.c
@@ -0,0 +1,3771 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, 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 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_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(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;
+
+ 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(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_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_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_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
+_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_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
+_equalAlterTableStmt(const AlterTableStmt *a, const AlterTableStmt *b)
+{
+ COMPARE_NODE_FIELD(relation);
+ COMPARE_NODE_FIELD(cmds);
+ COMPARE_SCALAR_FIELD(relkind);
+ 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);
+
+ 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_SCALAR_FIELD(behavior);
+
+ return true;
+}
+
+static bool
+_equalObjectWithArgs(const ObjectWithArgs *a, const ObjectWithArgs *b)
+{
+ COMPARE_NODE_FIELD(objname);
+ COMPARE_NODE_FIELD(objargs);
+ 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);
+
+ return true;
+}
+
+static bool
+_equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
+{
+ COMPARE_NODE_FIELD(relation);
+ COMPARE_STRING_FIELD(indexname);
+ COMPARE_SCALAR_FIELD(options);
+
+ 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(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(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);
+
+ 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
+_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(relkind);
+ 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_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_SCALAR_FIELD(isconstraint);
+ 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_SCALAR_FIELD(options);
+ COMPARE_SCALAR_FIELD(concurrent);
+
+ 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
+_equalCreatePublicationStmt(const CreatePublicationStmt *a,
+ const CreatePublicationStmt *b)
+{
+ COMPARE_STRING_FIELD(pubname);
+ COMPARE_NODE_FIELD(options);
+ COMPARE_NODE_FIELD(tables);
+ 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(tables);
+ COMPARE_SCALAR_FIELD(for_all_tables);
+ COMPARE_SCALAR_FIELD(tableAction);
+
+ 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
+_equalAExpr(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
+_equalAConst(const A_Const *a, const A_Const *b)
+{
+ if (!equal(&a->val, &b->val)) /* hack for in-line Value field */
+ return false;
+ 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_SCALAR_FIELD(agg_within_group);
+ COMPARE_SCALAR_FIELD(agg_star);
+ COMPARE_SCALAR_FIELD(agg_distinct);
+ COMPARE_SCALAR_FIELD(func_variadic);
+ COMPARE_NODE_FIELD(over);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
+static bool
+_equalAStar(const A_Star *a, const A_Star *b)
+{
+ return true;
+}
+
+static bool
+_equalAIndices(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
+_equalColumnDef(const ColumnDef *a, const ColumnDef *b)
+{
+ COMPARE_STRING_FIELD(colname);
+ COMPARE_NODE_FIELD(typeName);
+ 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_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(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(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_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
+_equalCommonTableExpr(const CommonTableExpr *a, const CommonTableExpr *b)
+{
+ COMPARE_STRING_FIELD(ctename);
+ COMPARE_NODE_FIELD(aliascolnames);
+ COMPARE_SCALAR_FIELD(ctematerialized);
+ COMPARE_NODE_FIELD(ctequery);
+ 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
+_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);
+
+ 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
+_equalValue(const Value *a, const Value *b)
+{
+ COMPARE_SCALAR_FIELD(type);
+
+ switch (a->type)
+ {
+ case T_Integer:
+ COMPARE_SCALAR_FIELD(val.ival);
+ break;
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ COMPARE_STRING_FIELD(val.str);
+ break;
+ case T_Null:
+ /* nothing to do */
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) a->type);
+ break;
+ }
+
+ 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:
+ case T_Float:
+ case T_String:
+ case T_BitString:
+ case T_Null:
+ retval = _equalValue(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_SelectStmt:
+ retval = _equalSelectStmt(a, b);
+ break;
+ case T_SetOperationStmt:
+ retval = _equalSetOperationStmt(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_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 = _equalAExpr(a, b);
+ break;
+ case T_ColumnRef:
+ retval = _equalColumnRef(a, b);
+ break;
+ case T_ParamRef:
+ retval = _equalParamRef(a, b);
+ break;
+ case T_A_Const:
+ retval = _equalAConst(a, b);
+ break;
+ case T_FuncCall:
+ retval = _equalFuncCall(a, b);
+ break;
+ case T_A_Star:
+ retval = _equalAStar(a, b);
+ break;
+ case T_A_Indices:
+ retval = _equalAIndices(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_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_CommonTableExpr:
+ retval = _equalCommonTableExpr(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;
+
+ 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..ab04459
--- /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-2020, 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;
+
+ memset(&ctl, 0, sizeof(HASHCTL));
+ ctl.keysize = EXTNODENAME_MAX_LEN;
+ ctl.entrysize = sizeof(ExtensibleNodeEntry);
+
+ *p_htable = hash_create(htable_label, 100, &ctl, HASH_ELEM);
+ }
+
+ 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..80fa8c8
--- /dev/null
+++ b/src/backend/nodes/list.c
@@ -0,0 +1,1508 @@
+/*-------------------------------------------------------------------------
+ *
+ * list.c
+ * implementation for PostgreSQL generic list package
+ *
+ * See comments in pg_list.h.
+ *
+ *
+ * Portions Copyright (c) 1996-2020, 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;
+}
+
+/*
+ * 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);
+
+ lfirst(list_tail(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);
+
+ lfirst_int(list_tail(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);
+
+ lfirst_oid(list_tail(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.
+ */
+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.
+ *
+ * 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);
+
+ lfirst(list_head(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);
+
+ lfirst_int(list_head(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);
+
+ lfirst_oid(list_head(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.
+ */
+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'.
+ */
+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.
+ */
+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.
+ */
+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().
+ */
+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.
+ */
+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.
+ *
+ * This is the opposite of list_delete_first(), but is noticeably cheaper
+ * with a long list, since no data need be moved.
+ */
+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);
+}
+
+/*
+ * 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.
+ *
+ * This function could probably be implemented a lot faster if it is 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.
+ */
+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()
+ */
+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().
+ */
+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.
+ */
+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).
+ */
+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, 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.
+ */
+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 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..b442b5a
--- /dev/null
+++ b/src/backend/nodes/makefuncs.c
@@ -0,0 +1,814 @@
+/*-------------------------------------------------------------------------
+ *
+ * makefuncs.c
+ * creator functions for various nodes. The functions here are for the
+ * most frequently created nodes.
+ *
+ * Portions Copyright (c) 1996-2020, 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(Index 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 = 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(Index 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,
+ Index 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))
+ elog(ERROR, "could not find type OID for relation %u",
+ 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, int location)
+{
+ FuncCall *n = makeNode(FuncCall);
+
+ n->funcname = name;
+ n->args = args;
+ n->agg_order = NIL;
+ n->agg_filter = NULL;
+ n->agg_within_group = false;
+ n->agg_star = false;
+ n->agg_distinct = false;
+ n->func_variadic = false;
+ n->over = NULL;
+ 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 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_ReadyForInserts = isready;
+ 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..1dc873e
--- /dev/null
+++ b/src/backend/nodes/nodeFuncs.c
@@ -0,0 +1,4052 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFuncs.c
+ * Various general-purpose manipulations of Node trees
+ *
+ * Portions Copyright (c) 1996-2020, 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:
+ {
+ const SubscriptingRef *sbsref = (const SubscriptingRef *) expr;
+
+ /* slice and/or store operations yield the container type */
+ if (sbsref->reflowerindexpr || sbsref->refassgnexpr)
+ type = sbsref->refcontainertype;
+ else
+ type = sbsref->refelemtype;
+ }
+ 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:
+ /* typmod is the same for container or element */
+ 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 */
+ }
+
+ /* Avoid recursion for some cases that parser checks not to return a set */
+ if (IsA(node, Aggref))
+ 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:
+ coll = InvalidOid; /* result is always boolean */
+ break;
+ case T_BoolExpr:
+ coll = InvalidOid; /* result is always boolean */
+ 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, result is RECORD or BOOLEAN */
+ coll = InvalidOid;
+ }
+ }
+ 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, result is RECORD or BOOLEAN */
+ coll = InvalidOid;
+ }
+ }
+ 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:
+ coll = InvalidOid; /* result is always composite */
+ 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:
+ coll = InvalidOid; /* result is always composite */
+ 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:
+ coll = InvalidOid; /* result is always composite */
+ break;
+ case T_RowCompareExpr:
+ coll = InvalidOid; /* result is always boolean */
+ 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:
+ coll = InvalidOid; /* result is always boolean */
+ break;
+ case T_BooleanTest:
+ coll = InvalidOid; /* result is always boolean */
+ 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:
+ coll = InvalidOid; /* result is always boolean */
+ break;
+ case T_NextValueExpr:
+ coll = InvalidOid; /* result is always an integer type */
+ 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:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_BoolExpr:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ 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:
+ Assert(!OidIsValid(collation)); /* result is always composite */
+ 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:
+ Assert(!OidIsValid(collation)); /* result is always composite */
+ break;
+ case T_CaseExpr:
+ ((CaseExpr *) expr)->casecollid = collation;
+ break;
+ case T_ArrayExpr:
+ ((ArrayExpr *) expr)->array_collid = collation;
+ break;
+ case T_RowExpr:
+ Assert(!OidIsValid(collation)); /* result is always composite */
+ break;
+ case T_RowCompareExpr:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ 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:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_BooleanTest:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ 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:
+ Assert(!OidIsValid(collation)); /* result is always boolean */
+ break;
+ case T_NextValueExpr:
+ Assert(!OidIsValid(collation)); /* result is always an integer
+ * type */
+ 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_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:
+ /* 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_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.
+ */
+ return walker(cte->ctequery, context);
+ }
+ 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_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->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 CHECKFLATCOPY(newnode, node, nodetype) \
+ ( AssertMacro(IsA((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:
+ 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_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 *);
+ 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_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 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->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))
+ {
+ CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
+ MUTATE(newrte->subquery, newrte->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) 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_String:
+ case T_BitString:
+ case T_Null:
+ 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_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_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:
+ 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_ModifyTable:
+ if (planstate_walk_members(((ModifyTableState *) planstate)->mt_plans,
+ ((ModifyTableState *) planstate)->mt_nplans,
+ walker, context))
+ return true;
+ break;
+ 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..e5dcda3
--- /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-2020, 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..1af3bf4
--- /dev/null
+++ b/src/backend/nodes/outfuncs.c
@@ -0,0 +1,4403 @@
+/*-------------------------------------------------------------------------
+ *
+ * outfuncs.c
+ * Output functions for Postgres tree nodes.
+ *
+ * Portions Copyright (c) 1996-2020, 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)
+
+#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(rootResultRelations);
+ 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_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_INT_FIELD(resultRelIndex);
+ WRITE_INT_FIELD(rootResultRelIndex);
+ WRITE_NODE_FIELD(plans);
+ 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(onConflictWhere);
+ WRITE_UINT_FIELD(exclRelRTI);
+ WRITE_NODE_FIELD(exclRelTlist);
+}
+
+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(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(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
+_outSubqueryScan(StringInfo str, const SubqueryScan *node)
+{
+ WRITE_NODE_TYPE("SUBQUERYSCAN");
+
+ _outScanInfo(str, (const Scan *) node);
+
+ WRITE_NODE_FIELD(subplan);
+}
+
+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_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_OID_FIELD(startInRangeFunc);
+ WRITE_OID_FIELD(endInRangeFunc);
+ WRITE_OID_FIELD(inRangeColl);
+ WRITE_BOOL_FIELD(inRangeAsc);
+ WRITE_BOOL_FIELD(inRangeNullsFirst);
+}
+
+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
+_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_UINT_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_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_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_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("COLLATE");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(collOid);
+ WRITE_LOCATION_FIELD(location);
+}
+
+static void
+_outCaseExpr(StringInfo str, const CaseExpr *node)
+{
+ WRITE_NODE_TYPE("CASE");
+
+ 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("WHEN");
+
+ 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("ARRAY");
+
+ 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("ROW");
+
+ 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("ROWCOMPARE");
+
+ 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("COALESCE");
+
+ 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("MINMAX");
+
+ 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(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
+_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(partitioned_rels);
+ 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(partitioned_rels);
+ 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
+_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);
+}
+
+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_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(subpaths);
+ WRITE_NODE_FIELD(subroots);
+ WRITE_NODE_FIELD(withCheckOptionLists);
+ WRITE_NODE_FIELD(returningLists);
+ WRITE_NODE_FIELD(rowMarks);
+ WRITE_NODE_FIELD(onconflict);
+ WRITE_INT_FIELD(epqParam);
+}
+
+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(rootResultRelations);
+ 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_NODE_FIELD(append_rel_list);
+ 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(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_ENUM_FIELD(inhTargetKind, InheritanceKind);
+ WRITE_BOOL_FIELD(hasJoinRTEs);
+ WRITE_BOOL_FIELD(hasLateralRTEs);
+ WRITE_BOOL_FIELD(hasHavingQual);
+ WRITE_BOOL_FIELD(hasPseudoConstantQuals);
+ 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_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(all_partrels);
+ WRITE_NODE_FIELD(partitioned_child_rels);
+}
+
+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(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);
+ if (node->sortgrouprefs)
+ {
+ int i;
+
+ appendStringInfoString(str, " :sortgrouprefs");
+ for (i = 0; i < list_length(node->exprs); i++)
+ appendStringInfo(str, " %u", node->sortgrouprefs[i]);
+ }
+ WRITE_FLOAT_FIELD(cost.startup, "%.2f");
+ WRITE_FLOAT_FIELD(cost.per_tuple, "%.2f");
+ WRITE_INT_FIELD(width);
+}
+
+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_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);
+}
+
+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
+_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(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(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("NOTIFY");
+
+ WRITE_STRING_FIELD(conditionname);
+ WRITE_STRING_FIELD(payload);
+}
+
+static void
+_outDeclareCursorStmt(StringInfo str, const DeclareCursorStmt *node)
+{
+ WRITE_NODE_TYPE("DECLARECURSOR");
+
+ 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_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
+_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_BOOL_FIELD(agg_within_group);
+ WRITE_BOOL_FIELD(agg_star);
+ WRITE_BOOL_FIELD(agg_distinct);
+ WRITE_BOOL_FIELD(func_variadic);
+ WRITE_NODE_FIELD(over);
+ 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_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
+_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_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_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_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_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
+_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_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
+_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("RTE");
+
+ /* 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);
+ 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
+_outAExpr(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_OF:
+ appendStringInfoString(str, " OF ");
+ 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;
+ case AEXPR_PAREN:
+ appendStringInfoString(str, " PAREN");
+ break;
+ default:
+ appendStringInfoString(str, " ??");
+ break;
+ }
+
+ WRITE_NODE_FIELD(lexpr);
+ WRITE_NODE_FIELD(rexpr);
+ WRITE_LOCATION_FIELD(location);
+}
+
+static void
+_outValue(StringInfo str, const Value *value)
+{
+ switch (value->type)
+ {
+ case T_Integer:
+ appendStringInfo(str, "%d", value->val.ival);
+ break;
+ case T_Float:
+
+ /*
+ * We assume the value is a valid numeric literal and so does not
+ * need quoting.
+ */
+ appendStringInfoString(str, value->val.str);
+ break;
+ case T_String:
+
+ /*
+ * 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 (value->val.str[0] != '\0')
+ outToken(str, value->val.str);
+ appendStringInfoChar(str, '"');
+ break;
+ case T_BitString:
+ /* internal representation already has leading 'b' */
+ appendStringInfoString(str, value->val.str);
+ break;
+ case T_Null:
+ /* this is seen only within A_Const, not in transformed trees */
+ appendStringInfoString(str, "NULL");
+ break;
+ default:
+ elog(ERROR, "unrecognized node type: %d", (int) value->type);
+ break;
+ }
+}
+
+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
+_outAConst(StringInfo str, const A_Const *node)
+{
+ WRITE_NODE_TYPE("A_CONST");
+
+ appendStringInfoString(str, " :val ");
+ _outValue(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(raw_expr);
+ WRITE_STRING_FIELD(cooked_expr);
+ 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);
+ 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_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(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);
+ else if (IsA(obj, Integer) ||
+ IsA(obj, Float) ||
+ IsA(obj, String) ||
+ IsA(obj, BitString))
+ {
+ /* nodeRead does not want to see { } around these! */
+ _outValue(str, 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_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_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_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_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_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_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_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_CommonTableExpr:
+ _outCommonTableExpr(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:
+ _outAExpr(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:
+ _outAConst(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..87c7788
--- /dev/null
+++ b/src/backend/nodes/params.c
@@ -0,0 +1,364 @@
+/*-------------------------------------------------------------------------
+ *
+ * params.c
+ * Support for finding the values associated with Param nodes.
+ *
+ *
+ * Portions Copyright (c) 1996-2020, 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 "mb/stringinfo_mb.h"
+#include "nodes/bitmapset.h"
+#include "nodes/params.h"
+#include "storage/shmem.h"
+#include "utils/datum.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+
+
+/*
+ * 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.
+ */
+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 = NULL;
+ retval->parserSetupArg = NULL;
+ 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;
+}
+
+/*
+ * 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 = &paramLI->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 = &paramLI->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 = &paramLI->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 = &params->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..4247672
--- /dev/null
+++ b/src/backend/nodes/print.c
@@ -0,0 +1,507 @@
+/*-------------------------------------------------------------------------
+ *
+ * print.c
+ * various print routines (used mostly for debugging)
+ *
+ * Portions Copyright (c) 1996-2020, 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
+ {
+ /* we print prefix and postfix ops the same... */
+ 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..8c1e390
--- /dev/null
+++ b/src/backend/nodes/read.c
@@ -0,0 +1,462 @@
+/*-------------------------------------------------------------------------
+ *
+ * read.c
+ * routines to convert a string (legal ascii representation of node) back
+ * to nodes
+ *
+ * Portions Copyright (c) 1996-2020, 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_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 (*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, 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_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..42050ab
--- /dev/null
+++ b/src/backend/nodes/readfuncs.c
@@ -0,0 +1,3048 @@
+/*-------------------------------------------------------------------------
+ *
+ * readfuncs.c
+ * Reader functions for Postgres tree nodes.
+ *
+ * Portions Copyright (c) 1996-2020, 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 "fmgr.h"
+#include "miscadmin.h"
+#include "nodes/extensible.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/readfuncs.h"
+#include "utils/builtins.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 = pg_strtouint64(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_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_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_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_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();
+}
+
+/*
+ * _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_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();
+}
+
+/*
+ * _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_UINT_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_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_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_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(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);
+ 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(rootResultRelations);
+ 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_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_INT_FIELD(resultRelIndex);
+ READ_INT_FIELD(rootResultRelIndex);
+ READ_NODE_FIELD(plans);
+ 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(onConflictWhere);
+ READ_UINT_FIELD(exclRelRTI);
+ READ_NODE_FIELD(exclRelTlist);
+
+ 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(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);
+
+ 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(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();
+}
+
+/*
+ * _readSubqueryScan
+ */
+static SubqueryScan *
+_readSubqueryScan(void)
+{
+ READ_LOCALS(SubqueryScan);
+
+ ReadCommonScan(&local_node->scan);
+
+ READ_NODE_FIELD(subplan);
+
+ 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_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();
+}
+
+/*
+ * 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_OID_FIELD(startInRangeFunc);
+ READ_OID_FIELD(endInRangeFunc);
+ READ_OID_FIELD(inRangeColl);
+ READ_BOOL_FIELD(inRangeAsc);
+ READ_BOOL_FIELD(inRangeNullsFirst);
+
+ 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("COMMONTABLEEXPR", 15))
+ return_value = _readCommonTableExpr();
+ 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("COLLATE", 7))
+ return_value = _readCollateExpr();
+ else if (MATCH("CASE", 4))
+ return_value = _readCaseExpr();
+ else if (MATCH("WHEN", 4))
+ return_value = _readCaseWhen();
+ else if (MATCH("CASETESTEXPR", 12))
+ return_value = _readCaseTestExpr();
+ else if (MATCH("ARRAY", 5))
+ return_value = _readArrayExpr();
+ else if (MATCH("ROW", 3))
+ return_value = _readRowExpr();
+ else if (MATCH("ROWCOMPARE", 10))
+ return_value = _readRowCompareExpr();
+ else if (MATCH("COALESCE", 8))
+ return_value = _readCoalesceExpr();
+ else if (MATCH("MINMAX", 6))
+ 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("RTE", 3))
+ return_value = _readRangeTblEntry();
+ else if (MATCH("RANGETBLFUNCTION", 16))
+ return_value = _readRangeTblFunction();
+ else if (MATCH("TABLESAMPLECLAUSE", 17))
+ return_value = _readTableSampleClause();
+ else if (MATCH("NOTIFY", 6))
+ return_value = _readNotifyStmt();
+ else if (MATCH("DEFELEM", 7))
+ return_value = _readDefElem();
+ else if (MATCH("DECLARECURSOR", 13))
+ 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("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("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..0d5056c
--- /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-2020, 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..45b9b84
--- /dev/null
+++ b/src/backend/nodes/value.c
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * value.c
+ * implementation of Value nodes
+ *
+ *
+ * Copyright (c) 2003-2020, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/nodes/value.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/parsenodes.h"
+
+/*
+ * makeInteger
+ */
+Value *
+makeInteger(int i)
+{
+ Value *v = makeNode(Value);
+
+ v->type = T_Integer;
+ v->val.ival = i;
+ return v;
+}
+
+/*
+ * makeFloat
+ *
+ * Caller is responsible for passing a palloc'd string.
+ */
+Value *
+makeFloat(char *numericStr)
+{
+ Value *v = makeNode(Value);
+
+ v->type = T_Float;
+ v->val.str = numericStr;
+ return v;
+}
+
+/*
+ * makeString
+ *
+ * Caller is responsible for passing a palloc'd string.
+ */
+Value *
+makeString(char *str)
+{
+ Value *v = makeNode(Value);
+
+ v->type = T_String;
+ v->val.str = str;
+ return v;
+}
+
+/*
+ * makeBitString
+ *
+ * Caller is responsible for passing a palloc'd string.
+ */
+Value *
+makeBitString(char *str)
+{
+ Value *v = makeNode(Value);
+
+ v->type = T_BitString;
+ v->val.str = str;
+ return v;
+}