pl_funcs.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,173 行 · 第 1/2 页
C
1,173 行
/*------------------------------------------------------------------------- * * pl_funcs.c - Misc functions for the PL/pgSQL * procedural language * * Portions Copyright (c) 1996-2008, 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.67 2008/01/01 19:46:00 momjian Exp $ * *------------------------------------------------------------------------- */#include "plpgsql.h"#include "pl.tab.h"#include <ctype.h>#include "parser/scansup.h"/* ---------- * Local variables for the namestack handling * ---------- */static PLpgSQL_ns *ns_current = NULL;static bool ns_localmode = false;/* ---------- * plpgsql_dstring_init Dynamic string initialization * ---------- */voidplpgsql_dstring_init(PLpgSQL_dstring *ds){ ds->value = palloc(ds->alloc = 512); ds->used = 1; ds->value[0] = '\0';}/* ---------- * plpgsql_dstring_free Dynamic string destruction * ---------- */voidplpgsql_dstring_free(PLpgSQL_dstring *ds){ pfree(ds->value);}static voidplpgsql_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 * ---------- */voidplpgsql_dstring_append(PLpgSQL_dstring *ds, const char *str){ int len = strlen(str); int needed = ds->used + len; 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_dstring_append_char Append a single character * to a dynamic string * ---------- */voidplpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c){ if (ds->used == ds->alloc) plpgsql_dstring_expand(ds, ds->used + 1); ds->value[ds->used - 1] = c; ds->value[ds->used] = '\0'; ds->used++;}/* ---------- * plpgsql_dstring_get Dynamic string get value * ---------- */char *plpgsql_dstring_get(PLpgSQL_dstring *ds){ return ds->value;}/* ---------- * plpgsql_ns_init Initialize the namestack * ---------- */voidplpgsql_ns_init(void){ ns_current = NULL; ns_localmode = false;}/* ---------- * plpgsql_ns_setlocal Tell plpgsql_ns_lookup whether to * look into the current level only. * * This is a crock, but in the current design we need it because scan.l * initiates name lookup, and the scanner does not know whether we are * examining a name being declared in a DECLARE section. For that case * we only want to know if there is a conflicting name earlier in the * same DECLARE section. So the grammar must temporarily set local mode * before scanning decl_varnames. * ---------- */boolplpgsql_ns_setlocal(bool flag){ bool oldstate; oldstate = ns_localmode; ns_localmode = flag; return oldstate;}/* ---------- * plpgsql_ns_push Enter a new namestack level * ---------- */voidplpgsql_ns_push(const char *label){ PLpgSQL_ns *new; if (label == NULL) label = ""; new = palloc0(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 * ---------- */voidplpgsql_ns_pop(void){ 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 * ---------- */voidplpgsql_ns_additem(int itemtype, int itemno, const char *name){ PLpgSQL_ns *ns = ns_current; PLpgSQL_nsitem *nse; Assert(name != NULL); 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 an identifier in the namestack * * Note that this only searches for variables, not labels. * * 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. * ---------- */PLpgSQL_nsitem *plpgsql_ns_lookup(const char *name1, const char *name2, const char *name3, int *names_used){ PLpgSQL_ns *ns; int i; /* Scan each level of the namestack */ for (ns = ns_current; ns != NULL; ns = ns->upper) { /* Check for unqualified match to variable name */ for (i = 1; i < ns->items_used; i++) { PLpgSQL_nsitem *nsitem = ns->items[i]; if (strcmp(nsitem->name, name1) == 0) { if (name2 == NULL || nsitem->itemtype != PLPGSQL_NSTYPE_VAR) { if (names_used) *names_used = 1; return nsitem; } } } /* Check for qualified match to variable name */ if (name2 != NULL && strcmp(ns->items[0]->name, name1) == 0) { for (i = 1; i < ns->items_used; i++) { PLpgSQL_nsitem *nsitem = ns->items[i]; if (strcmp(nsitem->name, name2) == 0) { if (name3 == NULL || nsitem->itemtype != PLPGSQL_NSTYPE_VAR) { if (names_used) *names_used = 2; return nsitem; } } } } if (ns_localmode) break; /* do not look into upper levels */ } /* This is just to suppress possibly-uninitialized-variable warnings */ if (names_used) *names_used = 0; return NULL; /* No match found */}/* ---------- * plpgsql_ns_lookup_label Lookup a label in the namestack * ---------- */PLpgSQL_nsitem *plpgsql_ns_lookup_label(const char *name){ PLpgSQL_ns *ns; for (ns = ns_current; ns != NULL; ns = ns->upper) { if (strcmp(ns->items[0]->name, name) == 0) return ns->items[0]; } return NULL; /* label not found */}/* ---------- * plpgsql_ns_rename Rename a namespace entry * ---------- */voidplpgsql_ns_rename(char *oldname, char *newname){ PLpgSQL_ns *ns; PLpgSQL_nsitem *newitem; int i; /* * 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) == 0) { 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; } } } ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("there is no variable \"%s\" in the current block", oldname)));}/* ---------- * 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. * ---------- */voidplpgsql_convert_ident(const char *s, char **output, int numidents){ const char *sstart = s; int identctr = 0; /* Outer loop over identifiers */ while (*s) { char *curident; char *cp; /* Process current identifier */ if (*s == '"') { /* Quoted identifier: copy, collapsing out doubled quotes */ curident = palloc(strlen(s) + 1); /* surely enough room */ cp = curident; s++; while (*s) { if (*s == '"') { 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 != '.' && !scanner_isspace(*s)) s++; /* Downcase and truncate to NAMEDATALEN */ curident = downcase_truncate_identifier(thisstart, s - thisstart, false); } /* 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 && scanner_isspace(*s)) s++; if (*s++ != '.') elog(ERROR, "expected dot between identifiers: %s", sstart); while (*s && scanner_isspace(*s)) s++; if (*s == '\0') elog(ERROR, "expected another identifier: %s", sstart); } } 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 _("statement block"); 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 loop variable"); 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_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"; } return "unknown";}/********************************************************************** * Debug functions for analyzing the compiled code **********************************************************************/static int dump_indent;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_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_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_dynfors(PLpgSQL_stmt_dynfors *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);static voiddump_ind(void){ int i; for (i = 0; i < dump_indent; i++) printf(" ");}static voiddump_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_EXIT: dump_exit((PLpgSQL_stmt_exit *) stmt); break; case PLPGSQL_STMT_RETURN:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?