⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gram.y

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 Y
📖 第 1 页 / 共 4 页
字号:
}/* * Read a SQL construct and build a PLpgSQL_expr for it. * * until:		token code for expected terminator * until2:		token code for alternate terminator (pass 0 if none) * expected:	text to use in complaining that terminator was not found * sqlstart:	text to prefix to the accumulated SQL text * isexpression: whether to say we're reading an "expression" or a "statement" * valid_sql:   whether to check the syntax of the expr (prefixed with sqlstart) * endtoken:	if not NULL, ending token is stored at *endtoken *				(this is only interesting if until2 isn't zero) */static PLpgSQL_expr *read_sql_construct(int until,				   int until2,				   const char *expected,				   const char *sqlstart,				   bool isexpression,				   bool valid_sql,				   int *endtoken){	int					tok;	int					lno;	PLpgSQL_dstring		ds;	int					parenlevel = 0;	int					nparams = 0;	int					params[MAX_EXPR_PARAMS];	char				buf[32];	PLpgSQL_expr		*expr;	lno = plpgsql_scanner_lineno();	plpgsql_dstring_init(&ds);	plpgsql_dstring_append(&ds, sqlstart);	for (;;)	{		tok = yylex();		if (tok == until && parenlevel == 0)			break;		if (tok == until2 && parenlevel == 0)			break;		if (tok == '(' || tok == '[')			parenlevel++;		else if (tok == ')' || tok == ']')		{			parenlevel--;			if (parenlevel < 0)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("mismatched parentheses")));		}		/*		 * End of function definition is an error, and we don't expect to		 * hit a semicolon either (unless it's the until symbol, in which		 * case we should have fallen out above).		 */		if (tok == 0 || tok == ';')		{			plpgsql_error_lineno = lno;			if (parenlevel != 0)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("mismatched parentheses")));			if (isexpression)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("missing \"%s\" at end of SQL expression",								expected)));			else				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("missing \"%s\" at end of SQL statement",								expected)));		}		if (plpgsql_SpaceScanned)			plpgsql_dstring_append(&ds, " ");		switch (tok)		{			case T_SCALAR:				snprintf(buf, sizeof(buf), " $%d ",						 assign_expr_param(yylval.scalar->dno,										   params, &nparams));				plpgsql_dstring_append(&ds, buf);				break;			case T_ROW:				snprintf(buf, sizeof(buf), " $%d ",						 assign_expr_param(yylval.row->rowno,										   params, &nparams));				plpgsql_dstring_append(&ds, buf);				break;			case T_RECORD:				snprintf(buf, sizeof(buf), " $%d ",						 assign_expr_param(yylval.rec->recno,										   params, &nparams));				plpgsql_dstring_append(&ds, buf);				break;			default:				plpgsql_dstring_append(&ds, yytext);				break;		}	}	if (endtoken)		*endtoken = tok;	expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));	expr->dtype			= PLPGSQL_DTYPE_EXPR;	expr->query			= pstrdup(plpgsql_dstring_get(&ds));	expr->plan			= NULL;	expr->nparams		= nparams;	while(nparams-- > 0)		expr->params[nparams] = params[nparams];	plpgsql_dstring_free(&ds);	if (valid_sql)		check_sql_expr(expr->query);	return expr;}static PLpgSQL_type *read_datatype(int tok){	int					lno;	PLpgSQL_dstring		ds;	PLpgSQL_type		*result;	bool				needspace = false;	int					parenlevel = 0;	lno = plpgsql_scanner_lineno();	/* Often there will be a lookahead token, but if not, get one */	if (tok == YYEMPTY)		tok = yylex();	if (tok == T_DTYPE)	{		/* lexer found word%TYPE and did its thing already */		return yylval.dtype;	}	plpgsql_dstring_init(&ds);	while (tok != ';')	{		if (tok == 0)		{			plpgsql_error_lineno = lno;			if (parenlevel != 0)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("mismatched parentheses")));			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("incomplete datatype declaration")));		}		/* Possible followers for datatype in a declaration */		if (tok == K_NOT || tok == K_ASSIGN || tok == K_DEFAULT)			break;		/* Possible followers for datatype in a cursor_arg list */		if ((tok == ',' || tok == ')') && parenlevel == 0)			break;		if (tok == '(')			parenlevel++;		else if (tok == ')')			parenlevel--;		if (needspace)			plpgsql_dstring_append(&ds, " ");		needspace = true;		plpgsql_dstring_append(&ds, yytext);		tok = yylex();	}	plpgsql_push_back_token(tok);	plpgsql_error_lineno = lno;	/* in case of error in parse_datatype */	result = plpgsql_parse_datatype(plpgsql_dstring_get(&ds));	plpgsql_dstring_free(&ds);	return result;}static PLpgSQL_stmt *make_select_stmt(void){	PLpgSQL_dstring		ds;	int					nparams = 0;	int					params[MAX_EXPR_PARAMS];	char				buf[32];	PLpgSQL_expr		*expr;	PLpgSQL_row			*row = NULL;	PLpgSQL_rec			*rec = NULL;	int					tok;	bool				have_into = false;	plpgsql_dstring_init(&ds);	plpgsql_dstring_append(&ds, "SELECT ");	while (1)	{		tok = yylex();		if (tok == ';')			break;		if (tok == 0)		{			plpgsql_error_lineno = plpgsql_scanner_lineno();			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("unexpected end of function definition")));		}		if (tok == K_INTO)		{			if (have_into)			{				plpgsql_error_lineno = plpgsql_scanner_lineno();				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("INTO specified more than once")));			}			tok = yylex();			switch (tok)			{				case T_ROW:					row = yylval.row;					have_into = true;					break;				case T_RECORD:					rec = yylval.rec;					have_into = true;					break;				case T_SCALAR:					row = read_into_scalar_list(yytext, yylval.scalar);					have_into = true;					break;				default:					/* Treat the INTO as non-special */					plpgsql_dstring_append(&ds, " INTO ");					plpgsql_push_back_token(tok);					break;			}			continue;		}		if (plpgsql_SpaceScanned)			plpgsql_dstring_append(&ds, " ");		switch (tok)		{			case T_SCALAR:				snprintf(buf, sizeof(buf), " $%d ",						 assign_expr_param(yylval.scalar->dno,										   params, &nparams));				plpgsql_dstring_append(&ds, buf);				break;			case T_ROW:				snprintf(buf, sizeof(buf), " $%d ",						 assign_expr_param(yylval.row->rowno,										   params, &nparams));				plpgsql_dstring_append(&ds, buf);				break;			case T_RECORD:				snprintf(buf, sizeof(buf), " $%d ",						 assign_expr_param(yylval.rec->recno,										   params, &nparams));				plpgsql_dstring_append(&ds, buf);				break;			default:				plpgsql_dstring_append(&ds, yytext);				break;		}	}	expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));	expr->dtype			= PLPGSQL_DTYPE_EXPR;	expr->query			= pstrdup(plpgsql_dstring_get(&ds));	expr->plan			= NULL;	expr->nparams		= nparams;	while(nparams-- > 0)		expr->params[nparams] = params[nparams];	plpgsql_dstring_free(&ds);	check_sql_expr(expr->query);	if (have_into)	{		PLpgSQL_stmt_select *select;		select = palloc0(sizeof(PLpgSQL_stmt_select));		select->cmd_type = PLPGSQL_STMT_SELECT;		select->rec		 = rec;		select->row		 = row;		select->query	 = expr;		return (PLpgSQL_stmt *)select;	}	else	{		PLpgSQL_stmt_execsql *execsql;		execsql = palloc(sizeof(PLpgSQL_stmt_execsql));		execsql->cmd_type = PLPGSQL_STMT_EXECSQL;		execsql->sqlstmt  = expr;		return (PLpgSQL_stmt *)execsql;	}}static PLpgSQL_stmt *make_fetch_stmt(void){	int					tok;	PLpgSQL_row		   *row = NULL;	PLpgSQL_rec		   *rec = NULL;	PLpgSQL_stmt_fetch *fetch;	/* We have already parsed everything through the INTO keyword */	tok = yylex();	switch (tok)	{		case T_ROW:			row = yylval.row;			break;		case T_RECORD:			rec = yylval.rec;			break;		case T_SCALAR:			row = read_into_scalar_list(yytext, yylval.scalar);			break;		default:			yyerror("syntax error");	}	tok = yylex();	if (tok != ';')		yyerror("syntax error");	fetch = palloc0(sizeof(PLpgSQL_stmt_select));	fetch->cmd_type = PLPGSQL_STMT_FETCH;	fetch->rec		 = rec;	fetch->row		 = row;	return (PLpgSQL_stmt *)fetch;}static voidcheck_assignable(PLpgSQL_datum *datum){	switch (datum->dtype)	{		case PLPGSQL_DTYPE_VAR:			if (((PLpgSQL_var *) datum)->isconst)			{				plpgsql_error_lineno = plpgsql_scanner_lineno();				ereport(ERROR,						(errcode(ERRCODE_ERROR_IN_ASSIGNMENT),						 errmsg("\"%s\" is declared CONSTANT",								((PLpgSQL_var *) datum)->refname)));			}			break;		case PLPGSQL_DTYPE_ROW:			/* always assignable? */			break;		case PLPGSQL_DTYPE_REC:			/* always assignable?  What about NEW/OLD? */			break;		case PLPGSQL_DTYPE_RECFIELD:			/* always assignable? */			break;		case PLPGSQL_DTYPE_ARRAYELEM:			/* always assignable? */			break;		case PLPGSQL_DTYPE_TRIGARG:			yyerror("cannot assign to tg_argv");			break;		default:			elog(ERROR, "unrecognized dtype: %d", datum->dtype);			break;	}}/* * Given the first datum and name in the INTO list, continue to read * comma-separated scalar variables until we run out. Then construct * and return a fake "row" variable that represents the list of * scalars. */static PLpgSQL_row *read_into_scalar_list(const char *initial_name,					  PLpgSQL_datum *initial_datum){	int				 nfields;	char			*fieldnames[1024];	int				 varnos[1024];	PLpgSQL_row		*row;	int				 tok;	check_assignable(initial_datum);	fieldnames[0] = pstrdup(initial_name);	varnos[0]	  = initial_datum->dno;	nfields		  = 1;	while ((tok = yylex()) == ',')	{		/* Check for array overflow */		if (nfields >= 1024)		{			plpgsql_error_lineno = plpgsql_scanner_lineno();			ereport(ERROR,					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),					 errmsg("too many INTO variables specified")));		}		tok = yylex();		switch(tok)		{			case T_SCALAR:				check_assignable(yylval.scalar);				fieldnames[nfields] = pstrdup(yytext);				varnos[nfields++]	= yylval.scalar->dno;				break;			default:				plpgsql_error_lineno = plpgsql_scanner_lineno();				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("\"%s\" is not a variable",								yytext)));		}	}	/*	 * We read an extra, non-comma character from yylex(), so push it	 * back onto the input stream	 */	plpgsql_push_back_token(tok);	row = palloc(sizeof(PLpgSQL_row));	row->dtype = PLPGSQL_DTYPE_ROW;	row->refname = pstrdup("*internal*");	row->lineno = plpgsql_scanner_lineno();	row->rowtupdesc = NULL;	row->nfields = nfields;	row->fieldnames = palloc(sizeof(char *) * nfields);	row->varnos = palloc(sizeof(int) * nfields);	while (--nfields >= 0)	{		row->fieldnames[nfields] = fieldnames[nfields];		row->varnos[nfields] = varnos[nfields];	}	plpgsql_adddatum((PLpgSQL_datum *)row);	return row;}/* * When the PL/PgSQL parser expects to see a SQL statement, it is very * liberal in what it accepts; for example, we often assume an * unrecognized keyword is the beginning of a SQL statement. This * avoids the need to duplicate parts of the SQL grammar in the * PL/PgSQL grammar, but it means we can accept wildly malformed * input. To try and catch some of the more obviously invalid input, * we run the strings we expect to be SQL statements through the main * SQL parser. * * We only invoke the raw parser (not the analyzer); this doesn't do * any database access and does not check any semantic rules, it just * checks for basic syntactic correctness. We do this here, rather * than after parsing has finished, because a malformed SQL statement * may cause the PL/PgSQL parser to become confused about statement * borders. So it is best to bail out as early as we can. */static voidcheck_sql_expr(const char *stmt){	ErrorContextCallback  syntax_errcontext;	ErrorContextCallback *previous_errcontext;	MemoryContext oldCxt;	if (!plpgsql_check_syntax)		return;	/*	 * Setup error traceback support for ereport(). The previous	 * ereport callback is installed by pl_comp.c, but we don't want	 * that to be invoked (since it will try to transpose the syntax	 * error to be relative to the CREATE FUNCTION), so temporarily	 * remove it from the list of callbacks.	 */	Assert(error_context_stack->callback == plpgsql_compile_error_callback);	previous_errcontext = error_context_stack;	syntax_errcontext.callback = plpgsql_sql_error_callback;	syntax_errcontext.arg = (char *) stmt;	syntax_errcontext.previous = error_context_stack->previous;	error_context_stack = &syntax_errcontext;	oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);	(void) raw_parser(stmt);	MemoryContextSwitchTo(oldCxt);	/* Restore former ereport callback */	error_context_stack = previous_errcontext;}static voidplpgsql_sql_error_callback(void *arg){	char *sql_stmt = (char *) arg;	Assert(plpgsql_error_funcname);	errcontext("SQL statement in PL/PgSQL function \"%s\" near line %d",			   plpgsql_error_funcname, plpgsql_error_lineno);	internalerrquery(sql_stmt);	internalerrposition(geterrposition());	errposition(0);}static voidcheck_labels(const char *start_label, const char *end_label){	if (end_label)	{		if (!start_label)		{			plpgsql_error_lineno = plpgsql_scanner_lineno();			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("end label \"%s\" specified for unlabelled block",							end_label)));		}		if (strcmp(start_label, end_label) != 0)		{			plpgsql_error_lineno = plpgsql_scanner_lineno();			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("end label \"%s\" differs from block's label \"%s\"",							end_label, start_label)));		}	}}#include "pl_scan.c"

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -