summaryrefslogtreecommitdiff
path: root/contrib/plpgsql/src/pl_funcs.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/plpgsql/src/pl_funcs.c')
-rw-r--r--contrib/plpgsql/src/pl_funcs.c675
1 files changed, 675 insertions, 0 deletions
diff --git a/contrib/plpgsql/src/pl_funcs.c b/contrib/plpgsql/src/pl_funcs.c
new file mode 100644
index 00000000000..94de9e4c9a6
--- /dev/null
+++ b/contrib/plpgsql/src/pl_funcs.c
@@ -0,0 +1,675 @@
+/**********************************************************************
+ * pl_funcs.c - Misc functins for the PL/pgSQL
+ * procedural language
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/contrib/plpgsql/src/Attic/pl_funcs.c,v 1.1 1998/08/22 12:38:32 momjian Exp $
+ *
+ * This software is copyrighted by Jan Wieck - Hamburg.
+ *
+ * The author hereby grants permission to use, copy, modify,
+ * distribute, and license this software and its documentation
+ * for any purpose, provided that existing copyright notices are
+ * retained in all copies and that this notice is included
+ * verbatim in any distributions. No written agreement, license,
+ * or royalty fee is required for any of the authorized uses.
+ * Modifications to this software may be copyrighted by their
+ * author and need not follow the licensing terms described
+ * here, provided that the new terms are clearly indicated on
+ * the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
+ * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN
+ * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON
+ * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
+ * ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ **********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "plpgsql.h"
+#include "pl.tab.h"
+
+
+/* ----------
+ * Local variables for the namestack handling
+ * ----------
+ */
+static PLpgSQL_ns *ns_current = NULL;
+static bool ns_localmode = false;
+
+
+/* ----------
+ * plpgsql_dstring_init Dynamic string initialization
+ * ----------
+ */
+void plpgsql_dstring_init(PLpgSQL_dstring *ds)
+{
+ ds->value = palloc(ds->alloc = 512);
+ ds->used = 0;
+}
+
+
+/* ----------
+ * plpgsql_dstring_free Dynamic string destruction
+ * ----------
+ */
+void plpgsql_dstring_free(PLpgSQL_dstring *ds)
+{
+ pfree(ds->value);
+}
+
+
+/* ----------
+ * plpgsql_dstring_append Dynamic string extending
+ * ----------
+ */
+void plpgsql_dstring_append(PLpgSQL_dstring *ds, char *str)
+{
+ int len = strlen(str);
+
+ if (ds->used + len + 1 > ds->alloc) {
+ ds->alloc *= 2;
+ ds->value = repalloc(ds->value, ds->alloc);
+ }
+
+ strcpy(&(ds->value[ds->used]), str);
+ ds->used += len;
+}
+
+
+/* ----------
+ * plpgsql_dstring_get Dynamic string get value
+ * ----------
+ */
+char *plpgsql_dstring_get(PLpgSQL_dstring *ds)
+{
+ return ds->value;
+}
+
+
+/* ----------
+ * plpgsql_ns_init Initialize the namestack
+ * ----------
+ */
+void plpgsql_ns_init(void)
+{
+ ns_current = NULL;
+ ns_localmode = false;
+}
+
+
+/* ----------
+ * plpgsql_ns_setlocal Tell plpgsql_ns_lookup to or to
+ * not look into the current level
+ * only.
+ * ----------
+ */
+bool plpgsql_ns_setlocal(bool flag)
+{
+ bool oldstate;
+
+ oldstate = ns_localmode;
+ ns_localmode = flag;
+ return oldstate;
+}
+
+
+/* ----------
+ * plpgsql_ns_push Enter a new namestack level
+ * ----------
+ */
+void plpgsql_ns_push(char *label)
+{
+ PLpgSQL_ns *new;
+
+ 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);
+}
+
+
+/* ----------
+ * plpgsql_ns_pop Return to the previous level
+ * ----------
+ */
+void plpgsql_ns_pop()
+{
+ int i;
+ PLpgSQL_ns *old;
+
+ old = ns_current;
+ ns_current = old->upper;
+
+ for (i = 0; i < old->items_used; i++) {
+ pfree(old->items[i]);
+ }
+ pfree(old->items);
+ pfree(old);
+}
+
+
+/* ----------
+ * plpgsql_ns_additem Add an item to the current
+ * namestack level
+ * ----------
+ */
+void plpgsql_ns_additem(int itemtype, int itemno, char *name)
+{
+ PLpgSQL_ns *ns = ns_current;
+ PLpgSQL_nsitem *nse;
+
+ if (name == NULL)
+ name = "";
+
+ if (ns->items_used == ns->items_alloc) {
+ 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);
+ }
+ }
+
+ nse = palloc(sizeof(PLpgSQL_nsitem) + strlen(name));
+ nse->itemtype = itemtype;
+ nse->itemno = itemno;
+ strcpy(nse->name, name);
+ ns->items[ns->items_used++] = nse;
+}
+
+
+/* ----------
+ * plpgsql_ns_lookup Lookup for a word in the namestack
+ * ----------
+ */
+PLpgSQL_nsitem *plpgsql_ns_lookup(char *name, char *label)
+{
+ PLpgSQL_ns *ns;
+ int i;
+
+ /* ----------
+ * If a label is specified, lookup only in that
+ * ----------
+ */
+ if (label != NULL) {
+ 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 */
+ }
+
+ /* ----------
+ * No label given, lookup for visible labels ignoring localmode
+ * ----------
+ */
+ for (ns = ns_current; ns != NULL; ns = ns->upper) {
+ if (!strcmp(ns->items[0]->name, name)) {
+ return ns->items[0];
+ }
+ }
+
+ /* ----------
+ * Finally lookup name in the namestack
+ * ----------
+ */
+ for (ns = ns_current; ns != NULL; ns = ns->upper) {
+ 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 */
+ }
+ }
+
+ return NULL;
+}
+
+
+/* ----------
+ * plpgsql_ns_rename Rename a namespace entry
+ * ----------
+ */
+void plpgsql_ns_rename(char *oldname, char *newname)
+{
+ PLpgSQL_ns *ns;
+ PLpgSQL_nsitem *newitem;
+ int i;
+
+ /* ----------
+ * Lookup in the current namespace only
+ * ----------
+ */
+ /* ----------
+ * Lookup name in the namestack
+ * ----------
+ */
+ for (ns = ns_current; ns != NULL; ns = ns->upper) {
+ 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;
+ }
+ }
+ }
+
+ elog(ERROR, "there is no variable '%s' in the current block", oldname);
+}
+
+
+/* ----------
+ * plpgsql_tolower Translate a string in place to
+ * lower case
+ * ----------
+ */
+char *plpgsql_tolower(char *s)
+{
+ char *cp;
+
+ for (cp = s; *cp; cp++) {
+ if (isupper(*cp)) *cp = tolower(*cp);
+ }
+
+ return s;
+}
+
+
+
+
+
+/**********************************************************************
+ * Debug functions for analyzing the compiled code
+ **********************************************************************/
+static int dump_indent;
+
+static void dump_ind();
+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_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_select(PLpgSQL_stmt_select *stmt);
+static void dump_exit(PLpgSQL_stmt_exit *stmt);
+static void dump_return(PLpgSQL_stmt_return *stmt);
+static void dump_raise(PLpgSQL_stmt_raise *stmt);
+static void dump_execsql(PLpgSQL_stmt_execsql *stmt);
+static void dump_expr(PLpgSQL_expr *expr);
+
+
+static void dump_ind()
+{
+ int i;
+ for (i = 0; i < dump_indent; i++) {
+ printf(" ");
+ }
+}
+
+static void dump_stmt(PLpgSQL_stmt *stmt)
+{
+ printf("%3d:", stmt->lineno);
+ switch (stmt->cmd_type) {
+ case PLPGSQL_STMT_BLOCK:
+ dump_block((PLpgSQL_stmt_block *)stmt);
+ break;
+ case PLPGSQL_STMT_ASSIGN:
+ dump_assign((PLpgSQL_stmt_assign *)stmt);
+ break;
+ case PLPGSQL_STMT_IF:
+ dump_if((PLpgSQL_stmt_if *)stmt);
+ break;
+ case PLPGSQL_STMT_LOOP:
+ dump_loop((PLpgSQL_stmt_loop *)stmt);
+ break;
+ case PLPGSQL_STMT_WHILE:
+ dump_while((PLpgSQL_stmt_while *)stmt);
+ break;
+ case PLPGSQL_STMT_FORI:
+ dump_fori((PLpgSQL_stmt_fori *)stmt);
+ break;
+ case PLPGSQL_STMT_FORS:
+ dump_fors((PLpgSQL_stmt_fors *)stmt);
+ break;
+ case PLPGSQL_STMT_SELECT:
+ dump_select((PLpgSQL_stmt_select *)stmt);
+ break;
+ case PLPGSQL_STMT_EXIT:
+ dump_exit((PLpgSQL_stmt_exit *)stmt);
+ break;
+ case PLPGSQL_STMT_RETURN:
+ dump_return((PLpgSQL_stmt_return *)stmt);
+ break;
+ case PLPGSQL_STMT_RAISE:
+ dump_raise((PLpgSQL_stmt_raise *)stmt);
+ break;
+ case PLPGSQL_STMT_EXECSQL:
+ dump_execsql((PLpgSQL_stmt_execsql *)stmt);
+ break;
+ default:
+ elog(ERROR, "plpgsql_dump: unknown cmd_type %d\n", stmt->cmd_type);
+ break;
+ }
+}
+
+static void dump_block(PLpgSQL_stmt_block *block)
+{
+ int i;
+ char *name;
+
+ if (block->label == NULL) {
+ name = "*unnamed*";
+ } else {
+ name = block->label;
+ }
+
+ dump_ind();
+ printf("BLOCK <<%s>>\n", name);
+
+ dump_indent += 2;
+ for (i = 0; i < block->body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(block->body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" END -- %s\n", name);
+}
+
+static void dump_assign(PLpgSQL_stmt_assign *stmt)
+{
+ dump_ind();
+ printf("ASSIGN var %d := ", stmt->varno);
+ dump_expr(stmt->expr);
+ printf("\n");
+}
+
+static void dump_if(PLpgSQL_stmt_if *stmt)
+{
+ int i;
+
+ dump_ind();
+ printf("IF ");
+ dump_expr(stmt->cond);
+ printf(" THEN\n");
+
+ dump_indent += 2;
+ for (i = 0; i < stmt->true_body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(stmt->true_body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" ELSE\n");
+
+ dump_indent += 2;
+ for (i = 0; i < stmt->false_body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(stmt->false_body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" ENDIF\n");
+}
+
+static void dump_loop(PLpgSQL_stmt_loop *stmt)
+{
+ int i;
+
+ dump_ind();
+ printf("LOOP\n");
+
+ dump_indent += 2;
+ for (i = 0; i < stmt->body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" ENDLOOP\n");
+}
+
+static void dump_while(PLpgSQL_stmt_while *stmt)
+{
+ int i;
+
+ dump_ind();
+ printf("WHILE ");
+ dump_expr(stmt->cond);
+ printf("\n");
+
+ dump_indent += 2;
+ for (i = 0; i < stmt->body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" ENDWHILE\n");
+}
+
+static void dump_fori(PLpgSQL_stmt_fori *stmt)
+{
+ int i;
+
+ dump_ind();
+ printf("FORI %s %s\n", stmt->var->refname, (stmt->reverse) ? "REVERSE" : "NORMAL");
+
+ dump_indent += 2;
+ dump_ind();
+ printf(" lower = ");
+ dump_expr(stmt->lower);
+ printf("\n");
+ dump_ind();
+ printf(" upper = ");
+ dump_expr(stmt->upper);
+ printf("\n");
+
+ for (i = 0; i < stmt->body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" ENDFORI\n");
+}
+
+static void dump_fors(PLpgSQL_stmt_fors *stmt)
+{
+ int i;
+
+ dump_ind();
+ printf("FORS %s ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
+ dump_expr(stmt->query);
+ printf("\n");
+
+ dump_indent += 2;
+ for (i = 0; i < stmt->body->stmts_used; i++) {
+ dump_stmt((PLpgSQL_stmt *)(stmt->body->stmts[i]));
+ }
+ dump_indent -= 2;
+
+ dump_ind();
+ printf(" ENDFORS\n");
+}
+
+static void dump_select(PLpgSQL_stmt_select *stmt)
+{
+ dump_ind();
+ printf("SELECT ");
+ dump_expr(stmt->query);
+ printf("\n");
+
+ dump_indent += 2;
+ if (stmt->rec != NULL) {
+ dump_ind();
+ printf(" target = %d %s\n", stmt->rec->recno, stmt->rec->refname);
+ }
+ if (stmt->row != NULL) {
+ dump_ind();
+ printf(" target = %d %s\n", stmt->row->rowno, stmt->row->refname);
+ }
+ dump_indent -= 2;
+
+}
+
+static void dump_exit(PLpgSQL_stmt_exit *stmt)
+{
+ dump_ind();
+ printf("EXIT lbl='%s'", stmt->label);
+ if (stmt->cond != NULL) {
+ printf(" WHEN ");
+ dump_expr(stmt->cond);
+ }
+ printf("\n");
+}
+
+static void dump_return(PLpgSQL_stmt_return *stmt)
+{
+ dump_ind();
+ printf("RETURN ");
+ if (stmt->retrecno >= 0) {
+ printf("record %d", stmt->retrecno);
+ } else {
+ if (stmt->expr == NULL) {
+ printf("NULL");
+ } else {
+ dump_expr(stmt->expr);
+ }
+ }
+ printf("\n");
+}
+
+static void dump_raise(PLpgSQL_stmt_raise *stmt)
+{
+ int i;
+
+ dump_ind();
+ printf("RAISE '%s'", stmt->message);
+ for (i = 0; i < stmt->nparams; i++) {
+ printf(" %d", stmt->params[i]);
+ }
+ printf("\n");
+}
+
+static void dump_execsql(PLpgSQL_stmt_execsql *stmt)
+{
+ dump_ind();
+ printf("EXECSQL ");
+ dump_expr(stmt->sqlstmt);
+ 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("'");
+}
+
+void plpgsql_dumptree(PLpgSQL_function *func)
+{
+ int i;
+ PLpgSQL_datum *d;
+
+ printf("\nExecution tree of successfully compiled PL/pgSQL function %s:\n",
+ func->fn_name);
+
+ printf("\nFunctions data area:\n");
+ for (i = 0; i < func->ndatums; i++) {
+ d = func->datums[i];
+
+ printf(" entry %d: ", i);
+ switch (d->dtype) {
+ case PLPGSQL_DTYPE_VAR:
+ {
+ PLpgSQL_var *var = (PLpgSQL_var *)d;
+ printf("VAR %-16s type %s (typoid %d) atttypmod %d\n",
+ var->refname, var->datatype->typname,
+ var->datatype->typoid,
+ var->datatype->atttypmod);
+ }
+ break;
+ case PLPGSQL_DTYPE_ROW:
+ {
+ PLpgSQL_row *row = (PLpgSQL_row *)d;
+ int i;
+ printf("ROW %-16s fields", row->refname);
+ for (i = 0; i < row->nfields; i++) {
+ printf(" %s=var %d", row->fieldnames[i],
+ row->varnos[i]);
+ }
+ printf("\n");
+ }
+ break;
+ case PLPGSQL_DTYPE_REC:
+ printf("REC %s\n", ((PLpgSQL_rec *)d)->refname);
+ break;
+ case PLPGSQL_DTYPE_RECFIELD:
+ printf("RECFIELD %-16s of REC %d\n", ((PLpgSQL_recfield *)d)->fieldname, ((PLpgSQL_recfield *)d)->recno);
+ 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);
+ }
+ }
+ printf("\nFunctions statements:\n");
+
+ 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);
+}
+
+