postgresql/src/pl/plpgsql/src/pl_funcs.c

748 lines
15 KiB
C

/**********************************************************************
* 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 <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 = "";
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);
}