/********************************************************************** * pl_funcs.c - Misc functins for the PL/pgSQL * procedural language * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.6 2000/06/14 18:18:00 petere 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 #include #include #include #include #include #include #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 = ""; name = plpgsql_tolower(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 to lower case * but honor "" escaping. * ---------- */ char * plpgsql_tolower(char *s) { char *ret; char *cp; ret = palloc(strlen(s) + 1); cp = ret; while (*s) { if (*s == '"') { s++; while (*s) { if (*s == '"') break; *cp++ = *s++; } if (*s != '"') { plpgsql_comperrinfo(); elog(ERROR, "unterminated \""); } s++; } else { if (isupper((int) *s)) *cp++ = tolower(*s++); else *cp++ = *s++; } } *cp = '\0'; return ret; } /********************************************************************** * 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 %u) 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); }