* pl_funcs.c - Misc functions for the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.54 2006/08/14 21:14:41 tgl Exp $
+ * src/pl/plpgsql/src/pl_funcs.c
*
*-------------------------------------------------------------------------
*/
#include "plpgsql.h"
-#include "pl.tab.h"
-#include <ctype.h>
-
-#include "parser/scansup.h"
+#include "utils/memutils.h"
/* ----------
- * Local variables for the namestack handling
+ * Local variables for namespace handling
+ *
+ * The namespace structure actually forms a tree, of which only one linear
+ * list or "chain" (from the youngest item to the root) is accessible from
+ * any one plpgsql statement. During initial parsing of a function, ns_top
+ * points to the youngest item accessible from the block currently being
+ * parsed. We store the entire tree, however, since at runtime we will need
+ * to access the chain that's relevant to any one statement.
+ *
+ * Block boundaries in the namespace chain are marked by PLPGSQL_NSTYPE_LABEL
+ * items.
* ----------
*/
-static PLpgSQL_ns *ns_current = NULL;
-static bool ns_localmode = false;
+static PLpgSQL_nsitem *ns_top = NULL;
/* ----------
- * plpgsql_dstring_init Dynamic string initialization
+ * plpgsql_ns_init Initialize namespace processing for a new function
* ----------
*/
void
-plpgsql_dstring_init(PLpgSQL_dstring *ds)
+plpgsql_ns_init(void)
{
- ds->value = palloc(ds->alloc = 512);
- ds->used = 1;
- ds->value[0] = '\0';
+ ns_top = NULL;
}
/* ----------
- * plpgsql_dstring_free Dynamic string destruction
+ * plpgsql_ns_push Create a new namespace level
* ----------
*/
void
-plpgsql_dstring_free(PLpgSQL_dstring *ds)
+plpgsql_ns_push(const char *label)
{
- pfree(ds->value);
+ if (label == NULL)
+ label = "";
+ plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, label);
}
-static void
-plpgsql_dstring_expand(PLpgSQL_dstring *ds, int needed)
-{
- /* Don't allow truncating the string */
- Assert(needed > ds->alloc);
- Assert(ds->used <= ds->alloc);
-
- /* Might have to double more than once, if needed is large */
- do
- {
- ds->alloc *= 2;
- } while (needed > ds->alloc);
- ds->value = repalloc(ds->value, ds->alloc);
-}
/* ----------
- * plpgsql_dstring_append Dynamic string extending
+ * plpgsql_ns_pop Pop entries back to (and including) the last label
* ----------
*/
void
-plpgsql_dstring_append(PLpgSQL_dstring *ds, const char *str)
+plpgsql_ns_pop(void)
{
- int len = strlen(str);
- int needed = ds->used + len;
+ Assert(ns_top != NULL);
+ while (ns_top->itemtype != PLPGSQL_NSTYPE_LABEL)
+ ns_top = ns_top->prev;
+ ns_top = ns_top->prev;
+}
- if (needed > ds->alloc)
- plpgsql_dstring_expand(ds, needed);
- memcpy(&(ds->value[ds->used - 1]), str, len);
- ds->used += len;
- ds->value[ds->used - 1] = '\0';
+/* ----------
+ * plpgsql_ns_top Fetch the current namespace chain end
+ * ----------
+ */
+PLpgSQL_nsitem *
+plpgsql_ns_top(void)
+{
+ return ns_top;
}
+
/* ----------
- * plpgsql_dstring_append_char Append a single character
- * to a dynamic string
+ * plpgsql_ns_additem Add an item to the current namespace chain
* ----------
*/
void
-plpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c)
+plpgsql_ns_additem(int itemtype, int itemno, const char *name)
{
- if (ds->used == ds->alloc)
- plpgsql_dstring_expand(ds, ds->used + 1);
+ PLpgSQL_nsitem *nse;
+
+ Assert(name != NULL);
+ /* first item added must be a label */
+ Assert(ns_top != NULL || itemtype == PLPGSQL_NSTYPE_LABEL);
- ds->value[ds->used - 1] = c;
- ds->value[ds->used] = '\0';
- ds->used++;
+ nse = palloc(sizeof(PLpgSQL_nsitem) + strlen(name));
+ nse->itemtype = itemtype;
+ nse->itemno = itemno;
+ nse->prev = ns_top;
+ strcpy(nse->name, name);
+ ns_top = nse;
}
/* ----------
- * plpgsql_dstring_get Dynamic string get value
+ * plpgsql_ns_lookup Lookup an identifier in the given namespace chain
+ *
+ * Note that this only searches for variables, not labels.
+ *
+ * If localmode is TRUE, only the topmost block level is searched.
+ *
+ * name1 must be non-NULL. Pass NULL for name2 and/or name3 if parsing a name
+ * with fewer than three components.
+ *
+ * If names_used isn't NULL, *names_used receives the number of names
+ * matched: 0 if no match, 1 if name1 matched an unqualified variable name,
+ * 2 if name1 and name2 matched a block label + variable name.
+ *
+ * Note that name3 is never directly matched to anything. However, if it
+ * isn't NULL, we will disregard qualified matches to scalar variables.
+ * Similarly, if name2 isn't NULL, we disregard unqualified matches to
+ * scalar variables.
* ----------
*/
-char *
-plpgsql_dstring_get(PLpgSQL_dstring *ds)
+PLpgSQL_nsitem *
+plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
+ const char *name1, const char *name2, const char *name3,
+ int *names_used)
{
- return ds->value;
+ /* Outer loop iterates once per block level in the namespace chain */
+ while (ns_cur != NULL)
+ {
+ PLpgSQL_nsitem *nsitem;
+
+ /* Check this level for unqualified match to variable name */
+ for (nsitem = ns_cur;
+ nsitem->itemtype != PLPGSQL_NSTYPE_LABEL;
+ nsitem = nsitem->prev)
+ {
+ if (strcmp(nsitem->name, name1) == 0)
+ {
+ if (name2 == NULL ||
+ nsitem->itemtype != PLPGSQL_NSTYPE_VAR)
+ {
+ if (names_used)
+ *names_used = 1;
+ return nsitem;
+ }
+ }
+ }
+
+ /* Check this level for qualified match to variable name */
+ if (name2 != NULL &&
+ strcmp(nsitem->name, name1) == 0)
+ {
+ for (nsitem = ns_cur;
+ nsitem->itemtype != PLPGSQL_NSTYPE_LABEL;
+ nsitem = nsitem->prev)
+ {
+ if (strcmp(nsitem->name, name2) == 0)
+ {
+ if (name3 == NULL ||
+ nsitem->itemtype != PLPGSQL_NSTYPE_VAR)
+ {
+ if (names_used)
+ *names_used = 2;
+ return nsitem;
+ }
+ }
+ }
+ }
+
+ if (localmode)
+ break; /* do not look into upper levels */
+
+ ns_cur = nsitem->prev;
+ }
+
+ /* This is just to suppress possibly-uninitialized-variable warnings */
+ if (names_used)
+ *names_used = 0;
+ return NULL; /* No match found */
}
/* ----------
- * plpgsql_ns_init Initialize the namestack
+ * plpgsql_ns_lookup_label Lookup a label in the given namespace chain
* ----------
*/
-void
-plpgsql_ns_init(void)
+PLpgSQL_nsitem *
+plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, const char *name)
{
- ns_current = NULL;
- ns_localmode = false;
+ while (ns_cur != NULL)
+ {
+ if (ns_cur->itemtype == PLPGSQL_NSTYPE_LABEL &&
+ strcmp(ns_cur->name, name) == 0)
+ return ns_cur;
+ ns_cur = ns_cur->prev;
+ }
+
+ return NULL; /* label not found */
}
-/* ----------
- * plpgsql_ns_setlocal Tell plpgsql_ns_lookup to or to
- * not look into the current level
- * only.
- * ----------
+/*
+ * Statement type as a string, for use in error messages etc.
*/
-bool
-plpgsql_ns_setlocal(bool flag)
+const char *
+plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
{
- bool oldstate;
+ switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
+ {
+ case PLPGSQL_STMT_BLOCK:
+ return _("statement block");
+ case PLPGSQL_STMT_ASSIGN:
+ return _("assignment");
+ case PLPGSQL_STMT_IF:
+ return "IF";
+ case PLPGSQL_STMT_CASE:
+ return "CASE";
+ case PLPGSQL_STMT_LOOP:
+ return "LOOP";
+ case PLPGSQL_STMT_WHILE:
+ return "WHILE";
+ case PLPGSQL_STMT_FORI:
+ return _("FOR with integer loop variable");
+ case PLPGSQL_STMT_FORS:
+ return _("FOR over SELECT rows");
+ case PLPGSQL_STMT_FORC:
+ return _("FOR over cursor");
+ case PLPGSQL_STMT_FOREACH_A:
+ return _("FOREACH over array");
+ case PLPGSQL_STMT_EXIT:
+ return "EXIT";
+ case PLPGSQL_STMT_RETURN:
+ return "RETURN";
+ case PLPGSQL_STMT_RETURN_NEXT:
+ return "RETURN NEXT";
+ case PLPGSQL_STMT_RETURN_QUERY:
+ return "RETURN QUERY";
+ case PLPGSQL_STMT_RAISE:
+ return "RAISE";
+ case PLPGSQL_STMT_EXECSQL:
+ return _("SQL statement");
+ case PLPGSQL_STMT_DYNEXECUTE:
+ return _("EXECUTE statement");
+ case PLPGSQL_STMT_DYNFORS:
+ return _("FOR over EXECUTE statement");
+ case PLPGSQL_STMT_GETDIAG:
+ return "GET DIAGNOSTICS";
+ case PLPGSQL_STMT_OPEN:
+ return "OPEN";
+ case PLPGSQL_STMT_FETCH:
+ return "FETCH";
+ case PLPGSQL_STMT_CLOSE:
+ return "CLOSE";
+ case PLPGSQL_STMT_PERFORM:
+ return "PERFORM";
+ }
- oldstate = ns_localmode;
- ns_localmode = flag;
- return oldstate;
+ return "unknown";
}
-
-/* ----------
- * plpgsql_ns_push Enter a new namestack level
- * ----------
+/*
+ * GET DIAGNOSTICS item name as a string, for use in error messages etc.
*/
-void
-plpgsql_ns_push(char *label)
+const char *
+plpgsql_getdiag_kindname(int kind)
{
- PLpgSQL_ns *new;
+ switch (kind)
+ {
+ case PLPGSQL_GETDIAG_ROW_COUNT:
+ return "ROW_COUNT";
+ case PLPGSQL_GETDIAG_RESULT_OID:
+ return "RESULT_OID";
+ case PLPGSQL_GETDIAG_CONTEXT:
+ return "PG_CONTEXT";
+ case PLPGSQL_GETDIAG_ERROR_CONTEXT:
+ return "PG_EXCEPTION_CONTEXT";
+ case PLPGSQL_GETDIAG_ERROR_DETAIL:
+ return "PG_EXCEPTION_DETAIL";
+ case PLPGSQL_GETDIAG_ERROR_HINT:
+ return "PG_EXCEPTION_HINT";
+ case PLPGSQL_GETDIAG_RETURNED_SQLSTATE:
+ return "RETURNED_SQLSTATE";
+ case PLPGSQL_GETDIAG_COLUMN_NAME:
+ return "COLUMN_NAME";
+ case PLPGSQL_GETDIAG_CONSTRAINT_NAME:
+ return "CONSTRAINT_NAME";
+ case PLPGSQL_GETDIAG_DATATYPE_NAME:
+ return "PG_DATATYPE_NAME";
+ case PLPGSQL_GETDIAG_MESSAGE_TEXT:
+ return "MESSAGE_TEXT";
+ case PLPGSQL_GETDIAG_TABLE_NAME:
+ return "TABLE_NAME";
+ case PLPGSQL_GETDIAG_SCHEMA_NAME:
+ return "SCHEMA_NAME";
+ }
- if (label == NULL)
- label = "";
+ return "unknown";
+}
- new = palloc(sizeof(PLpgSQL_ns));
- memset(new, 0, sizeof(PLpgSQL_ns));
- new->upper = ns_current;
- ns_current = new;
- plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, label);
+/**********************************************************************
+ * Release memory when a PL/pgSQL function is no longer needed
+ *
+ * The code for recursing through the function tree is really only
+ * needed to locate PLpgSQL_expr nodes, which may contain references
+ * to saved SPI Plans that must be freed. The function tree itself,
+ * along with subsidiary data, is freed in one swoop by freeing the
+ * function's permanent memory context.
+ **********************************************************************/
+static void free_stmt(PLpgSQL_stmt *stmt);
+static void free_block(PLpgSQL_stmt_block *block);
+static void free_assign(PLpgSQL_stmt_assign *stmt);
+static void free_if(PLpgSQL_stmt_if *stmt);
+static void free_case(PLpgSQL_stmt_case *stmt);
+static void free_loop(PLpgSQL_stmt_loop *stmt);
+static void free_while(PLpgSQL_stmt_while *stmt);
+static void free_fori(PLpgSQL_stmt_fori *stmt);
+static void free_fors(PLpgSQL_stmt_fors *stmt);
+static void free_forc(PLpgSQL_stmt_forc *stmt);
+static void free_foreach_a(PLpgSQL_stmt_foreach_a *stmt);
+static void free_exit(PLpgSQL_stmt_exit *stmt);
+static void free_return(PLpgSQL_stmt_return *stmt);
+static void free_return_next(PLpgSQL_stmt_return_next *stmt);
+static void free_return_query(PLpgSQL_stmt_return_query *stmt);
+static void free_raise(PLpgSQL_stmt_raise *stmt);
+static void free_execsql(PLpgSQL_stmt_execsql *stmt);
+static void free_dynexecute(PLpgSQL_stmt_dynexecute *stmt);
+static void free_dynfors(PLpgSQL_stmt_dynfors *stmt);
+static void free_getdiag(PLpgSQL_stmt_getdiag *stmt);
+static void free_open(PLpgSQL_stmt_open *stmt);
+static void free_fetch(PLpgSQL_stmt_fetch *stmt);
+static void free_close(PLpgSQL_stmt_close *stmt);
+static void free_perform(PLpgSQL_stmt_perform *stmt);
+static void free_expr(PLpgSQL_expr *expr);
+
+
+static void
+free_stmt(PLpgSQL_stmt *stmt)
+{
+ switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
+ {
+ case PLPGSQL_STMT_BLOCK:
+ free_block((PLpgSQL_stmt_block *) stmt);
+ break;
+ case PLPGSQL_STMT_ASSIGN:
+ free_assign((PLpgSQL_stmt_assign *) stmt);
+ break;
+ case PLPGSQL_STMT_IF:
+ free_if((PLpgSQL_stmt_if *) stmt);
+ break;
+ case PLPGSQL_STMT_CASE:
+ free_case((PLpgSQL_stmt_case *) stmt);
+ break;
+ case PLPGSQL_STMT_LOOP:
+ free_loop((PLpgSQL_stmt_loop *) stmt);
+ break;
+ case PLPGSQL_STMT_WHILE:
+ free_while((PLpgSQL_stmt_while *) stmt);
+ break;
+ case PLPGSQL_STMT_FORI:
+ free_fori((PLpgSQL_stmt_fori *) stmt);
+ break;
+ case PLPGSQL_STMT_FORS:
+ free_fors((PLpgSQL_stmt_fors *) stmt);
+ break;
+ case PLPGSQL_STMT_FORC:
+ free_forc((PLpgSQL_stmt_forc *) stmt);
+ break;
+ case PLPGSQL_STMT_FOREACH_A:
+ free_foreach_a((PLpgSQL_stmt_foreach_a *) stmt);
+ break;
+ case PLPGSQL_STMT_EXIT:
+ free_exit((PLpgSQL_stmt_exit *) stmt);
+ break;
+ case PLPGSQL_STMT_RETURN:
+ free_return((PLpgSQL_stmt_return *) stmt);
+ break;
+ case PLPGSQL_STMT_RETURN_NEXT:
+ free_return_next((PLpgSQL_stmt_return_next *) stmt);
+ break;
+ case PLPGSQL_STMT_RETURN_QUERY:
+ free_return_query((PLpgSQL_stmt_return_query *) stmt);
+ break;
+ case PLPGSQL_STMT_RAISE:
+ free_raise((PLpgSQL_stmt_raise *) stmt);
+ break;
+ case PLPGSQL_STMT_EXECSQL:
+ free_execsql((PLpgSQL_stmt_execsql *) stmt);
+ break;
+ case PLPGSQL_STMT_DYNEXECUTE:
+ free_dynexecute((PLpgSQL_stmt_dynexecute *) stmt);
+ break;
+ case PLPGSQL_STMT_DYNFORS:
+ free_dynfors((PLpgSQL_stmt_dynfors *) stmt);
+ break;
+ case PLPGSQL_STMT_GETDIAG:
+ free_getdiag((PLpgSQL_stmt_getdiag *) stmt);
+ break;
+ case PLPGSQL_STMT_OPEN:
+ free_open((PLpgSQL_stmt_open *) stmt);
+ break;
+ case PLPGSQL_STMT_FETCH:
+ free_fetch((PLpgSQL_stmt_fetch *) stmt);
+ break;
+ case PLPGSQL_STMT_CLOSE:
+ free_close((PLpgSQL_stmt_close *) stmt);
+ break;
+ case PLPGSQL_STMT_PERFORM:
+ free_perform((PLpgSQL_stmt_perform *) stmt);
+ break;
+ default:
+ elog(ERROR, "unrecognized cmd_type: %d", stmt->cmd_type);
+ break;
+ }
}
+static void
+free_stmts(List *stmts)
+{
+ ListCell *s;
-/* ----------
- * plpgsql_ns_pop Return to the previous level
- * ----------
- */
-void
-plpgsql_ns_pop(void)
+ foreach(s, stmts)
+ {
+ free_stmt((PLpgSQL_stmt *) lfirst(s));
+ }
+}
+
+static void
+free_block(PLpgSQL_stmt_block *block)
{
- int i;
- PLpgSQL_ns *old;
+ free_stmts(block->body);
+ if (block->exceptions)
+ {
+ ListCell *e;
- old = ns_current;
- ns_current = old->upper;
+ foreach(e, block->exceptions->exc_list)
+ {
+ PLpgSQL_exception *exc = (PLpgSQL_exception *) lfirst(e);
- for (i = 0; i < old->items_used; i++)
- pfree(old->items[i]);
- pfree(old->items);
- pfree(old);
+ free_stmts(exc->action);
+ }
+ }
}
+static void
+free_assign(PLpgSQL_stmt_assign *stmt)
+{
+ free_expr(stmt->expr);
+}
-/* ----------
- * plpgsql_ns_additem Add an item to the current
- * namestack level
- * ----------
- */
-void
-plpgsql_ns_additem(int itemtype, int itemno, const char *name)
+static void
+free_if(PLpgSQL_stmt_if *stmt)
{
- PLpgSQL_ns *ns = ns_current;
- PLpgSQL_nsitem *nse;
+ ListCell *l;
- Assert(name != NULL);
+ free_expr(stmt->cond);
+ free_stmts(stmt->then_body);
+ foreach(l, stmt->elsif_list)
+ {
+ PLpgSQL_if_elsif *elif = (PLpgSQL_if_elsif *) lfirst(l);
+
+ free_expr(elif->cond);
+ free_stmts(elif->stmts);
+ }
+ free_stmts(stmt->else_body);
+}
- if (ns->items_used == ns->items_alloc)
+static void
+free_case(PLpgSQL_stmt_case *stmt)
+{
+ ListCell *l;
+
+ free_expr(stmt->t_expr);
+ foreach(l, stmt->case_when_list)
{
- if (ns->items_alloc == 0)
- {
- ns->items_alloc = 32;
- ns->items = palloc(sizeof(PLpgSQL_nsitem *) * ns->items_alloc);
- }
- else
- {
- ns->items_alloc *= 2;
- ns->items = repalloc(ns->items,
- sizeof(PLpgSQL_nsitem *) * ns->items_alloc);
- }
+ PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
+
+ free_expr(cwt->expr);
+ free_stmts(cwt->stmts);
}
+ free_stmts(stmt->else_stmts);
+}
- nse = palloc(sizeof(PLpgSQL_nsitem) + strlen(name));
- nse->itemtype = itemtype;
- nse->itemno = itemno;
- strcpy(nse->name, name);
- ns->items[ns->items_used++] = nse;
+static void
+free_loop(PLpgSQL_stmt_loop *stmt)
+{
+ free_stmts(stmt->body);
}
+static void
+free_while(PLpgSQL_stmt_while *stmt)
+{
+ free_expr(stmt->cond);
+ free_stmts(stmt->body);
+}
-/* ----------
- * plpgsql_ns_lookup Lookup for a word in the namestack
- * ----------
- */
-PLpgSQL_nsitem *
-plpgsql_ns_lookup(char *name, char *label)
+static void
+free_fori(PLpgSQL_stmt_fori *stmt)
{
- PLpgSQL_ns *ns;
- int i;
+ free_expr(stmt->lower);
+ free_expr(stmt->upper);
+ free_expr(stmt->step);
+ free_stmts(stmt->body);
+}
- /*
- * If a label is specified, lookup only in that
- */
- if (label != NULL)
+static void
+free_fors(PLpgSQL_stmt_fors *stmt)
+{
+ free_stmts(stmt->body);
+ free_expr(stmt->query);
+}
+
+static void
+free_forc(PLpgSQL_stmt_forc *stmt)
+{
+ free_stmts(stmt->body);
+ free_expr(stmt->argquery);
+}
+
+static void
+free_foreach_a(PLpgSQL_stmt_foreach_a *stmt)
+{
+ free_expr(stmt->expr);
+ free_stmts(stmt->body);
+}
+
+static void
+free_open(PLpgSQL_stmt_open *stmt)
+{
+ ListCell *lc;
+
+ free_expr(stmt->argquery);
+ free_expr(stmt->query);
+ free_expr(stmt->dynquery);
+ foreach(lc, stmt->params)
{
- for (ns = ns_current; ns != NULL; ns = ns->upper)
- {
- if (!strcmp(ns->items[0]->name, label))
- {
- for (i = 1; i < ns->items_used; i++)
- {
- if (!strcmp(ns->items[i]->name, name))
- return ns->items[i];
- }
- return NULL; /* name not found in specified label */
- }
- }
- return NULL; /* label not found */
+ free_expr((PLpgSQL_expr *) lfirst(lc));
}
+}
- /*
- * No label given, lookup for visible labels ignoring localmode
- */
- for (ns = ns_current; ns != NULL; ns = ns->upper)
+static void
+free_fetch(PLpgSQL_stmt_fetch *stmt)
+{
+ free_expr(stmt->expr);
+}
+
+static void
+free_close(PLpgSQL_stmt_close *stmt)
+{
+}
+
+static void
+free_perform(PLpgSQL_stmt_perform *stmt)
+{
+ free_expr(stmt->expr);
+}
+
+static void
+free_exit(PLpgSQL_stmt_exit *stmt)
+{
+ free_expr(stmt->cond);
+}
+
+static void
+free_return(PLpgSQL_stmt_return *stmt)
+{
+ free_expr(stmt->expr);
+}
+
+static void
+free_return_next(PLpgSQL_stmt_return_next *stmt)
+{
+ free_expr(stmt->expr);
+}
+
+static void
+free_return_query(PLpgSQL_stmt_return_query *stmt)
+{
+ ListCell *lc;
+
+ free_expr(stmt->query);
+ free_expr(stmt->dynquery);
+ foreach(lc, stmt->params)
{
- if (!strcmp(ns->items[0]->name, name))
- return ns->items[0];
+ free_expr((PLpgSQL_expr *) lfirst(lc));
}
+}
- /*
- * Finally lookup name in the namestack
- */
- for (ns = ns_current; ns != NULL; ns = ns->upper)
+static void
+free_raise(PLpgSQL_stmt_raise *stmt)
+{
+ ListCell *lc;
+
+ foreach(lc, stmt->params)
{
- for (i = 1; i < ns->items_used; i++)
- {
- if (!strcmp(ns->items[i]->name, name))
- return ns->items[i];
- }
- if (ns_localmode)
- return NULL; /* name not found in current namespace */
+ free_expr((PLpgSQL_expr *) lfirst(lc));
+ }
+ foreach(lc, stmt->options)
+ {
+ PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(lc);
+
+ free_expr(opt->expr);
+ }
+}
+
+static void
+free_execsql(PLpgSQL_stmt_execsql *stmt)
+{
+ free_expr(stmt->sqlstmt);
+}
+
+static void
+free_dynexecute(PLpgSQL_stmt_dynexecute *stmt)
+{
+ ListCell *lc;
+
+ free_expr(stmt->query);
+ foreach(lc, stmt->params)
+ {
+ free_expr((PLpgSQL_expr *) lfirst(lc));
}
-
- return NULL;
}
-
-/* ----------
- * plpgsql_ns_rename Rename a namespace entry
- * ----------
- */
-void
-plpgsql_ns_rename(char *oldname, char *newname)
+static void
+free_dynfors(PLpgSQL_stmt_dynfors *stmt)
{
- PLpgSQL_ns *ns;
- PLpgSQL_nsitem *newitem;
- int i;
+ ListCell *lc;
- /*
- * Lookup name in the namestack; do the lookup in the current namespace
- * only.
- */
- for (ns = ns_current; ns != NULL; ns = ns->upper)
+ free_stmts(stmt->body);
+ free_expr(stmt->query);
+ foreach(lc, stmt->params)
{
- for (i = 1; i < ns->items_used; i++)
- {
- if (!strcmp(ns->items[i]->name, oldname))
- {
- newitem = palloc(sizeof(PLpgSQL_nsitem) + strlen(newname));
- newitem->itemtype = ns->items[i]->itemtype;
- newitem->itemno = ns->items[i]->itemno;
- strcpy(newitem->name, newname);
-
- pfree(oldname);
- pfree(newname);
-
- pfree(ns->items[i]);
- ns->items[i] = newitem;
- return;
- }
- }
+ free_expr((PLpgSQL_expr *) lfirst(lc));
}
+}
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("there is no variable \"%s\" in the current block",
- oldname)));
+static void
+free_getdiag(PLpgSQL_stmt_getdiag *stmt)
+{
}
+static void
+free_expr(PLpgSQL_expr *expr)
+{
+ if (expr && expr->plan)
+ {
+ SPI_freeplan(expr->plan);
+ expr->plan = NULL;
+ }
+}
-/* ----------
- * plpgsql_convert_ident
- *
- * Convert a possibly-qualified identifier to internal form: handle
- * double quotes, translate to lower case where not inside quotes,
- * truncate to NAMEDATALEN.
- *
- * There may be several identifiers separated by dots and optional
- * whitespace. Each one is converted to a separate palloc'd string.
- * The caller passes the expected number of identifiers, as well as
- * a char* array to hold them. It is an error if we find the wrong
- * number of identifiers (cf grammar processing of fori_varname).
- *
- * NOTE: the input string has already been accepted by the flex lexer,
- * so we don't need a heckuva lot of error checking here.
- * ----------
- */
void
-plpgsql_convert_ident(const char *s, char **output, int numidents)
+plpgsql_free_function_memory(PLpgSQL_function *func)
{
- const char *sstart = s;
- int identctr = 0;
+ int i;
- /* Outer loop over identifiers */
- while (*s)
- {
- char *curident;
- char *cp;
+ /* Better not call this on an in-use function */
+ Assert(func->use_count == 0);
- /* Process current identifier */
+ /* Release plans associated with variable declarations */
+ for (i = 0; i < func->ndatums; i++)
+ {
+ PLpgSQL_datum *d = func->datums[i];
- if (*s == '"')
+ switch (d->dtype)
{
- /* Quoted identifier: copy, collapsing out doubled quotes */
-
- curident = palloc(strlen(s) + 1); /* surely enough room */
- cp = curident;
- s++;
- while (*s)
- {
- if (*s == '"')
+ case PLPGSQL_DTYPE_VAR:
{
- if (s[1] != '"')
- break;
- s++;
- }
- *cp++ = *s++;
- }
- if (*s != '"') /* should not happen if lexer checked */
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unterminated \" in name: %s", sstart)));
- s++;
- *cp = '\0';
- /* Truncate to NAMEDATALEN */
- truncate_identifier(curident, cp - curident, false);
- }
- else
- {
- /* Normal identifier: extends till dot or whitespace */
- const char *thisstart = s;
-
- while (*s && *s != '.' && !isspace((unsigned char) *s))
- s++;
- /* Downcase and truncate to NAMEDATALEN */
- curident = downcase_truncate_identifier(thisstart, s - thisstart,
- false);
- }
+ PLpgSQL_var *var = (PLpgSQL_var *) d;
- /* Pass ident to caller */
- if (identctr < numidents)
- output[identctr++] = curident;
- else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("qualified identifier cannot be used here: %s",
- sstart)));
-
- /* If not done, skip whitespace, dot, whitespace */
- if (*s)
- {
- while (*s && isspace((unsigned char) *s))
- s++;
- if (*s++ != '.')
- elog(ERROR, "expected dot between identifiers: %s", sstart);
- while (*s && isspace((unsigned char) *s))
- s++;
- if (*s == '\0')
- elog(ERROR, "expected another identifier: %s", sstart);
+ free_expr(var->default_val);
+ free_expr(var->cursor_explicit_expr);
+ }
+ break;
+ case PLPGSQL_DTYPE_ROW:
+ break;
+ case PLPGSQL_DTYPE_REC:
+ break;
+ case PLPGSQL_DTYPE_RECFIELD:
+ break;
+ case PLPGSQL_DTYPE_ARRAYELEM:
+ free_expr(((PLpgSQL_arrayelem *) d)->subscript);
+ break;
+ default:
+ elog(ERROR, "unrecognized data type: %d", d->dtype);
}
}
+ func->ndatums = 0;
- if (identctr != numidents)
- elog(ERROR, "improperly qualified identifier: %s",
- sstart);
-}
-
-
-/*
- * Statement type as a string, for use in error messages etc.
- */
-const char *
-plpgsql_stmt_typename(PLpgSQL_stmt *stmt)
-{
- switch (stmt->cmd_type)
- {
- case PLPGSQL_STMT_BLOCK:
- return "block variables initialization";
- case PLPGSQL_STMT_ASSIGN:
- return "assignment";
- case PLPGSQL_STMT_IF:
- return "if";
- case PLPGSQL_STMT_LOOP:
- return "loop";
- case PLPGSQL_STMT_WHILE:
- return "while";
- case PLPGSQL_STMT_FORI:
- return "for with integer loopvar";
- case PLPGSQL_STMT_FORS:
- return "for over select rows";
- case PLPGSQL_STMT_EXIT:
- return "exit";
- case PLPGSQL_STMT_RETURN:
- return "return";
- case PLPGSQL_STMT_RETURN_NEXT:
- return "return next";
- case PLPGSQL_STMT_RAISE:
- return "raise";
- case PLPGSQL_STMT_EXECSQL:
- return "SQL statement";
- case PLPGSQL_STMT_DYNEXECUTE:
- return "execute statement";
- case PLPGSQL_STMT_DYNFORS:
- return "for over execute statement";
- case PLPGSQL_STMT_GETDIAG:
- return "get diagnostics";
- case PLPGSQL_STMT_OPEN:
- return "open";
- case PLPGSQL_STMT_FETCH:
- return "fetch";
- case PLPGSQL_STMT_CLOSE:
- return "close";
- case PLPGSQL_STMT_PERFORM:
- return "perform";
- }
+ /* Release plans in statement tree */
+ if (func->action)
+ free_block(func->action);
+ func->action = NULL;
- return "unknown";
+ /*
+ * And finally, release all memory except the PLpgSQL_function struct
+ * itself (which has to be kept around because there may be multiple
+ * fn_extra pointers to it).
+ */
+ if (func->fn_cxt)
+ MemoryContextDelete(func->fn_cxt);
+ func->fn_cxt = NULL;
}
**********************************************************************/
static int dump_indent;
-static void dump_ind();
+static void dump_ind(void);
static void dump_stmt(PLpgSQL_stmt *stmt);
static void dump_block(PLpgSQL_stmt_block *block);
static void dump_assign(PLpgSQL_stmt_assign *stmt);
static void dump_if(PLpgSQL_stmt_if *stmt);
+static void dump_case(PLpgSQL_stmt_case *stmt);
static void dump_loop(PLpgSQL_stmt_loop *stmt);
static void dump_while(PLpgSQL_stmt_while *stmt);
static void dump_fori(PLpgSQL_stmt_fori *stmt);
static void dump_fors(PLpgSQL_stmt_fors *stmt);
+static void dump_forc(PLpgSQL_stmt_forc *stmt);
+static void dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt);
static void dump_exit(PLpgSQL_stmt_exit *stmt);
static void dump_return(PLpgSQL_stmt_return *stmt);
static void dump_return_next(PLpgSQL_stmt_return_next *stmt);
+static void dump_return_query(PLpgSQL_stmt_return_query *stmt);
static void dump_raise(PLpgSQL_stmt_raise *stmt);
static void dump_execsql(PLpgSQL_stmt_execsql *stmt);
static void dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt);
static void dump_getdiag(PLpgSQL_stmt_getdiag *stmt);
static void dump_open(PLpgSQL_stmt_open *stmt);
static void dump_fetch(PLpgSQL_stmt_fetch *stmt);
+static void dump_cursor_direction(PLpgSQL_stmt_fetch *stmt);
static void dump_close(PLpgSQL_stmt_close *stmt);
static void dump_perform(PLpgSQL_stmt_perform *stmt);
static void dump_expr(PLpgSQL_expr *expr);
dump_stmt(PLpgSQL_stmt *stmt)
{
printf("%3d:", stmt->lineno);
- switch (stmt->cmd_type)
+ switch ((enum PLpgSQL_stmt_types) stmt->cmd_type)
{
case PLPGSQL_STMT_BLOCK:
dump_block((PLpgSQL_stmt_block *) stmt);
case PLPGSQL_STMT_IF:
dump_if((PLpgSQL_stmt_if *) stmt);
break;
+ case PLPGSQL_STMT_CASE:
+ dump_case((PLpgSQL_stmt_case *) stmt);
+ break;
case PLPGSQL_STMT_LOOP:
dump_loop((PLpgSQL_stmt_loop *) stmt);
break;
case PLPGSQL_STMT_FORS:
dump_fors((PLpgSQL_stmt_fors *) stmt);
break;
+ case PLPGSQL_STMT_FORC:
+ dump_forc((PLpgSQL_stmt_forc *) stmt);
+ break;
+ case PLPGSQL_STMT_FOREACH_A:
+ dump_foreach_a((PLpgSQL_stmt_foreach_a *) stmt);
+ break;
case PLPGSQL_STMT_EXIT:
dump_exit((PLpgSQL_stmt_exit *) stmt);
break;
case PLPGSQL_STMT_RETURN_NEXT:
dump_return_next((PLpgSQL_stmt_return_next *) stmt);
break;
+ case PLPGSQL_STMT_RETURN_QUERY:
+ dump_return_query((PLpgSQL_stmt_return_query *) stmt);
+ break;
case PLPGSQL_STMT_RAISE:
dump_raise((PLpgSQL_stmt_raise *) stmt);
break;
static void
dump_if(PLpgSQL_stmt_if *stmt)
{
+ ListCell *l;
+
dump_ind();
printf("IF ");
dump_expr(stmt->cond);
printf(" THEN\n");
+ dump_stmts(stmt->then_body);
+ foreach(l, stmt->elsif_list)
+ {
+ PLpgSQL_if_elsif *elif = (PLpgSQL_if_elsif *) lfirst(l);
- dump_stmts(stmt->true_body);
-
- if (stmt->false_body != NIL)
+ dump_ind();
+ printf(" ELSIF ");
+ dump_expr(elif->cond);
+ printf(" THEN\n");
+ dump_stmts(elif->stmts);
+ }
+ if (stmt->else_body != NIL)
{
dump_ind();
printf(" ELSE\n");
- dump_stmts(stmt->false_body);
+ dump_stmts(stmt->else_body);
}
-
dump_ind();
printf(" ENDIF\n");
}
+static void
+dump_case(PLpgSQL_stmt_case *stmt)
+{
+ ListCell *l;
+
+ dump_ind();
+ printf("CASE %d ", stmt->t_varno);
+ if (stmt->t_expr)
+ dump_expr(stmt->t_expr);
+ printf("\n");
+ dump_indent += 6;
+ foreach(l, stmt->case_when_list)
+ {
+ PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
+
+ dump_ind();
+ printf("WHEN ");
+ dump_expr(cwt->expr);
+ printf("\n");
+ dump_ind();
+ printf("THEN\n");
+ dump_indent += 2;
+ dump_stmts(cwt->stmts);
+ dump_indent -= 2;
+ }
+ if (stmt->have_else)
+ {
+ dump_ind();
+ printf("ELSE\n");
+ dump_indent += 2;
+ dump_stmts(stmt->else_stmts);
+ dump_indent -= 2;
+ }
+ dump_indent -= 6;
+ dump_ind();
+ printf(" ENDCASE\n");
+}
+
static void
dump_loop(PLpgSQL_stmt_loop *stmt)
{
printf(" upper = ");
dump_expr(stmt->upper);
printf("\n");
- dump_ind();
- printf(" by = ");
- dump_expr(stmt->by);
- printf("\n");
+ if (stmt->step)
+ {
+ dump_ind();
+ printf(" step = ");
+ dump_expr(stmt->step);
+ printf("\n");
+ }
dump_indent -= 2;
dump_stmts(stmt->body);
printf(" ENDFORS\n");
}
+static void
+dump_forc(PLpgSQL_stmt_forc *stmt)
+{
+ dump_ind();
+ printf("FORC %s ", stmt->rec->refname);
+ printf("curvar=%d\n", stmt->curvar);
+
+ dump_indent += 2;
+ if (stmt->argquery != NULL)
+ {
+ dump_ind();
+ printf(" arguments = ");
+ dump_expr(stmt->argquery);
+ printf("\n");
+ }
+ dump_indent -= 2;
+
+ dump_stmts(stmt->body);
+
+ dump_ind();
+ printf(" ENDFORC\n");
+}
+
+static void
+dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt)
+{
+ dump_ind();
+ printf("FOREACHA var %d ", stmt->varno);
+ if (stmt->slice != 0)
+ printf("SLICE %d ", stmt->slice);
+ printf("IN ");
+ dump_expr(stmt->expr);
+ printf("\n");
+
+ dump_stmts(stmt->body);
+
+ dump_ind();
+ printf(" ENDFOREACHA");
+}
+
static void
dump_open(PLpgSQL_stmt_open *stmt)
{
printf(" execute = '");
dump_expr(stmt->dynquery);
printf("'\n");
+
+ if (stmt->params != NIL)
+ {
+ ListCell *lc;
+ int i;
+
+ dump_indent += 2;
+ dump_ind();
+ printf(" USING\n");
+ dump_indent += 2;
+ i = 1;
+ foreach(lc, stmt->params)
+ {
+ dump_ind();
+ printf(" parameter $%d: ", i++);
+ dump_expr((PLpgSQL_expr *) lfirst(lc));
+ printf("\n");
+ }
+ dump_indent -= 4;
+ }
}
dump_indent -= 2;
-
}
static void
dump_fetch(PLpgSQL_stmt_fetch *stmt)
{
dump_ind();
- printf("FETCH curvar=%d\n", stmt->curvar);
+ if (!stmt->is_move)
+ {
+ printf("FETCH curvar=%d\n", stmt->curvar);
+ dump_cursor_direction(stmt);
+
+ dump_indent += 2;
+ if (stmt->rec != NULL)
+ {
+ dump_ind();
+ printf(" target = %d %s\n", stmt->rec->dno, stmt->rec->refname);
+ }
+ if (stmt->row != NULL)
+ {
+ dump_ind();
+ printf(" target = %d %s\n", stmt->row->dno, stmt->row->refname);
+ }
+ dump_indent -= 2;
+ }
+ else
+ {
+ printf("MOVE curvar=%d\n", stmt->curvar);
+ dump_cursor_direction(stmt);
+ }
+}
+
+static void
+dump_cursor_direction(PLpgSQL_stmt_fetch *stmt)
+{
dump_indent += 2;
- if (stmt->rec != NULL)
+ dump_ind();
+ switch (stmt->direction)
{
- dump_ind();
- printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
+ case FETCH_FORWARD:
+ printf(" FORWARD ");
+ break;
+ case FETCH_BACKWARD:
+ printf(" BACKWARD ");
+ break;
+ case FETCH_ABSOLUTE:
+ printf(" ABSOLUTE ");
+ break;
+ case FETCH_RELATIVE:
+ printf(" RELATIVE ");
+ break;
+ default:
+ printf("??? unknown cursor direction %d", stmt->direction);
}
- if (stmt->row != NULL)
+
+ if (stmt->expr)
{
- dump_ind();
- printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname);
+ dump_expr(stmt->expr);
+ printf("\n");
}
- dump_indent -= 2;
+ else
+ printf("%ld\n", stmt->how_many);
+ dump_indent -= 2;
}
static void
dump_exit(PLpgSQL_stmt_exit *stmt)
{
dump_ind();
- printf("%s label='%s'",
- stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label);
+ printf("%s", stmt->is_exit ? "EXIT" : "CONTINUE");
+ if (stmt->label != NULL)
+ printf(" label='%s'", stmt->label);
if (stmt->cond != NULL)
{
printf(" WHEN ");
printf("\n");
}
+static void
+dump_return_query(PLpgSQL_stmt_return_query *stmt)
+{
+ dump_ind();
+ if (stmt->query)
+ {
+ printf("RETURN QUERY ");
+ dump_expr(stmt->query);
+ printf("\n");
+ }
+ else
+ {
+ printf("RETURN QUERY EXECUTE ");
+ dump_expr(stmt->dynquery);
+ printf("\n");
+ if (stmt->params != NIL)
+ {
+ ListCell *lc;
+ int i;
+
+ dump_indent += 2;
+ dump_ind();
+ printf(" USING\n");
+ dump_indent += 2;
+ i = 1;
+ foreach(lc, stmt->params)
+ {
+ dump_ind();
+ printf(" parameter $%d: ", i++);
+ dump_expr((PLpgSQL_expr *) lfirst(lc));
+ printf("\n");
+ }
+ dump_indent -= 4;
+ }
+ }
+}
+
static void
dump_raise(PLpgSQL_stmt_raise *stmt)
{
int i = 0;
dump_ind();
- printf("RAISE '%s'\n", stmt->message);
+ printf("RAISE level=%d", stmt->elog_level);
+ if (stmt->condname)
+ printf(" condname='%s'", stmt->condname);
+ if (stmt->message)
+ printf(" message='%s'", stmt->message);
+ printf("\n");
dump_indent += 2;
foreach(lc, stmt->params)
{
dump_expr((PLpgSQL_expr *) lfirst(lc));
printf("\n");
}
+ if (stmt->options)
+ {
+ dump_ind();
+ printf(" USING\n");
+ dump_indent += 2;
+ foreach(lc, stmt->options)
+ {
+ PLpgSQL_raise_option *opt = (PLpgSQL_raise_option *) lfirst(lc);
+
+ dump_ind();
+ switch (opt->opt_type)
+ {
+ case PLPGSQL_RAISEOPTION_ERRCODE:
+ printf(" ERRCODE = ");
+ break;
+ case PLPGSQL_RAISEOPTION_MESSAGE:
+ printf(" MESSAGE = ");
+ break;
+ case PLPGSQL_RAISEOPTION_DETAIL:
+ printf(" DETAIL = ");
+ break;
+ case PLPGSQL_RAISEOPTION_HINT:
+ printf(" HINT = ");
+ break;
+ case PLPGSQL_RAISEOPTION_COLUMN:
+ printf(" COLUMN = ");
+ break;
+ case PLPGSQL_RAISEOPTION_CONSTRAINT:
+ printf(" CONSTRAINT = ");
+ break;
+ case PLPGSQL_RAISEOPTION_DATATYPE:
+ printf(" DATATYPE = ");
+ break;
+ case PLPGSQL_RAISEOPTION_TABLE:
+ printf(" TABLE = ");
+ break;
+ case PLPGSQL_RAISEOPTION_SCHEMA:
+ printf(" SCHEMA = ");
+ break;
+ }
+ dump_expr(opt->expr);
+ printf("\n");
+ }
+ dump_indent -= 2;
+ }
dump_indent -= 2;
}
dump_ind();
printf(" INTO%s target = %d %s\n",
stmt->strict ? " STRICT" : "",
- stmt->rec->recno, stmt->rec->refname);
+ stmt->rec->dno, stmt->rec->refname);
}
if (stmt->row != NULL)
{
dump_ind();
printf(" INTO%s target = %d %s\n",
stmt->strict ? " STRICT" : "",
- stmt->row->rowno, stmt->row->refname);
+ stmt->row->dno, stmt->row->refname);
}
dump_indent -= 2;
}
dump_ind();
printf(" INTO%s target = %d %s\n",
stmt->strict ? " STRICT" : "",
- stmt->rec->recno, stmt->rec->refname);
+ stmt->rec->dno, stmt->rec->refname);
}
if (stmt->row != NULL)
{
dump_ind();
printf(" INTO%s target = %d %s\n",
stmt->strict ? " STRICT" : "",
- stmt->row->rowno, stmt->row->refname);
+ stmt->row->dno, stmt->row->refname);
+ }
+ if (stmt->params != NIL)
+ {
+ ListCell *lc;
+ int i;
+
+ dump_ind();
+ printf(" USING\n");
+ dump_indent += 2;
+ i = 1;
+ foreach(lc, stmt->params)
+ {
+ dump_ind();
+ printf(" parameter %d: ", i++);
+ dump_expr((PLpgSQL_expr *) lfirst(lc));
+ printf("\n");
+ }
+ dump_indent -= 2;
}
dump_indent -= 2;
}
dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
{
dump_ind();
- printf("FORS %s EXECUTE ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
+ printf("FORS %s EXECUTE ",
+ (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
dump_expr(stmt->query);
printf("\n");
+ if (stmt->params != NIL)
+ {
+ ListCell *lc;
+ int i;
+ dump_indent += 2;
+ dump_ind();
+ printf(" USING\n");
+ dump_indent += 2;
+ i = 1;
+ foreach(lc, stmt->params)
+ {
+ dump_ind();
+ printf(" parameter $%d: ", i++);
+ dump_expr((PLpgSQL_expr *) lfirst(lc));
+ printf("\n");
+ }
+ dump_indent -= 4;
+ }
dump_stmts(stmt->body);
-
dump_ind();
printf(" ENDFORS\n");
}
ListCell *lc;
dump_ind();
- printf("GET DIAGNOSTICS ");
+ printf("GET %s DIAGNOSTICS ", stmt->is_stacked ? "STACKED" : "CURRENT");
foreach(lc, stmt->diag_items)
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
if (lc != list_head(stmt->diag_items))
printf(", ");
- printf("{var %d} = ", diag_item->target);
-
- switch (diag_item->kind)
- {
- case PLPGSQL_GETDIAG_ROW_COUNT:
- printf("ROW_COUNT");
- break;
-
- case PLPGSQL_GETDIAG_RESULT_OID:
- printf("RESULT_OID");
- break;
-
- default:
- printf("???");
- break;
- }
+ printf("{var %d} = %s", diag_item->target,
+ plpgsql_getdiag_kindname(diag_item->kind));
}
printf("\n");
}
static void
dump_expr(PLpgSQL_expr *expr)
{
- int i;
-
- printf("'%s", expr->query);
- if (expr->nparams > 0)
- {
- printf(" {");
- for (i = 0; i < expr->nparams; i++)
- {
- if (i > 0)
- printf(", ");
- printf("$%d=%d", i + 1, expr->params[i]);
- }
- printf("}");
- }
- printf("'");
+ printf("'%s'", expr->query);
}
void
PLpgSQL_datum *d;
printf("\nExecution tree of successfully compiled PL/pgSQL function %s:\n",
- func->fn_name);
+ func->fn_signature);
printf("\nFunction's data area:\n");
for (i = 0; i < func->ndatums; i++)
dump_expr(((PLpgSQL_arrayelem *) d)->subscript);
printf("\n");
break;
- case PLPGSQL_DTYPE_TRIGARG:
- printf("TRIGARG ");
- dump_expr(((PLpgSQL_trigarg *) d)->argnum);
- printf("\n");
- break;
default:
printf("??? unknown data type %d\n", d->dtype);
}
dump_indent = 0;
printf("%3d:", func->action->lineno);
dump_block(func->action);
- printf("\nEnd of execution tree of function %s\n\n", func->fn_name);
+ printf("\nEnd of execution tree of function %s\n\n", func->fn_signature);
fflush(stdout);
}