Re: graph representation of data structures in optimizer - Mailing list pgsql-hackers
From | Adriano Lange |
---|---|
Subject | Re: graph representation of data structures in optimizer |
Date | |
Msg-id | 49A1DA18.5040803@c3sl.ufpr.br Whole thread Raw |
In response to | graph representation of data structures in optimizer (Adriano Lange <adriano@c3sl.ufpr.br>) |
Responses |
Re: graph representation of data structures in optimizer
(ITAGAKI Takahiro <itagaki.takahiro@oss.ntt.co.jp>)
|
List | pgsql-hackers |
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
pgsql-hackers by date: