Hi,
The patch attached is an implementation for graph generation of data
structures in postgres. The file debuggraph.c is a simple library that
generate graphs in the format supported by graphviz. Example:
#include "nodes/debuggraph.h"
...
DebugGraph *graph;
DebugNode *node;
graph = createDebugGraph();
node = newDebugNode( graph, "node1", "Node 1" );
addDebugNodeAttribute( node, "Attribute 1", "Value 1" );
newDebugNode( graph, "node2", "Node 2" );
newDebugEdge( graph, "node1", "node2", "Edge 1" );
printGraphvizToFile( graph, stderr );
destroyDebugGraph( graph );
I've also made a reimplementation of outfuncs.c to outfuncs_graph.c for
an entire node output support.
#include "nodes/outfuncs_graph.h"
...
printGraphNodes( node, stderr );
I hope that this may be useful for somebody.
--
Adriano Lange
C3SL/UFPR - www.c3sl.ufpr.br
Index: src/include/nodes/print.h
===================================================================
--- src/include/nodes/print.h (revision 11)
+++ src/include/nodes/print.h (working copy)
@@ -28,6 +28,7 @@
extern char *pretty_format_node_dump(const char *dump);
extern void print_rt(List *rtable);
extern void print_expr(Node *expr, List *rtable);
+extern int print_expr_str(char *output, Node *expr, List *rtable);
extern void print_pathkeys(List *pathkeys, List *rtable);
extern void print_tl(List *tlist, List *rtable);
extern void print_slot(TupleTableSlot *slot);
Index: src/include/nodes/debuggraph.h
===================================================================
--- src/include/nodes/debuggraph.h (revision 0)
+++ src/include/nodes/debuggraph.h (revision 0)
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * debuggraph.h
+ *
+ * Copyright (c) 2009, Adriano Lange <alange0001@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifi-
+ * cation, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright noti-
+ * ce, this list of conditions and the following disclaimer in the docu-
+ * mentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the UNIVERSIDADE FEDERAL DO PARANÁ nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+2 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PRINT_GRAPH_H
+#define PRINT_GRAPH_H
+
+typedef struct DebugNode {
+ char* internal_name;
+ char* name;
+ int attributeCount;
+ int attributeMemorySpace;
+ char** attributeNames;
+ char** attributeValues;
+} DebugNode;
+
+typedef struct DebugEdge {
+ char* source;
+ char* destination;
+ char* label;
+} DebugEdge;
+
+typedef struct DebugGraph {
+ DebugNode** nodes;
+ DebugEdge** edges;
+ int nodeCount;
+ int nodeMemorySpace;
+ int edgeCount;
+ int edgeMemorySpace;
+} DebugGraph;
+
+extern DebugGraph* createDebugGraph();
+extern void destroyDebugGraph(DebugGraph* graph);
+extern DebugNode* newDebugNode(DebugGraph* graph, const char* internal_name,
+ const char* name);
+extern void addDebugNodeAttribute(DebugNode* node, const char* name,
+ const char* value);
+extern void addDebugNodeAttributeArgs(DebugNode* node, const char* name,
+ const char* value,...);
+extern void newDebugEdge(DebugGraph* graph, const char* source,
+ const char* destination, const char* label);
+extern void printGraphvizToFile( DebugGraph* graph, FILE* file );
+extern char* addressToName( char* destination, void* address );
+extern DebugNode* findDebugNodeByInternalName( DebugGraph *graph,
+ const char *internal_name );
+extern DebugEdge* findDebugEdge( DebugGraph *graph, const char *source,
+ const char *destination, const char *label );
+
+
+#endif
Index: src/include/nodes/outfuncs_graph.h
===================================================================
--- src/include/nodes/outfuncs_graph.h (revision 0)
+++ src/include/nodes/outfuncs_graph.h (revision 0)
@@ -0,0 +1,14 @@
+/*-------------------------------------------------------------------------
+ *
+ * outfuncs_graph.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEGRAPH_H
+#define NODEGRAPH_H
+
+extern DebugGraph *createGraphNodes(void *obj);
+extern void printGraphNodes(void *obj, FILE *file);
+
+#endif
+
Index: src/backend/optimizer/path/allpaths.c
===================================================================
--- src/backend/optimizer/path/allpaths.c (revision 11)
+++ src/backend/optimizer/path/allpaths.c (working copy)
@@ -15,8 +15,11 @@
#include "postgres.h"
+#define OPTIMIZER_DEBUG
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
+#include "nodes/debuggraph.h"
+#include "nodes/outfuncs_graph.h"
#endif
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
@@ -671,6 +674,7 @@
}
else
{
+ RelOptInfo *ret;
/*
* Consider the different orders in which we could join the rels,
* using a plugin, GEQO, or the regular join search code.
@@ -681,11 +685,16 @@
root->initial_rels = initial_rels;
if (join_search_hook)
- return (*join_search_hook) (root, levels_needed, initial_rels);
+ ret = (*join_search_hook) (root, levels_needed, initial_rels);
else if (enable_geqo && levels_needed >= geqo_threshold)
- return geqo(root, levels_needed, initial_rels);
+ ret = geqo(root, levels_needed, initial_rels);
else
- return standard_join_search(root, levels_needed, initial_rels);
+ ret = standard_join_search(root, levels_needed, initial_rels);
+
+# ifdef OPTIMIZER_DEBUG
+ //printGraphNodes( ret, stderr );
+# endif
+ return ret;
}
}
@@ -1108,47 +1117,95 @@
#ifdef OPTIMIZER_DEBUG
+static const char*
+get_renation_name(PlannerInfo *root, int relid)
+{
+ RangeTblEntry *rte;
+
+ Assert(relid <= list_length(root->parse->rtable));
+ rte = rt_fetch(relid, root->parse->rtable);
+ return rte->eref->aliasname;
+}
+
static void
-print_relids(Relids relids)
+print_relids(DebugNode *node, PlannerInfo *root, Relids relids)
{
Relids tmprelids;
int x;
- bool first = true;
+ char aux[30];
tmprelids = bms_copy(relids);
while ((x = bms_first_member(tmprelids)) >= 0)
{
- if (!first)
- printf(" ");
- printf("%d", x);
- first = false;
+ sprintf(aux, "relids[%d]", x);
+ addDebugNodeAttribute(node, aux, get_renation_name(root, x));
}
bms_free(tmprelids);
}
static void
-print_restrictclauses(PlannerInfo *root, List *clauses)
+print_restrictclauses(DebugGraph *graph, DebugNode *parent, PlannerInfo *root,
+ List *clauses)
{
+ DebugNode *node;
ListCell *l;
+ char aux[500];
foreach(l, clauses)
{
RestrictInfo *c = lfirst(l);
- print_expr((Node *) c->clause, root->parse->rtable);
- if (lnext(l))
- printf(", ");
+ node = newDebugNode( graph, addressToName(aux, c), "RestrictInfo" );
+ print_expr_str(aux, (Node *) c->clause, root->parse->rtable);
+ addDebugNodeAttribute( node, "clause", aux );
+ newDebugEdge( graph, parent->internal_name, node->internal_name, "" );
}
}
static void
-print_path(PlannerInfo *root, Path *path, int indent)
+print_pathkeys_node(DebugGraph *graph, DebugNode *parent, List *pathkeys,
+ List *rtable)
{
+ ListCell *i;
+
+ foreach(i, pathkeys)
+ {
+ PathKey *pathkey = (PathKey *) lfirst(i);
+ EquivalenceClass *eclass;
+ ListCell *k;
+ DebugNode *node;
+ char aux[500];
+
+ node = newDebugNode(graph, addressToName(aux, pathkey), "PathKey");
+ newDebugEdge(graph, parent->internal_name, node->internal_name, "");
+
+ eclass = pathkey->pk_eclass;
+ /* chase up, in case pathkey is non-canonical */
+ while (eclass->ec_merged)
+ eclass = eclass->ec_merged;
+
+ foreach(k, eclass->ec_members)
+ {
+ EquivalenceMember *mem = (EquivalenceMember *) lfirst(k);
+
+ print_expr_str(aux,(Node *) mem->em_expr, rtable);
+ addDebugNodeAttribute(node, "eclass", aux);
+ }
+ }
+}
+
+
+static DebugNode*
+print_path(DebugGraph *graph, PlannerInfo *root, Path *path)
+{
+ DebugNode *node;
+ char aux[500];
const char *ptype;
bool join = false;
Path *subpath = NULL;
- int i;
+ node = newDebugNode( graph, addressToName(aux, path), "Path" );
+
switch (nodeTag(path))
{
case T_Path:
@@ -1200,90 +1257,123 @@
break;
}
- for (i = 0; i < indent; i++)
- printf("\t");
- printf("%s", ptype);
+ addDebugNodeAttribute(node, "type", ptype );
- if (path->parent)
- {
- printf("(");
- print_relids(path->parent->relids);
- printf(") rows=%.0f", path->parent->rows);
- }
- printf(" cost=%.2f..%.2f\n", path->startup_cost, path->total_cost);
+ print_relids(node, root, path->parent->relids);
+ addDebugNodeAttributeArgs(node, "rows", "%.0f", path->parent->rows);
+ addDebugNodeAttributeArgs(node, "startup_cost", "%.2f", path->startup_cost);
+ addDebugNodeAttributeArgs(node, "total_cost", "%.2f", path->total_cost);
+
if (path->pathkeys)
{
- for (i = 0; i < indent; i++)
- printf("\t");
- printf(" pathkeys: ");
- print_pathkeys(path->pathkeys, root->parse->rtable);
+ DebugNode *node2;
+ node2 = newDebugNode( graph, addressToName(aux, path->pathkeys),
+ "List");
+ newDebugEdge( graph, node->internal_name, node2->internal_name,
+ "pathkeys" );
+ print_pathkeys_node(graph, node2, path->pathkeys, root->parse->rtable);
}
if (join)
{
+ DebugNode *node2;
JoinPath *jp = (JoinPath *) path;
- for (i = 0; i < indent; i++)
- printf("\t");
- printf(" clauses: ");
- print_restrictclauses(root, jp->joinrestrictinfo);
- printf("\n");
+ node2 = newDebugNode( graph, addressToName(aux, jp->joinrestrictinfo),
+ "List");
+ newDebugEdge( graph, node->internal_name, node2->internal_name,
+ "joinrestrictinfo" );
+ print_restrictclauses(graph, node2, root, jp->joinrestrictinfo);
+
if (IsA(path, MergePath))
{
MergePath *mp = (MergePath *) path;
if (mp->outersortkeys || mp->innersortkeys)
{
- for (i = 0; i < indent; i++)
- printf("\t");
- printf(" sortouter=%d sortinner=%d\n",
- ((mp->outersortkeys) ? 1 : 0),
- ((mp->innersortkeys) ? 1 : 0));
+ addDebugNodeAttributeArgs(node, "sortouter", "%d",
+ ((mp->outersortkeys) ? 1 : 0));
+ addDebugNodeAttributeArgs(node, "sortinner", "%d",
+ ((mp->innersortkeys) ? 1 : 0));
}
}
- print_path(root, jp->outerjoinpath, indent + 1);
- print_path(root, jp->innerjoinpath, indent + 1);
+ node2 = print_path(graph, root, jp->outerjoinpath);
+ newDebugEdge(graph, node->internal_name, node2->internal_name,
+ "outerjoinpath");
+ node2 = print_path(graph, root, jp->innerjoinpath);
+ newDebugEdge(graph, node->internal_name, node2->internal_name,
+ "innerjoinpath");
}
- if (subpath)
- print_path(root, subpath, indent + 1);
+ if (subpath) {
+ DebugNode *node2;
+ node2 = print_path(graph, root, subpath);
+ newDebugEdge(graph, node->internal_name, node2->internal_name,
+ "subpath");
+ }
+
+ return node;
}
void
debug_print_rel(PlannerInfo *root, RelOptInfo *rel)
{
- ListCell *l;
+ DebugGraph *graph;
+ DebugNode *node,
+ *node2;
+ char aux[500];
+ ListCell *l;
- printf("RELOPTINFO (");
- print_relids(rel->relids);
- printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
+ graph = createDebugGraph();
+ node = newDebugNode(graph, addressToName(aux,rel), "RelOptInfo" );
+
+ print_relids(node, root, rel->relids);
+
+ addDebugNodeAttributeArgs(node, "rows", "%.0f", rel->rows);
+
+ addDebugNodeAttributeArgs(node, "width", "%d", rel->width);
+
if (rel->baserestrictinfo)
{
- printf("\tbaserestrictinfo: ");
- print_restrictclauses(root, rel->baserestrictinfo);
- printf("\n");
+ node2 = newDebugNode( graph, addressToName(aux,rel->baserestrictinfo),
+ "List" );
+ newDebugEdge( graph, node->internal_name, node2->internal_name,
+ "baserestrictinfo" );
+ print_restrictclauses(graph, node2, root, rel->baserestrictinfo);
}
if (rel->joininfo)
{
- printf("\tjoininfo: ");
- print_restrictclauses(root, rel->joininfo);
- printf("\n");
+ node2 = newDebugNode( graph, addressToName(aux,rel->joininfo),
+ "List" );
+ newDebugEdge( graph, node->internal_name, node2->internal_name,
+ "joininfo" );
+ print_restrictclauses(graph, node2, root, rel->joininfo);
}
- printf("\tpath list:\n");
- foreach(l, rel->pathlist)
- print_path(root, lfirst(l), 1);
- printf("\n\tcheapest startup path:\n");
- print_path(root, rel->cheapest_startup_path, 1);
- printf("\n\tcheapest total path:\n");
- print_path(root, rel->cheapest_total_path, 1);
- printf("\n");
- fflush(stdout);
+
+ node2 = newDebugNode( graph, addressToName(aux,rel->pathlist),
+ "List" );
+ newDebugEdge( graph, node->internal_name, node2->internal_name,
+ "pathlist" );
+ foreach(l, rel->pathlist) {
+ DebugNode *node3;
+ node3 = print_path(graph, root, lfirst(l));
+ newDebugEdge( graph, node2->internal_name, node3->internal_name, "");
+ }
+ newDebugEdge( graph, node->internal_name,
+ addressToName(aux, rel->cheapest_startup_path),
+ "cheapest_startup_path");
+ newDebugEdge( graph, node->internal_name,
+ addressToName(aux, rel->cheapest_total_path),
+ "cheapest_total_path");
+
+ printGraphvizToFile( graph, stderr );
+ destroyDebugGraph( graph );
}
#endif /* OPTIMIZER_DEBUG */
Index: src/backend/nodes/debuggraph.c
===================================================================
--- src/backend/nodes/debuggraph.c (revision 0)
+++ src/backend/nodes/debuggraph.c (revision 0)
@@ -0,0 +1,415 @@
+/*-------------------------------------------------------------------------
+ *
+ * debuggraph.c
+ *
+ * Copyright (c) 2009, Adriano Lange <alange0001@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifi-
+ * cation, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright noti-
+ * ce, this list of conditions and the following disclaimer in the docu-
+ * mentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the UNIVERSIDADE FEDERAL DO PARANÁ nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "postgres.h"
+
+#include "nodes/debuggraph.h"
+
+#define INITIAL_LIST_SIZE 100
+
+#define ALLOC_FUNC palloc
+#define ALLOC_FUNC0 palloc0
+#define FREE_FUNC pfree
+
+static void addDebugNode(DebugGraph* graph, DebugNode* node);
+static void addDebugEdge(DebugGraph* graph, DebugEdge* edge);
+static DebugNode* createDebugNode( const char* internal_name,
+ const char* name );
+static void destroyDebugNode(DebugNode* node);
+static DebugEdge* createDebugEdge( const char* source, const char* destination,
+ const char* label );
+static void destroyDebugEdge(DebugEdge* edge);
+static char* copyString( const char* source );
+
+
+DebugGraph*
+createDebugGraph()
+{
+ DebugGraph* graph;
+
+ graph = (DebugGraph*) ALLOC_FUNC0(sizeof(DebugGraph));
+
+ return graph;
+}
+
+void
+destroyDebugGraph(DebugGraph* graph)
+{
+ if( graph ){
+ int i;
+
+ if( graph->nodeMemorySpace ){
+ for( i=0; i<graph->nodeCount; i++ ){
+ if( graph->nodes[i] )
+ destroyDebugNode( graph->nodes[i] );
+ }
+ FREE_FUNC( graph->nodes );
+ }
+
+ if( graph->edgeMemorySpace ){
+ for( i=0; i<graph->edgeCount; i++ ){
+ if( graph->edges[i] )
+ destroyDebugEdge( graph->edges[i] );
+ }
+ FREE_FUNC( graph->edges );
+ }
+
+ FREE_FUNC(graph);
+ }
+}
+
+DebugNode*
+newDebugNode(DebugGraph* graph, const char* internal_name, const char* name)
+{
+ DebugNode* node;
+
+ if( ! graph )
+ return NULL;
+
+ node = findDebugNodeByInternalName(graph, internal_name);
+ if( node )
+ return node;
+
+ node = createDebugNode( internal_name, name );
+ addDebugNode(graph, node);
+
+ return node;
+}
+
+void
+newDebugEdge(DebugGraph* graph, const char* source, const char* destination,
+ const char* label)
+{
+ DebugEdge* edge;
+
+ if( !graph )
+ return;
+
+ edge = findDebugEdge(graph, source, destination, label);
+ if( edge )
+ return;
+
+ edge = createDebugEdge( source, destination, label );
+ addDebugEdge( graph, edge );
+}
+
+void
+addDebugNodeAttributeArgs(DebugNode* node, const char* name,
+ const char* value,...)
+{
+ char *str_value;
+ va_list va;
+ int len = 100;
+ int print_return;
+
+ if( !node || !name || !value )
+ return;
+
+ va_start(va, value);
+ for(;;){
+ str_value = (char*) ALLOC_FUNC( sizeof(char) * len );
+
+ print_return = vsnprintf(str_value, len, value, va);
+
+ if( print_return >= 0 && print_return < len - 1 ) {
+ break;
+ } else {
+ FREE_FUNC(str_value);
+ len *= 2;
+ }
+ }
+ va_end(va);
+
+ addDebugNodeAttribute(node, name, str_value);
+
+ FREE_FUNC(str_value);
+}
+
+void
+addDebugNodeAttribute(DebugNode* node, const char* name, const char* value)
+{
+ int index;
+
+ if( !node || !name || !value )
+ return;
+ for( index=0; index<node->attributeCount; index++ ){
+ if( (!strcmp(node->attributeNames[index], name))
+ && (!strcmp(node->attributeValues[index], value)) )
+ return;
+ }
+
+ if( node->attributeCount >= node->attributeMemorySpace ){
+ int i;
+ int new_size;
+ char** new_names;
+ char** new_values;
+
+ if( node->attributeMemorySpace )
+ new_size = node->attributeMemorySpace *2;
+ else
+ new_size = INITIAL_LIST_SIZE;
+
+ new_names = (char**) ALLOC_FUNC( sizeof(char*) * new_size );
+ new_values = (char**) ALLOC_FUNC( sizeof(char*) * new_size );
+
+ for( i=0; i<node->attributeCount; i++ ){
+ new_names[i] = node->attributeNames[i];
+ new_values[i] = node->attributeValues[i];
+ }
+ if( node->attributeMemorySpace ){
+ FREE_FUNC( node->attributeNames );
+ FREE_FUNC( node->attributeValues );
+ }
+ node->attributeNames = new_names;
+ node->attributeValues = new_values;
+ node->attributeMemorySpace = new_size;
+ }
+
+ index = node->attributeCount++;
+
+ node->attributeNames[index] = copyString(name);
+ node->attributeValues[index] = copyString(value);
+}
+
+void
+printGraphvizToFile( DebugGraph* graph, FILE* file )
+{
+ int i,j;
+
+ if( !graph )
+ return;
+
+ fprintf(file, "digraph g {\n");
+ fprintf(file, "\tgraph [fontsize=30 labelloc=\"t\" label=\"\" splines=true overlap=false rankdir = \"LR\"];\n");
+ fprintf(file, "\tnode [style = \"filled\" penwidth = 1 fillcolor = \"white\" fontname = \"Courier New\" shape =
\"Mrecord\"];\n");
+ fprintf(file, "\tedge [ penwidth = 2 fontsize = 18 fontcolor = \"black\" ];\n");
+ fprintf(file, "\tratio = auto;\n");
+ for( i=0; i<graph->nodeCount; i++ ){
+ fprintf(file, "\t\"%s\" [ label =<\\\n", graph->nodes[i]->internal_name);
+ fprintf(file, "\t\t<table border=\"0\" cellborder=\"0\" cellpadding=\"3\" bgcolor=\"white\">\\\n");
+ fprintf(file, "\t\t\t<tr><td bgcolor=\"black\" align=\"center\" colspan=\"2\"><font
color=\"white\">%s</font></td></tr>\\\n",
+ graph->nodes[i]->name);
+ for( j=0; j<graph->nodes[i]->attributeCount; j++ ){
+ fprintf(file, "\t\t\t<tr><td bgcolor=\"grey\" align=\"left\">%s:</td><td align=\"left\">%s</td></tr>\n",
+ graph->nodes[i]->attributeNames[j],
+ graph->nodes[i]->attributeValues[j]);
+ }
+ fprintf(file, "\t\t</table>> ];\n");
+ }
+ for( i=0; i<graph->edgeCount; i++ ){
+ fprintf(file, "\t\"%s\" -> \"%s\" [ label = \"%s\" ];\n",
+ graph->edges[i]->source,
+ graph->edges[i]->destination,
+ graph->edges[i]->label);
+ }
+ fprintf(file, "}\n");
+}
+
+char*
+addressToName( char* destination, void* address )
+{
+ sprintf(destination, "a%p", address );
+ return destination;
+}
+
+DebugNode*
+findDebugNodeByInternalName( DebugGraph *graph, const char *internal_name )
+{
+ int i;
+ if( !graph )
+ return NULL;
+
+ for( i=0; i<graph->nodeCount; i++ ){
+ if( !strcmp( graph->nodes[i]->internal_name, internal_name ) )
+ return graph->nodes[i];
+ }
+ return NULL;
+}
+
+DebugEdge*
+findDebugEdge( DebugGraph *graph, const char *source, const char *destination,
+ const char *label )
+{
+ int i;
+ if( !graph )
+ return NULL;
+
+ for( i=0; i<graph->edgeCount; i++ ){
+ if( (!strcmp( graph->edges[i]->source, source ))
+ && (!strcmp( graph->edges[i]->destination, destination ))
+ && (!strcmp( graph->edges[i]->label, label )))
+ return graph->edges[i];
+ }
+ return NULL;
+}
+
+static void
+addDebugNode(DebugGraph* graph, DebugNode* node)
+{
+ if( (!graph) || (!node) )
+ return;
+
+ if( graph->nodeCount >= graph->nodeMemorySpace ){
+ int new_size;
+ DebugNode** new_list;
+
+ if( graph->nodeMemorySpace )
+ new_size = graph->nodeMemorySpace * 2;
+ else
+ new_size = INITIAL_LIST_SIZE;
+ new_list = (DebugNode**) ALLOC_FUNC( sizeof(DebugNode*) * new_size );
+
+ if( graph->nodeMemorySpace ){
+ int i;
+ for( i=0; i<graph->nodeCount; i++ ){
+ new_list[i] = graph->nodes[i];
+ }
+ FREE_FUNC( graph->nodes );
+ }
+ graph->nodeMemorySpace = new_size;
+ graph->nodes = new_list;
+ }
+
+ graph->nodes[ graph->nodeCount++ ] = node;
+}
+
+static void
+addDebugEdge(DebugGraph* graph, DebugEdge* edge)
+{
+ if( (!graph) || (!edge) )
+ return;
+
+ if( graph->edgeCount >= graph->edgeMemorySpace ){
+ int new_size;
+ DebugEdge** new_list;
+
+ if( graph->edgeMemorySpace )
+ new_size = graph->edgeMemorySpace *2;
+ else
+ new_size = INITIAL_LIST_SIZE;
+
+ new_list = (DebugEdge**) ALLOC_FUNC( sizeof(DebugEdge*) * new_size );
+
+ if( graph->edgeMemorySpace ){
+ int i;
+ for( i=0; i<graph->edgeCount; i++ ){
+ new_list[i] = graph->edges[i];
+ }
+ FREE_FUNC( graph->edges );
+ }
+ graph->edges = new_list;
+ graph->edgeMemorySpace = new_size;
+ }
+
+ graph->edges[ graph->edgeCount++ ] = edge;
+}
+
+static DebugNode*
+createDebugNode( const char* internal_name, const char* name )
+{
+ DebugNode* node;
+
+ node = (DebugNode*) ALLOC_FUNC0( sizeof(DebugNode) );
+
+ node->internal_name = copyString( internal_name );
+ node->name = copyString( name );
+
+ return node;
+}
+
+static void
+destroyDebugNode(DebugNode* node)
+{
+ if( node ) {
+ int i;
+
+ if( node->internal_name )
+ FREE_FUNC( node->internal_name );
+ if( node->name )
+ FREE_FUNC( node->name );
+
+ if( node->attributeMemorySpace ) {
+ for( i=0; i<node->attributeCount; i++ ){
+ FREE_FUNC( node->attributeNames[i] );
+ FREE_FUNC( node->attributeValues[i] );
+ }
+ FREE_FUNC( node->attributeNames );
+ FREE_FUNC( node->attributeValues );
+ }
+
+ FREE_FUNC(node);
+ }
+}
+
+static DebugEdge*
+createDebugEdge( const char* source, const char* destination,
+ const char* label )
+{
+ DebugEdge* edge;
+
+ edge = (DebugEdge*) ALLOC_FUNC( sizeof(DebugEdge) );
+
+ edge->source = copyString( source );
+ edge->destination = copyString( destination );
+ edge->label = copyString( label );
+
+ return edge;
+}
+
+static void
+destroyDebugEdge(DebugEdge* edge)
+{
+ if( edge ){
+ if( edge->source )
+ FREE_FUNC( edge->source );
+ if( edge->destination )
+ FREE_FUNC( edge->destination );
+ if( edge->label )
+ FREE_FUNC( edge->label );
+ FREE_FUNC( edge );
+ }
+}
+
+static char*
+copyString( const char* source )
+{
+ char* str;
+ int len;
+
+ len = strlen( source );
+ str = (char*) ALLOC_FUNC( sizeof(char) * (len+1) );
+ strcpy(str,source);
+
+ return str;
+}
Index: src/backend/nodes/outfuncs_graph.c
===================================================================
--- src/backend/nodes/outfuncs_graph.c (revision 0)
+++ src/backend/nodes/outfuncs_graph.c (revision 0)
@@ -0,0 +1,2529 @@
+/*-------------------------------------------------------------------------
+ *
+ * outfuncs_graph.c
+ *
+ * Portions Copyright (c) 2009, Adriano Lange <alange0001@gmail.com>.
+ * This file was changed from original src/backend/nodes/outfuncs.c file
+ * at PostgreSQL version 8.3. These portions of code follow the same BSD
+ * license adopted by the PostgreSQL Global Development Group.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <ctype.h>
+
+#include "lib/stringinfo.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+#include "nodes/debuggraph.h"
+#include "nodes/outfuncs_graph.h"
+#include "utils/datum.h"
+
+
+/*
+ * 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(nodetitle) \
+ DebugNode *graphnode; \
+ graphnode = _newDebugNode(graph, parent, nodetitle, label, node)
+
+
+#define WRITE_FORMATED_FIELD(fldname, format) \
+ addDebugNodeAttributeArgs(graphnode, CppAsString(fldname), \
+ format,node->fldname)
+
+/* Write an integer field (anything written as ":fldname %d") */
+#define WRITE_INT_FIELD(fldname) WRITE_FORMATED_FIELD(fldname, "%d")
+
+/* Write an unsigned integer field (anything written as ":fldname %u") */
+#define WRITE_UINT_FIELD(fldname) WRITE_FORMATED_FIELD(fldname, "%u")
+
+/* Write an OID field (don't hard-wire assumption that OID is same as uint) */
+#define WRITE_OID_FIELD(fldname) WRITE_FORMATED_FIELD(fldname, "%u")
+
+/* Write a long-integer field */
+#define WRITE_LONG_FIELD(fldname) WRITE_FORMATED_FIELD(fldname, "%ld")
+
+/* Write a char field (ie, one ascii character) */
+#define WRITE_CHAR_FIELD(fldname) WRITE_FORMATED_FIELD(fldname, "%c")
+
+/* Write an enumerated-type field as an integer code */
+#define WRITE_ENUM_FIELD(fldname, enumtype) \
+ addDebugNodeAttributeArgs(graphnode, CppAsString(fldname), \
+ "%d", (int) node->fldname)
+
+/* Write a float field --- caller must give format to define precision */
+#define WRITE_FLOAT_FIELD(fldname,format) WRITE_FORMATED_FIELD(fldname, format)
+
+/* Write a boolean field */
+#define WRITE_BOOL_FIELD(fldname) \
+ addDebugNodeAttributeArgs(graphnode, CppAsString(fldname), \
+ "%s",booltostr(node->fldname))
+
+/* Write a character-string (possibly NULL) field */
+#define WRITE_STRING_FIELD(fldname) \
+ _outToken(graph, graphnode, CppAsString(fldname), node->fldname)
+
+/* Write a Node field */
+#define WRITE_NODE_FIELD(fldname) \
+ _outNode(graph, graphnode, CppAsString(fldname), node->fldname)
+
+/* Write a bitmapset field */
+#define WRITE_BITMAPSET_FIELD(fldname) \
+ _outBitmapset(graph, graphnode, CppAsString(fldname), node->fldname)
+
+#define WRITE_LIST_VALUES(countvalue, fieldname, format) \
+ { \
+ int i; \
+ char str[500]; \
+ char *aux = str; \
+ \
+ for (i = 0; i < countvalue; i++) { \
+ if( i ) \
+ aux += sprintf(aux, " "); \
+ aux += sprintf(aux, format, node->fieldname[i]); \
+ } \
+ addDebugNodeAttribute(graphnode, CppAsString(fieldname), str); \
+ }
+
+
+#define booltostr(x) ((x) ? "true" : "false")
+
+static void _outNode(DebugGraph *graph, DebugNode *parent, const char *label,
+ void *obj);
+
+
+static DebugNode *
+_newDebugNode(DebugGraph *graph, DebugNode *parent, const char *node_title,
+ const char *edge_label, void *node)
+{
+ DebugNode *graphnode;
+ char aux[50];
+ graphnode = newDebugNode(graph, addressToName(aux, node), node_title);
+ if( parent )
+ newDebugEdge(graph, parent->internal_name, graphnode->internal_name,
+ edge_label);
+ return graphnode;
+}
+
+/*
+ * _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 "<>".
+ */
+static void
+_outToken(DebugGraph *graph, DebugNode *parent, const char *label, char *s)
+{
+ if (s == NULL)
+ {
+ addDebugNodeAttribute(parent,label,"NULL");
+ return;
+ }
+ if (*s == '\0')
+ return;
+
+# ifdef corrigir__out_Token
+ /*
+ * 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++);
+ }
+# else
+ // TODO: Replace HTML special characters
+ addDebugNodeAttribute(parent,label,s);
+# endif
+}
+
+static void
+_outList(DebugGraph *graph, DebugNode *parent, const char *label, List *node)
+{
+ ListCell *lc;
+ DebugNode *graphnode;
+ const char *object_name;
+ char aux[100];
+ int count = 0;
+
+ if (IsA(node, List))
+ object_name = "List";
+ else if (IsA(node, IntList))
+ object_name = "IntList";
+ else if (IsA(node, OidList))
+ object_name = "OidList";
+ else {
+ sprintf(aux, "List (unrecognized: %d)",
+ (int) node->type);
+ object_name = aux;
+ }
+
+ graphnode = newDebugNode(graph, addressToName(aux, node), object_name);
+ if( parent )
+ newDebugEdge(graph, parent->internal_name, graphnode->internal_name,
+ label);
+
+ foreach(lc, node)
+ {
+ sprintf(aux, "[%d]", count++);
+ /*
+ * 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(graph, graphnode, "", lfirst(lc));
+ }
+ else if (IsA(node, IntList))
+ addDebugNodeAttributeArgs(graphnode, aux, "%d", lfirst_int(lc));
+ else if (IsA(node, OidList))
+ addDebugNodeAttributeArgs(graphnode, aux, "%u", lfirst_oid(lc));
+ }
+}
+
+/*
+ * _outBitmapset -
+ * converts a bitmap set of integers
+ *
+ * Note: the output format is "(b int int ...)", similar to an integer List.
+ * Currently bitmapsets do not appear in any node type that is stored in
+ * rules, so there is no support in readfuncs.c for reading this format.
+ */
+static void
+_outBitmapset(DebugGraph *graph, DebugNode *parent, const char *label,
+ Bitmapset *bms)
+{
+ StringInfo str = makeStringInfo();
+ Bitmapset *tmpset;
+ int x;
+ bool first = true;
+
+ tmpset = bms_copy(bms);
+ while ((x = bms_first_member(tmpset)) >= 0){
+ if( !first )
+ appendStringInfo(str, ", ");
+ appendStringInfo(str, "%d", x);
+ first = false;
+ }
+ bms_free(tmpset);
+
+ if( parent ){
+ addDebugNodeAttribute(parent, label, str->data);
+ } else {
+ char aux[50];
+ DebugNode *graphnode = newDebugNode(graph, addressToName(aux, bms),
+ "Bitmapset");
+ addDebugNodeAttribute(graphnode, "", str->data);
+ }
+}
+
+/*
+ * Print the value of a Datum given its type.
+ */
+static void
+_outDatum(DebugGraph *graph, DebugNode *parent, const char *label,
+ Datum value, int typlen, bool typbyval)
+{
+ Size length,
+ i;
+ char *s;
+ StringInfo str = makeStringInfo();
+
+ 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]));
+ appendStringInfo(str, "]");
+ }
+ else
+ {
+ s = (char *) DatumGetPointer(value);
+ if (!PointerIsValid(s))
+ appendStringInfo(str, "0 [ ]");
+ else
+ {
+ appendStringInfo(str, "%u [ ", (unsigned int) length);
+ for (i = 0; i < length; i++)
+ appendStringInfo(str, "%d ", (int) (s[i]));
+ appendStringInfo(str, "]");
+ }
+ }
+
+ if( parent ){
+ addDebugNodeAttribute(parent, label, str->data);
+ } else {
+ char aux[50];
+ DebugNode *graphnode = newDebugNode(graph,
+ addressToName(aux, &value), "Datum");
+ addDebugNodeAttribute(graphnode, "", str->data);
+ }
+}
+
+/*
+ * Stuff from plannodes.h
+ */
+
+static void
+_outPlannedStmt(DebugGraph *graph, DebugNode *parent, const char *label,
+ PlannedStmt *node)
+{
+ WRITE_NODE_TYPE("PlannedStmt");
+
+ WRITE_ENUM_FIELD(commandType, CmdType);
+ WRITE_BOOL_FIELD(canSetTag);
+ WRITE_NODE_FIELD(planTree);
+ WRITE_NODE_FIELD(rtable);
+ WRITE_NODE_FIELD(resultRelations);
+ WRITE_NODE_FIELD(utilityStmt);
+ WRITE_NODE_FIELD(intoClause);
+ WRITE_NODE_FIELD(subplans);
+ WRITE_BITMAPSET_FIELD(rewindPlanIDs);
+ WRITE_NODE_FIELD(returningLists);
+ WRITE_NODE_FIELD(rowMarks);
+ WRITE_NODE_FIELD(relationOids);
+ WRITE_INT_FIELD(nParamExec);
+}
+
+/*
+ * print the basic stuff of all nodes that inherit from Plan
+ */
+static void
+_outPlanInfo(DebugGraph *graph, DebugNode *graphnode, 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_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(DebugGraph *graph, DebugNode *graphnode, Scan *node)
+{
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_UINT_FIELD(scanrelid);
+}
+
+/*
+ * print the basic stuff of all nodes that inherit from Join
+ */
+static void
+_outJoinPlanInfo(DebugGraph *graph, DebugNode *graphnode, Join *node)
+{
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_ENUM_FIELD(jointype, JoinType);
+ WRITE_NODE_FIELD(joinqual);
+}
+
+
+static void
+_outPlan(DebugGraph *graph, DebugNode *parent, const char *label, Plan *node)
+{
+ WRITE_NODE_TYPE("Plan");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+}
+
+static void
+_outResult(DebugGraph *graph, DebugNode *parent, const char *label, Result *node)
+{
+ WRITE_NODE_TYPE("Result");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_NODE_FIELD(resconstantqual);
+}
+
+static void
+_outAppend(DebugGraph *graph, DebugNode *parent, const char *label, Append *node)
+{
+ WRITE_NODE_TYPE("Append");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_NODE_FIELD(appendplans);
+ WRITE_BOOL_FIELD(isTarget);
+}
+
+static void
+_outBitmapAnd(DebugGraph *graph, DebugNode *parent, const char *label, BitmapAnd *node)
+{
+ WRITE_NODE_TYPE("BitmapAnd");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_NODE_FIELD(bitmapplans);
+}
+
+static void
+_outBitmapOr(DebugGraph *graph, DebugNode *parent, const char *label, BitmapOr *node)
+{
+ WRITE_NODE_TYPE("BitmapOr");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_NODE_FIELD(bitmapplans);
+}
+
+static void
+_outScan(DebugGraph *graph, DebugNode *parent, const char *label, Scan *node)
+{
+ WRITE_NODE_TYPE("Scan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+}
+
+static void
+_outSeqScan(DebugGraph *graph, DebugNode *parent, const char *label, SeqScan *node)
+{
+ WRITE_NODE_TYPE("SeqScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+}
+
+static void
+_outIndexScan(DebugGraph *graph, DebugNode *parent, const char *label, IndexScan *node)
+{
+ WRITE_NODE_TYPE("IndexScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_OID_FIELD(indexid);
+ WRITE_NODE_FIELD(indexqual);
+ WRITE_NODE_FIELD(indexqualorig);
+ WRITE_NODE_FIELD(indexstrategy);
+ WRITE_NODE_FIELD(indexsubtype);
+ WRITE_ENUM_FIELD(indexorderdir, ScanDirection);
+}
+
+static void
+_outBitmapIndexScan(DebugGraph *graph, DebugNode *parent, const char *label, BitmapIndexScan *node)
+{
+ WRITE_NODE_TYPE("BitmapIndexScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_OID_FIELD(indexid);
+ WRITE_NODE_FIELD(indexqual);
+ WRITE_NODE_FIELD(indexqualorig);
+ WRITE_NODE_FIELD(indexstrategy);
+ WRITE_NODE_FIELD(indexsubtype);
+}
+
+static void
+_outBitmapHeapScan(DebugGraph *graph, DebugNode *parent, const char *label, BitmapHeapScan *node)
+{
+ WRITE_NODE_TYPE("BitmapHeapScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_NODE_FIELD(bitmapqualorig);
+}
+
+static void
+_outTidScan(DebugGraph *graph, DebugNode *parent, const char *label, TidScan *node)
+{
+ WRITE_NODE_TYPE("TidScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_NODE_FIELD(tidquals);
+}
+
+static void
+_outSubqueryScan(DebugGraph *graph, DebugNode *parent, const char *label, SubqueryScan *node)
+{
+ WRITE_NODE_TYPE("SubqueryScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_NODE_FIELD(subplan);
+ WRITE_NODE_FIELD(subrtable);
+}
+
+static void
+_outFunctionScan(DebugGraph *graph, DebugNode *parent, const char *label, FunctionScan *node)
+{
+ WRITE_NODE_TYPE("FunctionScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_NODE_FIELD(funcexpr);
+ WRITE_NODE_FIELD(funccolnames);
+ WRITE_NODE_FIELD(funccoltypes);
+ WRITE_NODE_FIELD(funccoltypmods);
+}
+
+static void
+_outValuesScan(DebugGraph *graph, DebugNode *parent, const char *label, ValuesScan *node)
+{
+ WRITE_NODE_TYPE("ValuesScan");
+
+ _outScanInfo(graph, graphnode, (Scan *) node);
+
+ WRITE_NODE_FIELD(values_lists);
+}
+
+static void
+_outJoin(DebugGraph *graph, DebugNode *parent, const char *label, Join *node)
+{
+ WRITE_NODE_TYPE("Join");
+
+ _outJoinPlanInfo(graph, graphnode, (Join *) node);
+}
+
+static void
+_outNestLoop(DebugGraph *graph, DebugNode *parent, const char *label, NestLoop *node)
+{
+ WRITE_NODE_TYPE("NestLoop");
+
+ _outJoinPlanInfo(graph, graphnode, (Join *) node);
+}
+
+static void
+_outMergeJoin(DebugGraph *graph, DebugNode *parent, const char *label, MergeJoin *node)
+{
+ int numCols;
+
+ WRITE_NODE_TYPE("MergeJoin");
+
+ _outJoinPlanInfo(graph, graphnode, (Join *) node);
+
+ WRITE_NODE_FIELD(mergeclauses);
+
+ numCols = list_length(node->mergeclauses);
+
+ WRITE_LIST_VALUES(numCols, mergeFamilies, "%u");
+ WRITE_LIST_VALUES(numCols, mergeStrategies, "%d");
+ WRITE_LIST_VALUES(numCols, mergeNullsFirst, "%d");
+}
+
+static void
+_outHashJoin(DebugGraph *graph, DebugNode *parent, const char *label, HashJoin *node)
+{
+ WRITE_NODE_TYPE("MergeJoin");
+
+ _outJoinPlanInfo(graph, graphnode, (Join *) node);
+
+ WRITE_NODE_FIELD(hashclauses);
+}
+
+static void
+_outAgg(DebugGraph *graph, DebugNode *parent, const char *label, Agg *node)
+{
+ WRITE_NODE_TYPE("Agg");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_ENUM_FIELD(aggstrategy, AggStrategy);
+ WRITE_INT_FIELD(numCols);
+
+ WRITE_LIST_VALUES(node->numCols, grpColIdx, "%d");
+ WRITE_LIST_VALUES(node->numCols, grpOperators, "%u");
+
+ WRITE_LONG_FIELD(numGroups);
+}
+
+static void
+_outGroup(DebugGraph *graph, DebugNode *parent, const char *label, Group *node)
+{
+ WRITE_NODE_TYPE("Group");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_INT_FIELD(numCols);
+
+ WRITE_LIST_VALUES(node->numCols, grpColIdx, "%d");
+ WRITE_LIST_VALUES(node->numCols, grpOperators, "%u");
+}
+
+static void
+_outMaterial(DebugGraph *graph, DebugNode *parent, const char *label, Material *node)
+{
+ WRITE_NODE_TYPE("Material");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+}
+
+static void
+_outSort(DebugGraph *graph, DebugNode *parent, const char *label, Sort *node)
+{
+ WRITE_NODE_TYPE("Sort");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_INT_FIELD(numCols);
+
+ WRITE_LIST_VALUES(node->numCols, sortColIdx, "%d");
+ WRITE_LIST_VALUES(node->numCols, sortOperators, "%u");
+
+ {
+ int i;
+ char str[500];
+ char *aux = str;
+ for (i = 0; i < node->numCols; i++){
+ if( i )
+ aux += sprintf(aux, " ");
+ aux += sprintf(aux, " %s", booltostr(node->nullsFirst[i]));
+ }
+ addDebugNodeAttribute(graphnode, "nullsFirst", str);
+ }
+}
+
+static void
+_outUnique(DebugGraph *graph, DebugNode *parent, const char *label, Unique *node)
+{
+ WRITE_NODE_TYPE("Unique");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_INT_FIELD(numCols);
+
+ WRITE_LIST_VALUES(node->numCols, uniqColIdx, "%d");
+ WRITE_LIST_VALUES(node->numCols, uniqOperators, "%u");
+}
+
+static void
+_outSetOp(DebugGraph *graph, DebugNode *parent, const char *label, SetOp *node)
+{
+ WRITE_NODE_TYPE("SetOp");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_ENUM_FIELD(cmd, SetOpCmd);
+ WRITE_INT_FIELD(numCols);
+
+ WRITE_LIST_VALUES(node->numCols, dupColIdx, "%d");
+ WRITE_LIST_VALUES(node->numCols, dupOperators, "%u");
+
+ WRITE_INT_FIELD(flagColIdx);
+}
+
+static void
+_outLimit(DebugGraph *graph, DebugNode *parent, const char *label, Limit *node)
+{
+ WRITE_NODE_TYPE("Limit");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+
+ WRITE_NODE_FIELD(limitOffset);
+ WRITE_NODE_FIELD(limitCount);
+}
+
+static void
+_outHash(DebugGraph *graph, DebugNode *parent, const char *label, Hash *node)
+{
+ WRITE_NODE_TYPE("Hash");
+
+ _outPlanInfo(graph, graphnode, (Plan *) node);
+}
+
+/*****************************************************************************
+ *
+ * Stuff from primnodes.h.
+ *
+ *****************************************************************************/
+
+static void
+_outAlias(DebugGraph *graph, DebugNode *parent, const char *label, Alias *node)
+{
+ WRITE_NODE_TYPE("Alias");
+
+ WRITE_STRING_FIELD(aliasname);
+ WRITE_NODE_FIELD(colnames);
+}
+
+static void
+_outRangeVar(DebugGraph *graph, DebugNode *parent, const char *label, 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_ENUM_FIELD(inhOpt, InhOption);
+ WRITE_BOOL_FIELD(istemp);
+ WRITE_NODE_FIELD(alias);
+}
+
+static void
+_outIntoClause(DebugGraph *graph, DebugNode *parent, const char *label, IntoClause *node)
+{
+ WRITE_NODE_TYPE("IntoClause");
+
+ WRITE_NODE_FIELD(rel);
+ WRITE_NODE_FIELD(colNames);
+ WRITE_NODE_FIELD(options);
+ WRITE_ENUM_FIELD(onCommit, OnCommitAction);
+ WRITE_STRING_FIELD(tableSpaceName);
+}
+
+static void
+_outVar(DebugGraph *graph, DebugNode *parent, const char *label, Var *node)
+{
+ WRITE_NODE_TYPE("Var");
+
+ WRITE_UINT_FIELD(varno);
+ WRITE_INT_FIELD(varattno);
+ WRITE_OID_FIELD(vartype);
+ WRITE_INT_FIELD(vartypmod);
+ WRITE_UINT_FIELD(varlevelsup);
+ WRITE_UINT_FIELD(varnoold);
+ WRITE_INT_FIELD(varoattno);
+}
+
+static void
+_outConst(DebugGraph *graph, DebugNode *parent, const char *label, Const *node)
+{
+ WRITE_NODE_TYPE("Const");
+
+ WRITE_OID_FIELD(consttype);
+ WRITE_INT_FIELD(consttypmod);
+ WRITE_INT_FIELD(constlen);
+ WRITE_BOOL_FIELD(constbyval);
+ WRITE_BOOL_FIELD(constisnull);
+
+ if (node->constisnull)
+ addDebugNodeAttribute(graphnode, "constvalue", "");
+ else
+ _outDatum(graph, graphnode, "constvalue", node->constvalue,
+ node->constlen, node->constbyval);
+}
+
+static void
+_outParam(DebugGraph *graph, DebugNode *parent, const char *label, Param *node)
+{
+ WRITE_NODE_TYPE("Param");
+
+ WRITE_ENUM_FIELD(paramkind, ParamKind);
+ WRITE_INT_FIELD(paramid);
+ WRITE_OID_FIELD(paramtype);
+ WRITE_INT_FIELD(paramtypmod);
+}
+
+static void
+_outAggref(DebugGraph *graph, DebugNode *parent, const char *label, Aggref *node)
+{
+ WRITE_NODE_TYPE("Aggref");
+
+ WRITE_OID_FIELD(aggfnoid);
+ WRITE_OID_FIELD(aggtype);
+ WRITE_NODE_FIELD(args);
+ WRITE_UINT_FIELD(agglevelsup);
+ WRITE_BOOL_FIELD(aggstar);
+ WRITE_BOOL_FIELD(aggdistinct);
+}
+
+static void
+_outArrayRef(DebugGraph *graph, DebugNode *parent, const char *label, ArrayRef *node)
+{
+ WRITE_NODE_TYPE("ArrayRef");
+
+ WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refelemtype);
+ WRITE_INT_FIELD(reftypmod);
+ WRITE_NODE_FIELD(refupperindexpr);
+ WRITE_NODE_FIELD(reflowerindexpr);
+ WRITE_NODE_FIELD(refexpr);
+ WRITE_NODE_FIELD(refassgnexpr);
+}
+
+static void
+_outFuncExpr(DebugGraph *graph, DebugNode *parent, const char *label, FuncExpr *node)
+{
+ WRITE_NODE_TYPE("FuncExpr");
+
+ WRITE_OID_FIELD(funcid);
+ WRITE_OID_FIELD(funcresulttype);
+ WRITE_BOOL_FIELD(funcretset);
+ WRITE_ENUM_FIELD(funcformat, CoercionForm);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outOpExpr(DebugGraph *graph, DebugNode *parent, const char *label, OpExpr *node)
+{
+ WRITE_NODE_TYPE("OpExpr");
+
+ WRITE_OID_FIELD(opno);
+ WRITE_OID_FIELD(opfuncid);
+ WRITE_OID_FIELD(opresulttype);
+ WRITE_BOOL_FIELD(opretset);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outDistinctExpr(DebugGraph *graph, DebugNode *parent, const char *label, DistinctExpr *node)
+{
+ WRITE_NODE_TYPE("DistinctExpr");
+
+ WRITE_OID_FIELD(opno);
+ WRITE_OID_FIELD(opfuncid);
+ WRITE_OID_FIELD(opresulttype);
+ WRITE_BOOL_FIELD(opretset);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outScalarArrayOpExpr(DebugGraph *graph, DebugNode *parent, const char *label,
+ ScalarArrayOpExpr *node)
+{
+ WRITE_NODE_TYPE("ScalarArrayOpExpr");
+
+ WRITE_OID_FIELD(opno);
+ WRITE_OID_FIELD(opfuncid);
+ WRITE_BOOL_FIELD(useOr);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outBoolExpr(DebugGraph *graph, DebugNode *parent, const char *label,
+ 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;
+ }
+ addDebugNodeAttribute(graphnode, "boolop", opstr);
+
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outSubLink(DebugGraph *graph, DebugNode *parent, const char *label,
+ SubLink *node)
+{
+ WRITE_NODE_TYPE("SubLink");
+
+ WRITE_ENUM_FIELD(subLinkType, SubLinkType);
+ WRITE_NODE_FIELD(testexpr);
+ WRITE_NODE_FIELD(operName);
+ WRITE_NODE_FIELD(subselect);
+}
+
+static void
+_outSubPlan(DebugGraph *graph, DebugNode *parent, const char *label,
+ 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_OID_FIELD(firstColType);
+ WRITE_BOOL_FIELD(useHashTable);
+ WRITE_BOOL_FIELD(unknownEqFalse);
+ WRITE_NODE_FIELD(setParam);
+ WRITE_NODE_FIELD(parParam);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outFieldSelect(DebugGraph *graph, DebugNode *parent, const char *label,
+ FieldSelect *node)
+{
+ WRITE_NODE_TYPE("FieldSelect");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_INT_FIELD(fieldnum);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_INT_FIELD(resulttypmod);
+}
+
+static void
+_outFieldStore(DebugGraph *graph, DebugNode *parent, const char *label, 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(DebugGraph *graph, DebugNode *parent, const char *label, RelabelType *node)
+{
+ WRITE_NODE_TYPE("RelabelType");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_INT_FIELD(resulttypmod);
+ WRITE_ENUM_FIELD(relabelformat, CoercionForm);
+}
+
+static void
+_outCoerceViaIO(DebugGraph *graph, DebugNode *parent, const char *label, CoerceViaIO *node)
+{
+ WRITE_NODE_TYPE("CoerceViaIO");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_ENUM_FIELD(coerceformat, CoercionForm);
+}
+
+static void
+_outArrayCoerceExpr(DebugGraph *graph, DebugNode *parent, const char *label,
+ ArrayCoerceExpr *node)
+{
+ WRITE_NODE_TYPE("ArrayCoerceExpr");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(elemfuncid);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_INT_FIELD(resulttypmod);
+ WRITE_BOOL_FIELD(isExplicit);
+ WRITE_ENUM_FIELD(coerceformat, CoercionForm);
+}
+
+static void
+_outConvertRowtypeExpr(DebugGraph *graph, DebugNode *parent, const char *label,
+ ConvertRowtypeExpr *node)
+{
+ WRITE_NODE_TYPE("ConvertRowtypeExpr");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_ENUM_FIELD(convertformat, CoercionForm);
+}
+
+static void
+_outCaseExpr(DebugGraph *graph, DebugNode *parent, const char *label, CaseExpr *node)
+{
+ WRITE_NODE_TYPE("CaseExpr");
+
+ WRITE_OID_FIELD(casetype);
+ WRITE_NODE_FIELD(arg);
+ WRITE_NODE_FIELD(args);
+ WRITE_NODE_FIELD(defresult);
+}
+
+static void
+_outCaseWhen(DebugGraph *graph, DebugNode *parent, const char *label, CaseWhen *node)
+{
+ WRITE_NODE_TYPE("CaseExpr");
+
+ WRITE_NODE_FIELD(expr);
+ WRITE_NODE_FIELD(result);
+}
+
+static void
+_outCaseTestExpr(DebugGraph *graph, DebugNode *parent, const char *label, CaseTestExpr *node)
+{
+ WRITE_NODE_TYPE("CaseTestExpr");
+
+ WRITE_OID_FIELD(typeId);
+ WRITE_INT_FIELD(typeMod);
+}
+
+static void
+_outArrayExpr(DebugGraph *graph, DebugNode *parent, const char *label, ArrayExpr *node)
+{
+ WRITE_NODE_TYPE("ArrayExpr");
+
+ WRITE_OID_FIELD(array_typeid);
+ WRITE_OID_FIELD(element_typeid);
+ WRITE_NODE_FIELD(elements);
+ WRITE_BOOL_FIELD(multidims);
+}
+
+static void
+_outRowExpr(DebugGraph *graph, DebugNode *parent, const char *label, RowExpr *node)
+{
+ WRITE_NODE_TYPE("RowExpr");
+
+ WRITE_NODE_FIELD(args);
+ WRITE_OID_FIELD(row_typeid);
+ WRITE_ENUM_FIELD(row_format, CoercionForm);
+}
+
+static void
+_outRowCompareExpr(DebugGraph *graph, DebugNode *parent, const char *label, RowCompareExpr *node)
+{
+ WRITE_NODE_TYPE("RowCompareExpr");
+
+ WRITE_ENUM_FIELD(rctype, RowCompareType);
+ WRITE_NODE_FIELD(opnos);
+ WRITE_NODE_FIELD(opfamilies);
+ WRITE_NODE_FIELD(largs);
+ WRITE_NODE_FIELD(rargs);
+}
+
+static void
+_outCoalesceExpr(DebugGraph *graph, DebugNode *parent, const char *label, CoalesceExpr *node)
+{
+ WRITE_NODE_TYPE("CoalesceExpr");
+
+ WRITE_OID_FIELD(coalescetype);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outMinMaxExpr(DebugGraph *graph, DebugNode *parent, const char *label, MinMaxExpr *node)
+{
+ WRITE_NODE_TYPE("MinMaxExpr");
+
+ WRITE_OID_FIELD(minmaxtype);
+ WRITE_ENUM_FIELD(op, MinMaxOp);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outXmlExpr(DebugGraph *graph, DebugNode *parent, const char *label, 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);
+}
+
+static void
+_outNullIfExpr(DebugGraph *graph, DebugNode *parent, const char *label, NullIfExpr *node)
+{
+ WRITE_NODE_TYPE("NullIfExpr");
+
+ WRITE_OID_FIELD(opno);
+ WRITE_OID_FIELD(opfuncid);
+ WRITE_OID_FIELD(opresulttype);
+ WRITE_BOOL_FIELD(opretset);
+ WRITE_NODE_FIELD(args);
+}
+
+static void
+_outNullTest(DebugGraph *graph, DebugNode *parent, const char *label, NullTest *node)
+{
+ WRITE_NODE_TYPE("NullTest");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_ENUM_FIELD(nulltesttype, NullTestType);
+}
+
+static void
+_outBooleanTest(DebugGraph *graph, DebugNode *parent, const char *label, BooleanTest *node)
+{
+ WRITE_NODE_TYPE("BooleanTest");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_ENUM_FIELD(booltesttype, BoolTestType);
+}
+
+static void
+_outCoerceToDomain(DebugGraph *graph, DebugNode *parent, const char *label, CoerceToDomain *node)
+{
+ WRITE_NODE_TYPE("CoerceToDomain");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_OID_FIELD(resulttype);
+ WRITE_INT_FIELD(resulttypmod);
+ WRITE_ENUM_FIELD(coercionformat, CoercionForm);
+}
+
+static void
+_outCoerceToDomainValue(DebugGraph *graph, DebugNode *parent, const char *label,
+ CoerceToDomainValue *node)
+{
+ WRITE_NODE_TYPE("CoerceToDomainValue");
+
+ WRITE_OID_FIELD(typeId);
+ WRITE_INT_FIELD(typeMod);
+}
+
+static void
+_outSetToDefault(DebugGraph *graph, DebugNode *parent, const char *label,
+ SetToDefault *node)
+{
+ WRITE_NODE_TYPE("SetToDefault");
+
+ WRITE_OID_FIELD(typeId);
+ WRITE_INT_FIELD(typeMod);
+}
+
+static void
+_outCurrentOfExpr(DebugGraph *graph, DebugNode *parent, const char *label,
+ CurrentOfExpr *node)
+{
+ WRITE_NODE_TYPE("CurrentOfExpr");
+
+ WRITE_UINT_FIELD(cvarno);
+ WRITE_STRING_FIELD(cursor_name);
+ WRITE_INT_FIELD(cursor_param);
+}
+
+static void
+_outTargetEntry(DebugGraph *graph, DebugNode *parent, const char *label, 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(DebugGraph *graph, DebugNode *parent, const char *label, RangeTblRef *node)
+{
+ WRITE_NODE_TYPE("RangeTblRef");
+
+ WRITE_INT_FIELD(rtindex);
+}
+
+static void
+_outJoinExpr(DebugGraph *graph, DebugNode *parent, const char *label, 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(using);
+ WRITE_NODE_FIELD(quals);
+ WRITE_NODE_FIELD(alias);
+ WRITE_INT_FIELD(rtindex);
+}
+
+static void
+_outFromExpr(DebugGraph *graph, DebugNode *parent, const char *label, FromExpr *node)
+{
+ WRITE_NODE_TYPE("FromExpr");
+
+ WRITE_NODE_FIELD(fromlist);
+ WRITE_NODE_FIELD(quals);
+}
+
+/*****************************************************************************
+ *
+ * Stuff from relation.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
+ */
+static void
+_outPathInfo(DebugGraph *graph, DebugNode *graphnode, Path *node)
+{
+ WRITE_ENUM_FIELD(pathtype, NodeTag);
+ 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(DebugGraph *graph, DebugNode *graphnode, JoinPath *node)
+{
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_ENUM_FIELD(jointype, JoinType);
+ WRITE_NODE_FIELD(outerjoinpath);
+ WRITE_NODE_FIELD(innerjoinpath);
+ WRITE_NODE_FIELD(joinrestrictinfo);
+}
+
+static void
+_outPath(DebugGraph *graph, DebugNode *parent, const char *label, Path *node)
+{
+ WRITE_NODE_TYPE("Path");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+}
+
+static void
+_outIndexPath(DebugGraph *graph, DebugNode *parent, const char *label, IndexPath *node)
+{
+ WRITE_NODE_TYPE("IndexPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(indexinfo);
+ WRITE_NODE_FIELD(indexclauses);
+ WRITE_NODE_FIELD(indexquals);
+ WRITE_BOOL_FIELD(isjoininner);
+ WRITE_ENUM_FIELD(indexscandir, ScanDirection);
+ WRITE_FLOAT_FIELD(indextotalcost, "%.2f");
+ WRITE_FLOAT_FIELD(indexselectivity, "%.4f");
+ WRITE_FLOAT_FIELD(rows, "%.0f");
+}
+
+static void
+_outBitmapHeapPath(DebugGraph *graph, DebugNode *parent, const char *label, BitmapHeapPath *node)
+{
+ WRITE_NODE_TYPE("BitmapHeapPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(bitmapqual);
+ WRITE_BOOL_FIELD(isjoininner);
+ WRITE_FLOAT_FIELD(rows, "%.0f");
+}
+
+static void
+_outBitmapAndPath(DebugGraph *graph, DebugNode *parent, const char *label, BitmapAndPath *node)
+{
+ WRITE_NODE_TYPE("BitmapAndPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(bitmapquals);
+ WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f");
+}
+
+static void
+_outBitmapOrPath(DebugGraph *graph, DebugNode *parent, const char *label, BitmapOrPath *node)
+{
+ WRITE_NODE_TYPE("BitmapOrPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(bitmapquals);
+ WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f");
+}
+
+static void
+_outTidPath(DebugGraph *graph, DebugNode *parent, const char *label, TidPath *node)
+{
+ WRITE_NODE_TYPE("TidPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(tidquals);
+}
+
+static void
+_outAppendPath(DebugGraph *graph, DebugNode *parent, const char *label, AppendPath *node)
+{
+ WRITE_NODE_TYPE("AppendPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(subpaths);
+}
+
+static void
+_outResultPath(DebugGraph *graph, DebugNode *parent, const char *label, ResultPath *node)
+{
+ WRITE_NODE_TYPE("ResultPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(quals);
+}
+
+static void
+_outMaterialPath(DebugGraph *graph, DebugNode *parent, const char *label, MaterialPath *node)
+{
+ WRITE_NODE_TYPE("MaterialPath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(subpath);
+}
+
+static void
+_outUniquePath(DebugGraph *graph, DebugNode *parent, const char *label, UniquePath *node)
+{
+ WRITE_NODE_TYPE("UniquePath");
+
+ _outPathInfo(graph, graphnode, (Path *) node);
+
+ WRITE_NODE_FIELD(subpath);
+ WRITE_ENUM_FIELD(umethod, UniquePathMethod);
+ WRITE_FLOAT_FIELD(rows, "%.0f");
+}
+
+static void
+_outNestPath(DebugGraph *graph, DebugNode *parent, const char *label, NestPath *node)
+{
+ WRITE_NODE_TYPE("NestPath");
+
+ _outJoinPathInfo(graph, graphnode, (JoinPath *) node);
+}
+
+static void
+_outMergePath(DebugGraph *graph, DebugNode *parent, const char *label, MergePath *node)
+{
+ WRITE_NODE_TYPE("MergePath");
+
+ _outJoinPathInfo(graph, graphnode, (JoinPath *) node);
+
+ WRITE_NODE_FIELD(path_mergeclauses);
+ WRITE_NODE_FIELD(outersortkeys);
+ WRITE_NODE_FIELD(innersortkeys);
+}
+
+static void
+_outHashPath(DebugGraph *graph, DebugNode *parent, const char *label, HashPath *node)
+{
+ WRITE_NODE_TYPE("HashPath");
+
+ _outJoinPathInfo(graph, graphnode, (JoinPath *) node);
+
+ WRITE_NODE_FIELD(path_hashclauses);
+}
+
+static void
+_outPlannerGlobal(DebugGraph *graph, DebugNode *parent, const char *label, PlannerGlobal *node)
+{
+ WRITE_NODE_TYPE("PlannerGlobal");
+
+ /* NB: this isn't a complete set of fields */
+ WRITE_NODE_FIELD(paramlist);
+ WRITE_NODE_FIELD(subplans);
+ WRITE_NODE_FIELD(subrtables);
+ WRITE_BITMAPSET_FIELD(rewindPlanIDs);
+ WRITE_NODE_FIELD(finalrtable);
+ WRITE_NODE_FIELD(relationOids);
+}
+
+static void
+_outPlannerInfo(DebugGraph *graph, DebugNode *parent, const char *label, 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(join_rel_list);
+ WRITE_NODE_FIELD(resultRelations);
+ WRITE_NODE_FIELD(returningLists);
+ WRITE_NODE_FIELD(init_plans);
+ WRITE_NODE_FIELD(eq_classes);
+ 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(oj_info_list);
+ WRITE_NODE_FIELD(in_info_list);
+ WRITE_NODE_FIELD(append_rel_list);
+ WRITE_NODE_FIELD(query_pathkeys);
+ WRITE_NODE_FIELD(group_pathkeys);
+ WRITE_NODE_FIELD(sort_pathkeys);
+ WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
+ WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
+ WRITE_BOOL_FIELD(hasJoinRTEs);
+ WRITE_BOOL_FIELD(hasOuterJoins);
+ WRITE_BOOL_FIELD(hasHavingQual);
+ WRITE_BOOL_FIELD(hasPseudoConstantQuals);
+}
+
+static void
+_outRelOptInfo(DebugGraph *graph, DebugNode *parent, const char *label, 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_INT_FIELD(width);
+ WRITE_NODE_FIELD(reltargetlist);
+ WRITE_NODE_FIELD(pathlist);
+ WRITE_NODE_FIELD(cheapest_startup_path);
+ WRITE_NODE_FIELD(cheapest_total_path);
+ WRITE_NODE_FIELD(cheapest_unique_path);
+ WRITE_UINT_FIELD(relid);
+ WRITE_ENUM_FIELD(rtekind, RTEKind);
+ WRITE_INT_FIELD(min_attr);
+ WRITE_INT_FIELD(max_attr);
+ WRITE_NODE_FIELD(indexlist);
+ WRITE_UINT_FIELD(pages);
+ WRITE_FLOAT_FIELD(tuples, "%.0f");
+ WRITE_NODE_FIELD(subplan);
+ WRITE_NODE_FIELD(subrtable);
+ WRITE_NODE_FIELD(baserestrictinfo);
+ WRITE_NODE_FIELD(joininfo);
+ WRITE_BOOL_FIELD(has_eclass_joins);
+ WRITE_BITMAPSET_FIELD(index_outer_relids);
+ WRITE_NODE_FIELD(index_inner_paths);
+}
+
+static void
+_outIndexOptInfo(DebugGraph *graph, DebugNode *parent, const char *label, 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(ncolumns);
+ WRITE_NODE_FIELD(indexprs);
+ WRITE_NODE_FIELD(indpred);
+ WRITE_BOOL_FIELD(predOK);
+ WRITE_BOOL_FIELD(unique);
+}
+
+static void
+_outEquivalenceClass(DebugGraph *graph, DebugNode *parent, const char *label, 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_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);
+}
+
+static void
+_outEquivalenceMember(DebugGraph *graph, DebugNode *parent, const char *label, EquivalenceMember *node)
+{
+ WRITE_NODE_TYPE("EquivalenceMember");
+
+ WRITE_NODE_FIELD(em_expr);
+ WRITE_BITMAPSET_FIELD(em_relids);
+ WRITE_BOOL_FIELD(em_is_const);
+ WRITE_BOOL_FIELD(em_is_child);
+ WRITE_OID_FIELD(em_datatype);
+}
+
+static void
+_outPathKey(DebugGraph *graph, DebugNode *parent, const char *label, 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
+_outRestrictInfo(DebugGraph *graph, DebugNode *parent, const char *label, 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_BITMAPSET_FIELD(clause_relids);
+ WRITE_BITMAPSET_FIELD(required_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_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
+_outInnerIndexscanInfo(DebugGraph *graph, DebugNode *parent, const char *label, InnerIndexscanInfo *node)
+{
+ WRITE_NODE_TYPE("InnerIndexscanInfo");
+ WRITE_BITMAPSET_FIELD(other_relids);
+ WRITE_BOOL_FIELD(isouterjoin);
+ WRITE_NODE_FIELD(cheapest_startup_innerpath);
+ WRITE_NODE_FIELD(cheapest_total_innerpath);
+}
+
+static void
+_outOuterJoinInfo(DebugGraph *graph, DebugNode *parent, const char *label, OuterJoinInfo *node)
+{
+ WRITE_NODE_TYPE("OuterJoinInfo");
+
+ WRITE_BITMAPSET_FIELD(min_lefthand);
+ WRITE_BITMAPSET_FIELD(min_righthand);
+ WRITE_BITMAPSET_FIELD(syn_lefthand);
+ WRITE_BITMAPSET_FIELD(syn_righthand);
+ WRITE_BOOL_FIELD(is_full_join);
+ WRITE_BOOL_FIELD(lhs_strict);
+ WRITE_BOOL_FIELD(delay_upper_joins);
+}
+
+static void
+_outInClauseInfo(DebugGraph *graph, DebugNode *parent, const char *label, InClauseInfo *node)
+{
+ WRITE_NODE_TYPE("InClauseInfo");
+
+ WRITE_BITMAPSET_FIELD(lefthand);
+ WRITE_BITMAPSET_FIELD(righthand);
+ WRITE_NODE_FIELD(sub_targetlist);
+ WRITE_NODE_FIELD(in_operators);
+}
+
+static void
+_outAppendRelInfo(DebugGraph *graph, DebugNode *parent, const char *label, 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(col_mappings);
+ WRITE_NODE_FIELD(translated_vars);
+ WRITE_OID_FIELD(parent_reloid);
+}
+
+static void
+_outPlannerParamItem(DebugGraph *graph, DebugNode *parent, const char *label, PlannerParamItem *node)
+{
+ WRITE_NODE_TYPE("PlannerParamItem");
+
+ WRITE_NODE_FIELD(item);
+ WRITE_UINT_FIELD(abslevel);
+}
+
+/*****************************************************************************
+ *
+ * Stuff from parsenodes.h.
+ *
+ *****************************************************************************/
+
+static void
+_outCreateStmt(DebugGraph *graph, DebugNode *parent, const char *label, CreateStmt *node)
+{
+ WRITE_NODE_TYPE("CreateStmt");
+
+ WRITE_NODE_FIELD(relation);
+ WRITE_NODE_FIELD(tableElts);
+ WRITE_NODE_FIELD(inhRelations);
+ WRITE_NODE_FIELD(constraints);
+ WRITE_NODE_FIELD(options);
+ WRITE_ENUM_FIELD(oncommit, OnCommitAction);
+ WRITE_STRING_FIELD(tablespacename);
+}
+
+static void
+_outIndexStmt(DebugGraph *graph, DebugNode *parent, const char *label, 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(options);
+ WRITE_NODE_FIELD(whereClause);
+ WRITE_BOOL_FIELD(unique);
+ WRITE_BOOL_FIELD(primary);
+ WRITE_BOOL_FIELD(isconstraint);
+ WRITE_BOOL_FIELD(concurrent);
+}
+
+static void
+_outNotifyStmt(DebugGraph *graph, DebugNode *parent, const char *label, NotifyStmt *node)
+{
+ WRITE_NODE_TYPE("NotifyStmt");
+
+ WRITE_NODE_FIELD(relation);
+}
+
+static void
+_outDeclareCursorStmt(DebugGraph *graph, DebugNode *parent, const char *label, DeclareCursorStmt *node)
+{
+ WRITE_NODE_TYPE("DeclareCursorStmt");
+
+ WRITE_STRING_FIELD(portalname);
+ WRITE_INT_FIELD(options);
+ WRITE_NODE_FIELD(query);
+}
+
+static void
+_outSelectStmt(DebugGraph *graph, DebugNode *parent, const char *label, SelectStmt *node)
+{
+ WRITE_NODE_TYPE("SelectStmt");
+
+ 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(valuesLists);
+ WRITE_NODE_FIELD(sortClause);
+ WRITE_NODE_FIELD(limitOffset);
+ WRITE_NODE_FIELD(limitCount);
+ WRITE_NODE_FIELD(lockingClause);
+ WRITE_ENUM_FIELD(op, SetOperation);
+ WRITE_BOOL_FIELD(all);
+ WRITE_NODE_FIELD(larg);
+ WRITE_NODE_FIELD(rarg);
+}
+
+static void
+_outFuncCall(DebugGraph *graph, DebugNode *parent, const char *label, FuncCall *node)
+{
+ WRITE_NODE_TYPE("FuncCall");
+
+ WRITE_NODE_FIELD(funcname);
+ WRITE_NODE_FIELD(args);
+ WRITE_BOOL_FIELD(agg_star);
+ WRITE_BOOL_FIELD(agg_distinct);
+ WRITE_INT_FIELD(location);
+}
+
+static void
+_outDefElem(DebugGraph *graph, DebugNode *parent, const char *label, DefElem *node)
+{
+ WRITE_NODE_TYPE("DefElem");
+
+ WRITE_STRING_FIELD(defname);
+ WRITE_NODE_FIELD(arg);
+}
+
+static void
+_outLockingClause(DebugGraph *graph, DebugNode *parent, const char *label, LockingClause *node)
+{
+ WRITE_NODE_TYPE("LockingClause");
+
+ WRITE_NODE_FIELD(lockedRels);
+ WRITE_BOOL_FIELD(forUpdate);
+ WRITE_BOOL_FIELD(noWait);
+}
+
+static void
+_outXmlSerialize(DebugGraph *graph, DebugNode *parent, const char *label, XmlSerialize *node)
+{
+ WRITE_NODE_TYPE("XmlSerialize");
+
+ WRITE_ENUM_FIELD(xmloption, XmlOptionType);
+ WRITE_NODE_FIELD(expr);
+ WRITE_NODE_FIELD(typename);
+}
+
+static void
+_outColumnDef(DebugGraph *graph, DebugNode *parent, const char *label, 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_NODE_FIELD(raw_default);
+ WRITE_STRING_FIELD(cooked_default);
+ WRITE_NODE_FIELD(constraints);
+}
+
+static void
+_outTypeName(DebugGraph *graph, DebugNode *parent, const char *label, TypeName *node)
+{
+ WRITE_NODE_TYPE("TypeName");
+
+ WRITE_NODE_FIELD(names);
+ WRITE_OID_FIELD(typeid);
+ WRITE_BOOL_FIELD(timezone);
+ WRITE_BOOL_FIELD(setof);
+ WRITE_BOOL_FIELD(pct_type);
+ WRITE_NODE_FIELD(typmods);
+ WRITE_INT_FIELD(typemod);
+ WRITE_NODE_FIELD(arrayBounds);
+ WRITE_INT_FIELD(location);
+}
+
+static void
+_outTypeCast(DebugGraph *graph, DebugNode *parent, const char *label, TypeCast *node)
+{
+ WRITE_NODE_TYPE("TypeCast");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_NODE_FIELD(typename);
+}
+
+static void
+_outIndexElem(DebugGraph *graph, DebugNode *parent, const char *label, IndexElem *node)
+{
+ WRITE_NODE_TYPE("IndexElem");
+
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(expr);
+ WRITE_NODE_FIELD(opclass);
+ WRITE_ENUM_FIELD(ordering, SortByDir);
+ WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
+}
+
+static void
+_outQuery(DebugGraph *graph, DebugNode *parent, const char *label, Query *node)
+{
+ WRITE_NODE_TYPE("Query");
+
+ WRITE_ENUM_FIELD(commandType, CmdType);
+ WRITE_ENUM_FIELD(querySource, QuerySource);
+ 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:
+ addDebugNodeAttribute(graphnode, "utilityStmt", "?");
+ break;
+ }
+ }
+ else
+ addDebugNodeAttribute(graphnode, "utilityStmt", "NULL");
+
+ WRITE_INT_FIELD(resultRelation);
+ WRITE_NODE_FIELD(intoClause);
+ WRITE_BOOL_FIELD(hasAggs);
+ WRITE_BOOL_FIELD(hasSubLinks);
+ WRITE_NODE_FIELD(rtable);
+ WRITE_NODE_FIELD(jointree);
+ WRITE_NODE_FIELD(targetList);
+ WRITE_NODE_FIELD(returningList);
+ WRITE_NODE_FIELD(groupClause);
+ WRITE_NODE_FIELD(havingQual);
+ WRITE_NODE_FIELD(distinctClause);
+ WRITE_NODE_FIELD(sortClause);
+ WRITE_NODE_FIELD(limitOffset);
+ WRITE_NODE_FIELD(limitCount);
+ WRITE_NODE_FIELD(rowMarks);
+ WRITE_NODE_FIELD(setOperations);
+}
+
+static void
+_outSortClause(DebugGraph *graph, DebugNode *parent, const char *label, SortClause *node)
+{
+ WRITE_NODE_TYPE("SortClause");
+
+ WRITE_UINT_FIELD(tleSortGroupRef);
+ WRITE_OID_FIELD(sortop);
+ WRITE_BOOL_FIELD(nulls_first);
+}
+
+static void
+_outGroupClause(DebugGraph *graph, DebugNode *parent, const char *label, GroupClause *node)
+{
+ WRITE_NODE_TYPE("GroupClause");
+
+ WRITE_UINT_FIELD(tleSortGroupRef);
+ WRITE_OID_FIELD(sortop);
+ WRITE_BOOL_FIELD(nulls_first);
+}
+
+static void
+_outRowMarkClause(DebugGraph *graph, DebugNode *parent, const char *label, RowMarkClause *node)
+{
+ WRITE_NODE_TYPE("RowMarkClause");
+
+ WRITE_UINT_FIELD(rti);
+ WRITE_BOOL_FIELD(forUpdate);
+ WRITE_BOOL_FIELD(noWait);
+}
+
+static void
+_outSetOperationStmt(DebugGraph *graph, DebugNode *parent, const char *label, 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);
+}
+
+static void
+_outRangeTblEntry(DebugGraph *graph, DebugNode *parent, const char *label, RangeTblEntry *node)
+{
+ WRITE_NODE_TYPE("RangeTblEntry");
+
+ /* put alias + eref first to make dump more legible */
+ WRITE_NODE_FIELD(alias);
+ WRITE_NODE_FIELD(eref);
+ WRITE_ENUM_FIELD(rtekind, RTEKind);
+
+ switch (node->rtekind)
+ {
+ case RTE_RELATION:
+ case RTE_SPECIAL:
+ WRITE_OID_FIELD(relid);
+ break;
+ case RTE_SUBQUERY:
+ WRITE_NODE_FIELD(subquery);
+ break;
+ case RTE_FUNCTION:
+ WRITE_NODE_FIELD(funcexpr);
+ WRITE_NODE_FIELD(funccoltypes);
+ WRITE_NODE_FIELD(funccoltypmods);
+ break;
+ case RTE_VALUES:
+ WRITE_NODE_FIELD(values_lists);
+ break;
+ case RTE_JOIN:
+ WRITE_ENUM_FIELD(jointype, JoinType);
+ WRITE_NODE_FIELD(joinaliasvars);
+ break;
+ default:
+ addDebugNodeAttributeArgs(graphnode, "rtekind",
+ "unrecognized RTE kind: %d",
+ (int) node->rtekind);
+ break;
+ }
+
+ WRITE_BOOL_FIELD(inh);
+ WRITE_BOOL_FIELD(inFromCl);
+ WRITE_UINT_FIELD(requiredPerms);
+ WRITE_OID_FIELD(checkAsUser);
+}
+
+static void
+_outAExpr(DebugGraph *graph, DebugNode *parent, const char *label, A_Expr *node)
+{
+ WRITE_NODE_TYPE("A_Expr");
+
+ switch (node->kind)
+ {
+ case AEXPR_OP:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_OP");
+ WRITE_NODE_FIELD(name);
+ break;
+ case AEXPR_AND:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_AND");
+ break;
+ case AEXPR_OR:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_OR");
+ break;
+ case AEXPR_NOT:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_NOT");
+ break;
+ case AEXPR_OP_ANY:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_OP_ANY");
+ WRITE_NODE_FIELD(name);
+ break;
+ case AEXPR_OP_ALL:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_OP_ALL");
+ WRITE_NODE_FIELD(name);
+ break;
+ case AEXPR_DISTINCT:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_DISTINCT");
+ WRITE_NODE_FIELD(name);
+ break;
+ case AEXPR_NULLIF:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_NULLIF");
+ WRITE_NODE_FIELD(name);
+ break;
+ case AEXPR_OF:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_OF");
+ WRITE_NODE_FIELD(name);
+ break;
+ case AEXPR_IN:
+ addDebugNodeAttribute(graphnode, "kind", "AEXPR_IN");
+ WRITE_NODE_FIELD(name);
+ break;
+ default:
+ addDebugNodeAttribute(graphnode, "kind", "??");
+ break;
+ }
+
+ WRITE_NODE_FIELD(lexpr);
+ WRITE_NODE_FIELD(rexpr);
+ WRITE_INT_FIELD(location);
+}
+
+static void
+_outValue(DebugGraph *graph, DebugNode *parent, const char *label, Value *value)
+{
+ DebugNode *graphnode;
+ if( parent ){
+ graphnode = parent;
+ } else {
+ char aux[100];
+ graphnode = newDebugNode(graph, addressToName(aux, value), "Value");
+ }
+
+ switch (value->type)
+ {
+ case T_Integer:
+ addDebugNodeAttributeArgs(graphnode, label, "%ld", value->val.ival);
+ break;
+ case T_Float:
+
+ /*
+ * We assume the value is a valid numeric literal and so does not
+ * need quoting.
+ */
+ addDebugNodeAttribute(graphnode, label, value->val.str);
+ break;
+ case T_String:
+ _outToken(graph, graphnode, label, value->val.str);
+ break;
+ case T_BitString:
+ /* internal representation already has leading 'b' */
+ addDebugNodeAttribute(graphnode, label, value->val.str);
+ break;
+ case T_Null:
+ /* this is seen only within A_Const, not in transformed trees */
+ addDebugNodeAttribute(graphnode, label, "NULL");
+ break;
+ default:
+ addDebugNodeAttributeArgs(graphnode, label,
+ "unrecognized node type: %d", (int) value->type);
+ break;
+ }
+}
+
+static void
+_outColumnRef(DebugGraph *graph, DebugNode *parent, const char *label,
+ ColumnRef *node)
+{
+ WRITE_NODE_TYPE("ColumnRef");
+
+ WRITE_NODE_FIELD(fields);
+ WRITE_INT_FIELD(location);
+}
+
+static void
+_outParamRef(DebugGraph *graph, DebugNode *parent, const char *label,
+ ParamRef *node)
+{
+ WRITE_NODE_TYPE("ParamRef");
+
+ WRITE_INT_FIELD(number);
+}
+
+static void
+_outAConst(DebugGraph *graph, DebugNode *parent, const char *label, A_Const *node)
+{
+ WRITE_NODE_TYPE("A_Const");
+
+ _outValue(graph, graphnode, "val", &(node->val));
+ WRITE_NODE_FIELD(typename);
+}
+
+static void
+_outA_Indices(DebugGraph *graph, DebugNode *parent, const char *label,
+ A_Indices *node)
+{
+ WRITE_NODE_TYPE("A_Indices");
+
+ WRITE_NODE_FIELD(lidx);
+ WRITE_NODE_FIELD(uidx);
+}
+
+static void
+_outA_Indirection(DebugGraph *graph, DebugNode *parent, const char *label,
+ A_Indirection *node)
+{
+ WRITE_NODE_TYPE("A_Indirection");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_NODE_FIELD(indirection);
+}
+
+static void
+_outResTarget(DebugGraph *graph, DebugNode *parent, const char *label, ResTarget *node)
+{
+ WRITE_NODE_TYPE("ResTarget");
+
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(indirection);
+ WRITE_NODE_FIELD(val);
+ WRITE_INT_FIELD(location);
+}
+
+static void
+_outConstraint(DebugGraph *graph, DebugNode *parent, const char *label,
+ Constraint *node)
+{
+ WRITE_NODE_TYPE("Constraint");
+
+ WRITE_STRING_FIELD(name);
+
+ switch (node->contype)
+ {
+ case CONSTR_PRIMARY:
+ addDebugNodeAttribute(graphnode, "contype", "PRIMARY_KEY");
+ WRITE_NODE_FIELD(keys);
+ WRITE_NODE_FIELD(options);
+ WRITE_STRING_FIELD(indexspace);
+ break;
+
+ case CONSTR_UNIQUE:
+ addDebugNodeAttribute(graphnode, "contype", "UNIQUE");
+ WRITE_NODE_FIELD(keys);
+ WRITE_NODE_FIELD(options);
+ WRITE_STRING_FIELD(indexspace);
+ break;
+
+ case CONSTR_CHECK:
+ addDebugNodeAttribute(graphnode, "contype", "CHECK");
+ WRITE_NODE_FIELD(raw_expr);
+ WRITE_STRING_FIELD(cooked_expr);
+ break;
+
+ case CONSTR_DEFAULT:
+ addDebugNodeAttribute(graphnode, "contype", "DEFAULT");
+ WRITE_NODE_FIELD(raw_expr);
+ WRITE_STRING_FIELD(cooked_expr);
+ break;
+
+ case CONSTR_NOTNULL:
+ addDebugNodeAttribute(graphnode, "contype", "NOT_NULL");
+ break;
+
+ default:
+ addDebugNodeAttribute(graphnode, "contype",
+ "<unrecognized_constraint>");
+ break;
+ }
+}
+
+static void
+_outFkConstraint(DebugGraph *graph, DebugNode *parent, const char *label,
+ FkConstraint *node)
+{
+ WRITE_NODE_TYPE("FkConstraint");
+
+ WRITE_STRING_FIELD(constr_name);
+ 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_BOOL_FIELD(deferrable);
+ WRITE_BOOL_FIELD(initdeferred);
+ WRITE_BOOL_FIELD(skip_validation);
+}
+
+
+/*
+ * _outNode -
+ * converts a Node into ascii string and append it to 'str'
+ */
+static void
+_outNode(DebugGraph *graph, DebugNode *parent, const char *label, void *obj)
+{
+ if (obj == NULL)
+ return;
+ else if (IsA(obj, List) ||IsA(obj, IntList) || IsA(obj, OidList))
+ _outList(graph, parent, label, obj);
+ else if (IsA(obj, Integer) ||
+ IsA(obj, Float) ||
+ IsA(obj, String) ||
+ IsA(obj, BitString))
+ {
+ /* nodeRead does not want to see { } around these! */
+ _outValue(graph, parent, label, obj);
+ }
+ else
+ {
+ switch (nodeTag(obj))
+ {
+ case T_PlannedStmt:
+ _outPlannedStmt(graph, parent, label, obj);
+ break;
+ case T_Plan:
+ _outPlan(graph, parent, label, obj);
+ break;
+ case T_Result:
+ _outResult(graph, parent, label, obj);
+ break;
+ case T_Append:
+ _outAppend(graph, parent, label, obj);
+ break;
+ case T_BitmapAnd:
+ _outBitmapAnd(graph, parent, label, obj);
+ break;
+ case T_BitmapOr:
+ _outBitmapOr(graph, parent, label, obj);
+ break;
+ case T_Scan:
+ _outScan(graph, parent, label, obj);
+ break;
+ case T_SeqScan:
+ _outSeqScan(graph, parent, label, obj);
+ break;
+ case T_IndexScan:
+ _outIndexScan(graph, parent, label, obj);
+ break;
+ case T_BitmapIndexScan:
+ _outBitmapIndexScan(graph, parent, label, obj);
+ break;
+ case T_BitmapHeapScan:
+ _outBitmapHeapScan(graph, parent, label, obj);
+ break;
+ case T_TidScan:
+ _outTidScan(graph, parent, label, obj);
+ break;
+ case T_SubqueryScan:
+ _outSubqueryScan(graph, parent, label, obj);
+ break;
+ case T_FunctionScan:
+ _outFunctionScan(graph, parent, label, obj);
+ break;
+ case T_ValuesScan:
+ _outValuesScan(graph, parent, label, obj);
+ break;
+ case T_Join:
+ _outJoin(graph, parent, label, obj);
+ break;
+ case T_NestLoop:
+ _outNestLoop(graph, parent, label, obj);
+ break;
+ case T_MergeJoin:
+ _outMergeJoin(graph, parent, label, obj);
+ break;
+ case T_HashJoin:
+ _outHashJoin(graph, parent, label, obj);
+ break;
+ case T_Agg:
+ _outAgg(graph, parent, label, obj);
+ break;
+ case T_Group:
+ _outGroup(graph, parent, label, obj);
+ break;
+ case T_Material:
+ _outMaterial(graph, parent, label, obj);
+ break;
+ case T_Sort:
+ _outSort(graph, parent, label, obj);
+ break;
+ case T_Unique:
+ _outUnique(graph, parent, label, obj);
+ break;
+ case T_SetOp:
+ _outSetOp(graph, parent, label, obj);
+ break;
+ case T_Limit:
+ _outLimit(graph, parent, label, obj);
+ break;
+ case T_Hash:
+ _outHash(graph, parent, label, obj);
+ break;
+ case T_Alias:
+ _outAlias(graph, parent, label, obj);
+ break;
+ case T_RangeVar:
+ _outRangeVar(graph, parent, label, obj);
+ break;
+ case T_IntoClause:
+ _outIntoClause(graph, parent, label, obj);
+ break;
+ case T_Var:
+ _outVar(graph, parent, label, obj);
+ break;
+ case T_Const:
+ _outConst(graph, parent, label, obj);
+ break;
+ case T_Param:
+ _outParam(graph, parent, label, obj);
+ break;
+ case T_Aggref:
+ _outAggref(graph, parent, label, obj);
+ break;
+ case T_ArrayRef:
+ _outArrayRef(graph, parent, label, obj);
+ break;
+ case T_FuncExpr:
+ _outFuncExpr(graph, parent, label, obj);
+ break;
+ case T_OpExpr:
+ _outOpExpr(graph, parent, label, obj);
+ break;
+ case T_DistinctExpr:
+ _outDistinctExpr(graph, parent, label, obj);
+ break;
+ case T_ScalarArrayOpExpr:
+ _outScalarArrayOpExpr(graph, parent, label, obj);
+ break;
+ case T_BoolExpr:
+ _outBoolExpr(graph, parent, label, obj);
+ break;
+ case T_SubLink:
+ _outSubLink(graph, parent, label, obj);
+ break;
+ case T_SubPlan:
+ _outSubPlan(graph, parent, label, obj);
+ break;
+ case T_FieldSelect:
+ _outFieldSelect(graph, parent, label, obj);
+ break;
+ case T_FieldStore:
+ _outFieldStore(graph, parent, label, obj);
+ break;
+ case T_RelabelType:
+ _outRelabelType(graph, parent, label, obj);
+ break;
+ case T_CoerceViaIO:
+ _outCoerceViaIO(graph, parent, label, obj);
+ break;
+ case T_ArrayCoerceExpr:
+ _outArrayCoerceExpr(graph, parent, label, obj);
+ break;
+ case T_ConvertRowtypeExpr:
+ _outConvertRowtypeExpr(graph, parent, label, obj);
+ break;
+ case T_CaseExpr:
+ _outCaseExpr(graph, parent, label, obj);
+ break;
+ case T_CaseWhen:
+ _outCaseWhen(graph, parent, label, obj);
+ break;
+ case T_CaseTestExpr:
+ _outCaseTestExpr(graph, parent, label, obj);
+ break;
+ case T_ArrayExpr:
+ _outArrayExpr(graph, parent, label, obj);
+ break;
+ case T_RowExpr:
+ _outRowExpr(graph, parent, label, obj);
+ break;
+ case T_RowCompareExpr:
+ _outRowCompareExpr(graph, parent, label, obj);
+ break;
+ case T_CoalesceExpr:
+ _outCoalesceExpr(graph, parent, label, obj);
+ break;
+ case T_MinMaxExpr:
+ _outMinMaxExpr(graph, parent, label, obj);
+ break;
+ case T_XmlExpr:
+ _outXmlExpr(graph, parent, label, obj);
+ break;
+ case T_NullIfExpr:
+ _outNullIfExpr(graph, parent, label, obj);
+ break;
+ case T_NullTest:
+ _outNullTest(graph, parent, label, obj);
+ break;
+ case T_BooleanTest:
+ _outBooleanTest(graph, parent, label, obj);
+ break;
+ case T_CoerceToDomain:
+ _outCoerceToDomain(graph, parent, label, obj);
+ break;
+ case T_CoerceToDomainValue:
+ _outCoerceToDomainValue(graph, parent, label, obj);
+ break;
+ case T_SetToDefault:
+ _outSetToDefault(graph, parent, label, obj);
+ break;
+ case T_CurrentOfExpr:
+ _outCurrentOfExpr(graph, parent, label, obj);
+ break;
+ case T_TargetEntry:
+ _outTargetEntry(graph, parent, label, obj);
+ break;
+ case T_RangeTblRef:
+ _outRangeTblRef(graph, parent, label, obj);
+ break;
+ case T_JoinExpr:
+ _outJoinExpr(graph, parent, label, obj);
+ break;
+ case T_FromExpr:
+ _outFromExpr(graph, parent, label, obj);
+ break;
+
+ case T_Path:
+ _outPath(graph, parent, label, obj);
+ break;
+ case T_IndexPath:
+ _outIndexPath(graph, parent, label, obj);
+ break;
+ case T_BitmapHeapPath:
+ _outBitmapHeapPath(graph, parent, label, obj);
+ break;
+ case T_BitmapAndPath:
+ _outBitmapAndPath(graph, parent, label, obj);
+ break;
+ case T_BitmapOrPath:
+ _outBitmapOrPath(graph, parent, label, obj);
+ break;
+ case T_TidPath:
+ _outTidPath(graph, parent, label, obj);
+ break;
+ case T_AppendPath:
+ _outAppendPath(graph, parent, label, obj);
+ break;
+ case T_ResultPath:
+ _outResultPath(graph, parent, label, obj);
+ break;
+ case T_MaterialPath:
+ _outMaterialPath(graph, parent, label, obj);
+ break;
+ case T_UniquePath:
+ _outUniquePath(graph, parent, label, obj);
+ break;
+ case T_NestPath:
+ _outNestPath(graph, parent, label, obj);
+ break;
+ case T_MergePath:
+ _outMergePath(graph, parent, label, obj);
+ break;
+ case T_HashPath:
+ _outHashPath(graph, parent, label, obj);
+ break;
+ case T_PlannerGlobal:
+ _outPlannerGlobal(graph, parent, label, obj);
+ break;
+ case T_PlannerInfo:
+ _outPlannerInfo(graph, parent, label, obj);
+ break;
+ case T_RelOptInfo:
+ _outRelOptInfo(graph, parent, label, obj);
+ break;
+ case T_IndexOptInfo:
+ _outIndexOptInfo(graph, parent, label, obj);
+ break;
+ case T_EquivalenceClass:
+ _outEquivalenceClass(graph, parent, label, obj);
+ break;
+ case T_EquivalenceMember:
+ _outEquivalenceMember(graph, parent, label, obj);
+ break;
+ case T_PathKey:
+ _outPathKey(graph, parent, label, obj);
+ break;
+ case T_RestrictInfo:
+ _outRestrictInfo(graph, parent, label, obj);
+ break;
+ case T_InnerIndexscanInfo:
+ _outInnerIndexscanInfo(graph, parent, label, obj);
+ break;
+ case T_OuterJoinInfo:
+ _outOuterJoinInfo(graph, parent, label, obj);
+ break;
+ case T_InClauseInfo:
+ _outInClauseInfo(graph, parent, label, obj);
+ break;
+ case T_AppendRelInfo:
+ _outAppendRelInfo(graph, parent, label, obj);
+ break;
+ case T_PlannerParamItem:
+ _outPlannerParamItem(graph, parent, label, obj);
+ break;
+
+ case T_CreateStmt:
+ _outCreateStmt(graph, parent, label, obj);
+ break;
+ case T_IndexStmt:
+ _outIndexStmt(graph, parent, label, obj);
+ break;
+ case T_NotifyStmt:
+ _outNotifyStmt(graph, parent, label, obj);
+ break;
+ case T_DeclareCursorStmt:
+ _outDeclareCursorStmt(graph, parent, label, obj);
+ break;
+ case T_SelectStmt:
+ _outSelectStmt(graph, parent, label, obj);
+ break;
+ case T_ColumnDef:
+ _outColumnDef(graph, parent, label, obj);
+ break;
+ case T_TypeName:
+ _outTypeName(graph, parent, label, obj);
+ break;
+ case T_TypeCast:
+ _outTypeCast(graph, parent, label, obj);
+ break;
+ case T_IndexElem:
+ _outIndexElem(graph, parent, label, obj);
+ break;
+ case T_Query:
+ _outQuery(graph, parent, label, obj);
+ break;
+ case T_SortClause:
+ _outSortClause(graph, parent, label, obj);
+ break;
+ case T_GroupClause:
+ _outGroupClause(graph, parent, label, obj);
+ break;
+ case T_RowMarkClause:
+ _outRowMarkClause(graph, parent, label, obj);
+ break;
+ case T_SetOperationStmt:
+ _outSetOperationStmt(graph, parent, label, obj);
+ break;
+ case T_RangeTblEntry:
+ _outRangeTblEntry(graph, parent, label, obj);
+ break;
+ case T_A_Expr:
+ _outAExpr(graph, parent, label, obj);
+ break;
+ case T_ColumnRef:
+ _outColumnRef(graph, parent, label, obj);
+ break;
+ case T_ParamRef:
+ _outParamRef(graph, parent, label, obj);
+ break;
+ case T_A_Const:
+ _outAConst(graph, parent, label, obj);
+ break;
+ case T_A_Indices:
+ _outA_Indices(graph, parent, label, obj);
+ break;
+ case T_A_Indirection:
+ _outA_Indirection(graph, parent, label, obj);
+ break;
+ case T_ResTarget:
+ _outResTarget(graph, parent, label, obj);
+ break;
+ case T_Constraint:
+ _outConstraint(graph, parent, label, obj);
+ break;
+ case T_FkConstraint:
+ _outFkConstraint(graph, parent, label, obj);
+ break;
+ case T_FuncCall:
+ _outFuncCall(graph, parent, label, obj);
+ break;
+ case T_DefElem:
+ _outDefElem(graph, parent, label, obj);
+ break;
+ case T_LockingClause:
+ _outLockingClause(graph, parent, label, obj);
+ break;
+ case T_XmlSerialize:
+ _outXmlSerialize(graph, parent, label, 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.
+ */
+ {
+ char name[100];
+ char aux[100];
+ DebugNode *graphnode;
+ sprintf(aux, "unrecognized node type: %d",
+ (int) nodeTag(obj));
+ graphnode = newDebugNode(graph, addressToName(name, obj), aux);
+ if( parent )
+ newDebugEdge(graph, parent->internal_name,
+ graphnode->internal_name, label);
+ }
+ break;
+ }
+ }
+}
+
+DebugGraph *
+createGraphNodes(void *obj)
+{
+ DebugGraph *graph;
+
+ graph = createDebugGraph();
+ _outNode(graph, NULL, "", obj);
+ return graph;
+}
+
+void
+printGraphNodes(void *obj, FILE *file)
+{
+ DebugGraph *graph;
+
+ if( !file && !obj )
+ return;
+
+ graph = createGraphNodes( obj );
+ printGraphvizToFile( graph, file );
+ destroyDebugGraph( graph );
+}
Index: src/backend/nodes/Makefile
===================================================================
--- src/backend/nodes/Makefile (revision 11)
+++ src/backend/nodes/Makefile (working copy)
@@ -14,7 +14,8 @@
OBJS = nodeFuncs.o nodes.o list.o bitmapset.o tidbitmap.o \
copyfuncs.o equalfuncs.o makefuncs.o \
- outfuncs.o readfuncs.o print.o read.o params.o value.o
+ outfuncs.o readfuncs.o print.o read.o params.o value.o \
+ debuggraph.o outfuncs_graph.o
all: SUBSYS.o
Index: src/backend/nodes/print.c
===================================================================
--- src/backend/nodes/print.c (revision 11)
+++ src/backend/nodes/print.c (working copy)
@@ -401,6 +401,111 @@
printf("unknown expr");
}
+int
+print_expr_str(char *output, Node *expr, List *rtable)
+{
+ char *aux = output;
+
+ if (expr == NULL)
+ {
+ aux += sprintf(aux, "<>");
+ return aux - output;
+ }
+
+ if (IsA(expr, Var))
+ {
+ Var *var = (Var *) expr;
+ char *relname,
+ *attname;
+
+ switch (var->varno)
+ {
+ case INNER:
+ relname = "INNER";
+ attname = "?";
+ break;
+ case OUTER:
+ relname = "OUTER";
+ 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;
+ }
+ aux = aux + sprintf(aux, "%s.%s", relname, attname);
+ }
+ else if (IsA(expr, Const))
+ {
+ Const *c = (Const *) expr;
+ Oid typoutput;
+ bool typIsVarlena;
+ char *outputstr;
+
+ if (c->constisnull)
+ {
+ aux = aux + sprintf(aux, "NULL");
+ return;
+ }
+
+ getTypeOutputInfo(c->consttype,
+ &typoutput, &typIsVarlena);
+
+ outputstr = OidOutputFunctionCall(typoutput, c->constvalue);
+ aux = aux + sprintf(aux, "%s", outputstr);
+ pfree(outputstr);
+ }
+ else if (IsA(expr, OpExpr))
+ {
+ OpExpr *e = (OpExpr *) expr;
+ char *opname;
+
+ opname = get_opname(e->opno);
+ if (list_length(e->args) > 1)
+ {
+ aux += print_expr_str(aux, get_leftop((Expr *) e), rtable);
+ aux = aux + sprintf(aux, " %s ",
+ ((opname != NULL) ? opname : "(invalid operator)"));
+ aux += print_expr_str(aux, get_rightop((Expr *) e), rtable);
+ }
+ else
+ {
+ /* we print prefix and postfix ops the same... */
+ aux = aux + sprintf(aux, "%s ",
+ ((opname != NULL) ? opname : "(invalid operator)"));
+ aux += print_expr_str(aux, get_leftop((Expr *) e), rtable);
+ }
+ }
+ else if (IsA(expr, FuncExpr))
+ {
+ FuncExpr *e = (FuncExpr *) expr;
+ char *funcname;
+ ListCell *l;
+
+ funcname = get_func_name(e->funcid);
+ aux = aux + sprintf(aux, "%s(",
+ ((funcname != NULL) ? funcname : "(invalid function)"));
+ foreach(l, e->args)
+ {
+ aux += print_expr_str(aux, lfirst(l), rtable);
+ if (lnext(l))
+ aux= aux + sprintf(aux, ",");
+ }
+ aux = aux + sprintf(aux, ")");
+ }
+ else
+ aux = aux + sprintf(aux, "unknown expr");
+
+ return aux - output;
+}
+
/*
* print_pathkeys -
* pathkeys list of PathKeys