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 + -
显示快捷键?