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

📄 gram.y

📁 postgresql8.3.4源码,开源数据库
💻 Y
📖 第 1 页 / 共 4 页
字号:
		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);	type_name = plpgsql_dstring_get(&ds);	if (type_name[0] == '\0')		yyerror("missing datatype declaration");	plpgsql_error_lineno = lno;	/* in case of error in parse_datatype */	result = plpgsql_parse_datatype(type_name);	plpgsql_dstring_free(&ds);	return result;}static PLpgSQL_stmt *make_execsql_stmt(const char *sqlstart, int lineno){	PLpgSQL_dstring		ds;	int					nparams = 0;	int					params[MAX_EXPR_PARAMS];	char				buf[32];	PLpgSQL_stmt_execsql *execsql;	PLpgSQL_expr		*expr;	PLpgSQL_row			*row = NULL;	PLpgSQL_rec			*rec = NULL;	int					tok;	bool				have_into = false;	bool				have_strict = false;	plpgsql_dstring_init(&ds);	plpgsql_dstring_append(&ds, sqlstart);	for (;;)	{		tok = yylex();		if (tok == ';')			break;		if (tok == 0)			yyerror("unexpected end of function definition");		if (tok == K_INTO)		{			if (have_into)				yyerror("INTO specified more than once");			have_into = true;			read_into_target(&rec, &row, &have_strict);			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);	execsql = palloc(sizeof(PLpgSQL_stmt_execsql));	execsql->cmd_type = PLPGSQL_STMT_EXECSQL;	execsql->lineno  = lineno;	execsql->sqlstmt = expr;	execsql->into	 = have_into;	execsql->strict	 = have_strict;	execsql->rec	 = rec;	execsql->row	 = row;	return (PLpgSQL_stmt *) execsql;}static PLpgSQL_stmt_fetch *read_fetch_direction(void){	PLpgSQL_stmt_fetch *fetch;	int			tok;	bool		check_FROM = true;	/*	 * We create the PLpgSQL_stmt_fetch struct here, but only fill in	 * the fields arising from the optional direction clause	 */	fetch = (PLpgSQL_stmt_fetch *) palloc0(sizeof(PLpgSQL_stmt_fetch));	fetch->cmd_type = PLPGSQL_STMT_FETCH;	/* set direction defaults: */	fetch->direction = FETCH_FORWARD;	fetch->how_many  = 1;	fetch->expr      = NULL;	/*	 * Most of the direction keywords are not plpgsql keywords, so we	 * rely on examining yytext ...	 */	tok = yylex();	if (tok == 0)		yyerror("unexpected end of function definition");	if (pg_strcasecmp(yytext, "next") == 0)	{		/* use defaults */	}	else if (pg_strcasecmp(yytext, "prior") == 0)	{		fetch->direction = FETCH_BACKWARD;	}	else if (pg_strcasecmp(yytext, "first") == 0)	{		fetch->direction = FETCH_ABSOLUTE;	}	else if (pg_strcasecmp(yytext, "last") == 0)	{		fetch->direction = FETCH_ABSOLUTE;		fetch->how_many  = -1;	}	else if (pg_strcasecmp(yytext, "absolute") == 0)	{		fetch->direction = FETCH_ABSOLUTE;		fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",										 "SELECT ", true, true, NULL);		check_FROM = false;	}	else if (pg_strcasecmp(yytext, "relative") == 0)	{		fetch->direction = FETCH_RELATIVE;		fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",										 "SELECT ", true, true, NULL);		check_FROM = false;	}	else if (pg_strcasecmp(yytext, "forward") == 0)	{		/* use defaults */	}	else if (pg_strcasecmp(yytext, "backward") == 0)	{		fetch->direction = FETCH_BACKWARD;	}	else if (tok != T_SCALAR)	{		plpgsql_push_back_token(tok);		fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",										 "SELECT ", true, true, NULL);		check_FROM = false;	}	else	{		/* Assume there's no direction clause */		plpgsql_push_back_token(tok);		check_FROM = false;	}	/* check FROM or IN keyword after direction's specification */	if (check_FROM)	{		tok = yylex();		if (tok != K_FROM && tok != K_IN)			yyerror("expected FROM or IN");	}	return fetch;}static PLpgSQL_stmt *make_return_stmt(int lineno){	PLpgSQL_stmt_return *new;	new = palloc0(sizeof(PLpgSQL_stmt_return));	new->cmd_type = PLPGSQL_STMT_RETURN;	new->lineno   = lineno;	new->expr	  = NULL;	new->retvarno = -1;	if (plpgsql_curr_compile->fn_retset)	{		if (yylex() != ';')			yyerror("RETURN cannot have a parameter in function "					"returning set; use RETURN NEXT or RETURN QUERY");	}	else if (plpgsql_curr_compile->out_param_varno >= 0)	{		if (yylex() != ';')			yyerror("RETURN cannot have a parameter in function with OUT parameters");		new->retvarno = plpgsql_curr_compile->out_param_varno;	}	else if (plpgsql_curr_compile->fn_rettype == VOIDOID)	{		if (yylex() != ';')			yyerror("RETURN cannot have a parameter in function returning void");	}	else if (plpgsql_curr_compile->fn_retistuple)	{		switch (yylex())		{			case K_NULL:				/* we allow this to support RETURN NULL in triggers */				break;			case T_ROW:				new->retvarno = yylval.row->rowno;				break;			case T_RECORD:				new->retvarno = yylval.rec->recno;				break;			default:				yyerror("RETURN must specify a record or row variable in function returning tuple");				break;		}		if (yylex() != ';')			yyerror("RETURN must specify a record or row variable in function returning tuple");	}	else	{		/*		 * Note that a well-formed expression is		 * _required_ here; anything else is a		 * compile-time error.		 */		new->expr = plpgsql_read_expression(';', ";");	}	return (PLpgSQL_stmt *) new;}static PLpgSQL_stmt *make_return_next_stmt(int lineno){	PLpgSQL_stmt_return_next *new;	if (!plpgsql_curr_compile->fn_retset)		yyerror("cannot use RETURN NEXT in a non-SETOF function");	new = palloc0(sizeof(PLpgSQL_stmt_return_next));	new->cmd_type	= PLPGSQL_STMT_RETURN_NEXT;	new->lineno		= lineno;	new->expr		= NULL;	new->retvarno	= -1;	if (plpgsql_curr_compile->out_param_varno >= 0)	{		if (yylex() != ';')			yyerror("RETURN NEXT cannot have a parameter in function with OUT parameters");		new->retvarno = plpgsql_curr_compile->out_param_varno;	}	else if (plpgsql_curr_compile->fn_retistuple)	{		switch (yylex())		{			case T_ROW:				new->retvarno = yylval.row->rowno;				break;			case T_RECORD:				new->retvarno = yylval.rec->recno;				break;			default:				yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");				break;		}		if (yylex() != ';')			yyerror("RETURN NEXT must specify a record or row variable in function returning tuple");	}	else		new->expr = plpgsql_read_expression(';', ";");	return (PLpgSQL_stmt *) new;}static PLpgSQL_stmt *make_return_query_stmt(int lineno){	PLpgSQL_stmt_return_query *new;	if (!plpgsql_curr_compile->fn_retset)		yyerror("cannot use RETURN QUERY in a non-SETOF function");	new = palloc0(sizeof(PLpgSQL_stmt_return_query));	new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;	new->lineno = lineno;	new->query = read_sql_construct(';', 0, ")", "", false, true, NULL);	return (PLpgSQL_stmt *) new;}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;	}}/* * Read the argument of an INTO clause.  On entry, we have just read the * INTO keyword. */static voidread_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict){	int			tok;	/* Set default results */	*rec = NULL;	*row = NULL;	if (strict)		*strict = false;	tok = yylex();	if (strict && tok == K_STRICT)	{		*strict = true;		tok = yylex();	}	switch (tok)	{		case T_ROW:			*row = yylval.row;			check_assignable((PLpgSQL_datum *) *row);			break;		case T_RECORD:			*rec = yylval.rec;			check_assignable((PLpgSQL_datum *) *rec);			break;		case T_SCALAR:			*row = read_into_scalar_list(yytext, yylval.scalar);			break;		default:			plpgsql_error_lineno = plpgsql_scanner_lineno();			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),					 errmsg("syntax error at \"%s\"", yytext),					 errdetail("Expected record variable, row variable, "							   "or list of scalar variables following INTO.")));	}}/* * 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 scalar variable",								yytext)));		}	}	/*	 * We read an extra, non-comma token 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;}/* * Convert a single scalar into a "row" list.  This is exactly * like read_into_scalar_list except we never consume any input. * In fact, since this can be invoked long after the source * input was actually read, the lineno has to be passed in. */static PLpgSQL_row *make_scalar_list1(const char *initial_name,				  PLpgSQL_datum *initial_datum,				  int lineno){	PLpgSQL_row		*row;	check_assignable(initial_datum);	row = palloc(sizeof(PLpgSQL_row));	row->dtype = PLPGSQL_DTYPE_ROW;	row->refname = pstrdup("*internal*");	row->lineno = lineno;	row->rowtupdesc = NULL;	row->nfields = 1;	row->fieldnames = palloc(sizeof(char *));	row->varnos = palloc(sizeof(int));	row->fieldnames[0] = pstrdup(initial_name);	row->varnos[0] = initial_datum->dno;	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 char *check_label(const char *yytxt){	char	   *label_name;	plpgsql_convert_ident(yytxt, &label_name, 1);	if (plpgsql_ns_lookup_label(label_name) == NULL)		yyerror("no such label");	return label_name;}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)));		}	}}/* Needed to avoid conflict between different prefix settings: */#undef yylex#include "pl_scan.c"

⌨️ 快捷键说明

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