*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.133 2009/11/09 00:26:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.134 2009/11/10 02:13:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "plpgsql.h"
+#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "parser/parser.h"
#include "parser/parse_type.h"
#define parser_errposition(pos) plpgsql_scanner_errposition(pos)
-static PLpgSQL_expr *read_sql_construct(int until,
+union YYSTYPE; /* need forward reference for tok_is_keyword */
+
+static bool tok_is_keyword(int token, union YYSTYPE *lval,
+ const char *keyword);
+static void token_is_not_variable(int tok);
+static PLpgSQL_expr *read_sql_construct(int until,
int until2,
int until3,
const char *expected,
bool valid_sql,
int *startloc,
int *endtoken);
-static PLpgSQL_expr *read_sql_expression2(int until, int until2,
+static PLpgSQL_expr *read_sql_expression2(int until, int until2,
const char *expected,
int *endtoken);
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
static PLpgSQL_stmt *make_return_query_stmt(int location);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
List *case_when_list, List *else_stmts);
+static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location);
static void read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row,
bool *strict);
-static PLpgSQL_row *read_into_scalar_list(const char *initial_name,
+static PLpgSQL_row *read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
int initial_location);
-static PLpgSQL_row *make_scalar_list1(const char *initial_name,
+static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno, int location);
static void check_sql_expr(const char *stmt, int location,
int leaderlen);
static void plpgsql_sql_error_callback(void *arg);
-static PLpgSQL_type *parse_datatype(const char *string, int location);
+static PLpgSQL_type *parse_datatype(const char *string, int location);
static char *parse_string_token(const char *token, int location);
-static char *check_label(const char *yytxt);
static void check_labels(const char *start_label,
const char *end_label,
int end_location);
-static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
+static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
int until, const char *expected);
-static List *read_raise_options(void);
+static List *read_raise_options(void);
%}
%locations
%union {
- int32 ival;
- bool boolean;
+ /* these fields must match core_YYSTYPE: */
+ int ival;
char *str;
+ const char *keyword;
+
+ PLword word;
+ PLcword cword;
+ PLwdatum wdatum;
+ bool boolean;
struct
{
char *name;
%type <forvariable> for_variable
%type <stmt> for_control
-%type <str> any_identifier any_name opt_block_label opt_label
+%type <str> any_identifier opt_block_label opt_label
%type <list> proc_sect proc_stmts stmt_else
%type <loop_body> loop_body
%type <list> getdiag_list
%type <diagitem> getdiag_list_item
-%type <ival> getdiag_kind getdiag_target
+%type <ival> getdiag_item getdiag_target
%type <ival> opt_scrollable
%type <fetch> opt_fetch_direction
- /*
- * Keyword tokens
- */
+/*
+ * Basic non-keyword token types. These are hard-wired into the core lexer.
+ * They must be listed first so that their numeric codes do not depend on
+ * the set of keywords. Keep this list in sync with backend/parser/gram.y!
+ *
+ * Some of these are not directly referenced in this file, but they must be
+ * here anyway.
+ */
+%token <str> IDENT FCONST SCONST BCONST XCONST Op
+%token <ival> ICONST PARAM
+%token TYPECAST DOT_DOT COLON_EQUALS
+
+/*
+ * Other tokens recognized by plpgsql's lexer interface layer.
+ */
+%token T_STRING
+%token T_NUMBER
+%token <word> T_WORD /* unrecognized simple identifier */
+%token <cword> T_CWORD /* unrecognized composite identifier */
+%token <wdatum> T_DATUM /* a VAR, ROW, REC, or RECFIELD variable */
+
+%token O_OPTION
+%token O_DUMP
+
+/*
+ * Keyword tokens
+ */
%token K_ALIAS
%token K_ALL
%token K_ASSIGN
%token K_OPEN
%token K_OR
%token K_PERFORM
-%token K_ROW_COUNT
%token K_RAISE
-%token K_RESULT_OID
%token K_RETURN
-%token K_REVERSE
%token K_SCROLL
%token K_STRICT
%token K_THEN
%token K_TO
-%token K_TYPE
%token K_USING
%token K_WHEN
%token K_WHILE
- /*
- * Other tokens
- */
-%token T_STRING
-%token T_NUMBER
-%token T_DATUM /* a VAR, ROW, REC, or RECFIELD variable */
-%token T_WORD /* unrecognized simple identifier */
-%token T_DBLWORD /* unrecognized ident.ident */
-%token T_TRIPWORD /* unrecognized ident.ident.ident */
-
-%token O_OPTION
-%token O_DUMP
-
%%
pl_function : comp_optsect pl_block opt_semi
{ $$ = $1; }
;
-decl_stmt : '<' '<' any_name '>' '>'
+decl_stmt : '<' '<' any_identifier '>' '>'
{ $$ = $3; }
| K_DECLARE
{ $$ = NULL; }
decl_aliasitem : T_WORD
{
- char *name[1];
PLpgSQL_nsitem *nsi;
- plpgsql_convert_ident(yytext, name, 1);
-
nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- name[0], NULL, NULL,
+ $1.ident, NULL, NULL,
NULL);
if (nsi == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("variable \"%s\" does not exist",
- name[0]),
+ $1.ident),
parser_errposition(@1)));
-
- pfree(name[0]);
-
$$ = nsi;
}
- | T_DBLWORD
+ | T_CWORD
{
- char *name[2];
PLpgSQL_nsitem *nsi;
- plpgsql_convert_ident(yytext, name, 2);
-
- nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- name[0], name[1], NULL,
- NULL);
+ if (list_length($1.idents) == 2)
+ nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
+ strVal(linitial($1.idents)),
+ strVal(lsecond($1.idents)),
+ NULL,
+ NULL);
+ else if (list_length($1.idents) == 3)
+ nsi = plpgsql_ns_lookup(plpgsql_ns_top(), false,
+ strVal(linitial($1.idents)),
+ strVal(lsecond($1.idents)),
+ strVal(lthird($1.idents)),
+ NULL);
+ else
+ nsi = NULL;
if (nsi == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("variable \"%s.%s\" does not exist",
- name[0], name[1]),
+ errmsg("variable \"%s\" does not exist",
+ NameListToString($1.idents)),
parser_errposition(@1)));
-
- pfree(name[0]);
- pfree(name[1]);
-
$$ = nsi;
}
;
decl_varname : T_WORD
{
- char *name;
-
- plpgsql_convert_ident(yytext, &name, 1);
- $$.name = name;
+ $$.name = $1.ident;
$$.lineno = plpgsql_location_to_lineno(@1);
/*
* Check to make sure name isn't already declared
* in the current block.
*/
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
- name, NULL, NULL,
+ $1.ident, NULL, NULL,
NULL) != NULL)
yyerror("duplicate declaration");
}
}
;
-getdiag_list_item : getdiag_target K_ASSIGN getdiag_kind
+getdiag_list_item : getdiag_target K_ASSIGN getdiag_item
{
PLpgSQL_diag_item *new;
}
;
-getdiag_kind : K_ROW_COUNT
+getdiag_item :
{
- $$ = PLPGSQL_GETDIAG_ROW_COUNT;
- }
- | K_RESULT_OID
- {
- $$ = PLPGSQL_GETDIAG_RESULT_OID;
+ int tok = yylex();
+
+ if (tok_is_keyword(tok, &yylval, "row_count"))
+ $$ = PLPGSQL_GETDIAG_ROW_COUNT;
+ else if (tok_is_keyword(tok, &yylval, "result_oid"))
+ $$ = PLPGSQL_GETDIAG_RESULT_OID;
+ else
+ yyerror("unrecognized GET DIAGNOSTICS item");
}
;
getdiag_target : T_DATUM
{
- check_assignable(yylval.datum, @1);
- if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.datum->dtype == PLPGSQL_DTYPE_REC)
+ check_assignable($1.datum, @1);
+ if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ $1.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
- yytext),
+ NameOfDatum(&($1))),
parser_errposition(@1)));
- $$ = yylval.datum->dno;
+ $$ = $1.datum->dno;
}
| T_WORD
{
/* just to give a better message than "syntax error" */
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("\"%s\" is not a known variable",
- yytext),
- parser_errposition(@1)));
+ token_is_not_variable(T_WORD);
+ }
+ | T_CWORD
+ {
+ /* just to give a better message than "syntax error" */
+ token_is_not_variable(T_CWORD);
}
;
assign_var : T_DATUM
{
- check_assignable(yylval.datum, @1);
- $$ = yylval.datum->dno;
+ check_assignable($1.datum, @1);
+ $$ = $1.datum->dno;
}
| assign_var '[' expr_until_rightbracket
{
$$ = (PLpgSQL_stmt *) new;
}
else if (tok == T_DATUM &&
- yylval.datum->dtype == PLPGSQL_DTYPE_VAR &&
- ((PLpgSQL_var *) yylval.datum)->datatype->typoid == REFCURSOROID)
+ yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR &&
+ ((PLpgSQL_var *) yylval.wdatum.datum)->datatype->typoid == REFCURSOROID)
{
/* It's FOR var IN cursor */
PLpgSQL_stmt_forc *new;
- PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.datum;
- char *varname;
+ PLpgSQL_var *cursor = (PLpgSQL_var *) yylval.wdatum.datum;
new = (PLpgSQL_stmt_forc *) palloc0(sizeof(PLpgSQL_stmt_forc));
new->cmd_type = PLPGSQL_STMT_FORC;
"LOOP");
/* create loop's private RECORD variable */
- plpgsql_convert_ident($1.name, &varname, 1);
- new->rec = plpgsql_build_record(varname,
+ new->rec = plpgsql_build_record($1.name,
$1.lineno,
true);
* keyword, which means it must be an
* integer loop.
*/
- if (tok == K_REVERSE)
+ if (tok_is_keyword(tok, &yylval, "reverse"))
reverse = true;
else
plpgsql_push_back_token(tok);
PLpgSQL_expr *expr_by;
PLpgSQL_var *fvar;
PLpgSQL_stmt_fori *new;
- char *varname;
/* Check first expression is well-formed */
check_sql_expr(expr1->query, expr1loc, 7);
parser_errposition(@1)));
/* create loop's private variable */
- plpgsql_convert_ident($1.name, &varname, 1);
fvar = (PLpgSQL_var *)
- plpgsql_build_variable(varname,
+ plpgsql_build_variable($1.name,
$1.lineno,
plpgsql_build_datatype(INT4OID,
-1),
*/
for_variable : T_DATUM
{
- $$.name = pstrdup(yytext);
+ $$.name = NameOfDatum(&($1));
$$.lineno = plpgsql_location_to_lineno(@1);
- if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW)
+ if ($1.datum->dtype == PLPGSQL_DTYPE_ROW)
{
$$.scalar = NULL;
$$.rec = NULL;
- $$.row = (PLpgSQL_row *) yylval.datum;
+ $$.row = (PLpgSQL_row *) $1.datum;
}
- else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC)
+ else if ($1.datum->dtype == PLPGSQL_DTYPE_REC)
{
$$.scalar = NULL;
- $$.rec = (PLpgSQL_rec *) yylval.datum;
+ $$.rec = (PLpgSQL_rec *) $1.datum;
$$.row = NULL;
}
else
{
int tok;
- $$.scalar = yylval.datum;
+ $$.scalar = $1.datum;
$$.rec = NULL;
$$.row = NULL;
/* check for comma-separated list */
{
int tok;
- $$.name = pstrdup(yytext);
+ $$.name = $1.ident;
$$.lineno = plpgsql_location_to_lineno(@1);
$$.scalar = NULL;
$$.rec = NULL;
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == ',')
+ {
+ /* can't use token_is_not_variable here */
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a known variable",
- $$.name),
+ $1.ident),
parser_errposition(@1)));
+ }
+ }
+ | T_CWORD
+ {
+ /* just to give a better message than "syntax error" */
+ token_is_not_variable(T_CWORD);
}
;
if (tok == 0)
yyerror("unexpected end of function definition");
- /*
- * To avoid making NEXT and QUERY effectively be
- * reserved words within plpgsql, recognize them
- * via yytext.
- */
- if (pg_strcasecmp(yytext, "next") == 0)
+ if (tok_is_keyword(tok, &yylval, "next"))
{
$$ = make_return_next_stmt(@1);
}
- else if (pg_strcasecmp(yytext, "query") == 0)
+ else if (tok_is_keyword(tok, &yylval, "query"))
{
$$ = make_return_query_stmt(@1);
}
{
/*
* First is an optional elog severity level.
- * Most of these are not plpgsql keywords,
- * so we rely on examining yytext.
*/
- if (pg_strcasecmp(yytext, "exception") == 0)
+ if (tok == K_EXCEPTION)
{
new->elog_level = ERROR;
tok = yylex();
}
- else if (pg_strcasecmp(yytext, "warning") == 0)
+ else if (tok_is_keyword(tok, &yylval, "warning"))
{
new->elog_level = WARNING;
tok = yylex();
}
- else if (pg_strcasecmp(yytext, "notice") == 0)
+ else if (tok_is_keyword(tok, &yylval, "notice"))
{
new->elog_level = NOTICE;
tok = yylex();
}
- else if (pg_strcasecmp(yytext, "info") == 0)
+ else if (tok_is_keyword(tok, &yylval, "info"))
{
new->elog_level = INFO;
tok = yylex();
}
- else if (pg_strcasecmp(yytext, "log") == 0)
+ else if (tok_is_keyword(tok, &yylval, "log"))
{
new->elog_level = LOG;
tok = yylex();
}
- else if (pg_strcasecmp(yytext, "debug") == 0)
+ else if (tok_is_keyword(tok, &yylval, "debug"))
{
new->elog_level = DEBUG1;
tok = yylex();
else if (tok != K_USING)
{
/* must be condition name or SQLSTATE */
- if (pg_strcasecmp(yytext, "sqlstate") == 0)
+ if (tok_is_keyword(tok, &yylval, "sqlstate"))
{
/* next token should be a string literal */
char *sqlstatestr;
}
else
{
- char *cname;
-
if (tok != T_WORD)
yyerror("syntax error");
- plpgsql_convert_ident(yytext, &cname, 1);
- plpgsql_recognize_err_condition(cname,
+ new->condname = yylval.word.ident;
+ plpgsql_recognize_err_condition(new->condname,
false);
- new->condname = cname;
}
tok = yylex();
if (tok != ';' && tok != K_USING)
;
/*
- * T_WORD+T_DBLWORD+T_TRIPWORD match any initial identifier that is not a
- * known plpgsql variable. The latter two cases are probably syntax errors,
- * but we'll let the core parser decide that.
+ * T_WORD+T_CWORD match any initial identifier that is not a known plpgsql
+ * variable. The composite case is probably a syntax error, but we'll let
+ * the core parser decide that.
*/
stmt_execsql : K_INSERT
{ $$ = make_execsql_stmt(K_INSERT, @1); }
| T_WORD
{ $$ = make_execsql_stmt(T_WORD, @1); }
- | T_DBLWORD
- { $$ = make_execsql_stmt(T_DBLWORD, @1); }
- | T_TRIPWORD
- { $$ = make_execsql_stmt(T_TRIPWORD, @1); }
+ | T_CWORD
+ { $$ = make_execsql_stmt(T_CWORD, @1); }
;
stmt_dynexecute : K_EXECUTE
}
if (tok != K_FOR)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("syntax error at \"%s\"",
- yytext),
- errdetail("Expected \"FOR\", to open a cursor for an unbound cursor variable."),
- parser_errposition(yylloc)));
+ yyerror("syntax error, expected \"FOR\"");
tok = yylex();
if (tok == K_EXECUTE)
cursor_variable : T_DATUM
{
- if (yylval.datum->dtype != PLPGSQL_DTYPE_VAR)
+ if ($1.datum->dtype != PLPGSQL_DTYPE_VAR)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cursor variable must be a simple variable"),
parser_errposition(@1)));
- if (((PLpgSQL_var *) yylval.datum)->datatype->typoid != REFCURSOROID)
+ if (((PLpgSQL_var *) $1.datum)->datatype->typoid != REFCURSOROID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("variable \"%s\" must be of type cursor or refcursor",
- ((PLpgSQL_var *) yylval.datum)->refname),
+ ((PLpgSQL_var *) $1.datum)->refname),
parser_errposition(@1)));
- $$ = (PLpgSQL_var *) yylval.datum;
+ $$ = (PLpgSQL_var *) $1.datum;
}
| T_WORD
{
/* just to give a better message than "syntax error" */
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("\"%s\" is not a known variable",
- yytext),
- parser_errposition(@1)));
+ token_is_not_variable(T_WORD);
+ }
+ | T_CWORD
+ {
+ /* just to give a better message than "syntax error" */
+ token_is_not_variable(T_CWORD);
}
;
}
;
-proc_condition : any_name
+proc_condition : any_identifier
{
if (strcmp($1, "sqlstate") != 0)
{
plpgsql_ns_push(NULL);
$$ = NULL;
}
- | '<' '<' any_name '>' '>'
+ | '<' '<' any_identifier '>' '>'
{
plpgsql_ns_push($3);
$$ = $3;
}
| any_identifier
{
- $$ = check_label($1);
+ if (plpgsql_ns_lookup_label(plpgsql_ns_top(), $1) == NULL)
+ yyerror("label does not exist");
+ $$ = $1;
}
;
*/
any_identifier : T_WORD
{
- $$ = yytext;
+ $$ = $1.ident;
}
| T_DATUM
{
- $$ = yytext;
- }
- ;
-
-any_name : any_identifier
- {
- char *name;
-
- plpgsql_convert_ident($1, &name, 1);
- $$ = name;
+ if ($1.ident == NULL) /* composite name not OK */
+ yyerror("syntax error");
+ $$ = $1.ident;
}
;
%%
+/*
+ * Check whether a token represents an "unreserved keyword".
+ * We have various places where we want to recognize a keyword in preference
+ * to a variable name, but not reserve that keyword in other contexts.
+ * Hence, this kluge. CAUTION: don't use this for reserved keywords;
+ * it won't recognize them.
+ */
+static bool
+tok_is_keyword(int token, union YYSTYPE *lval, const char *keyword)
+{
+ if (token == T_WORD)
+ {
+ /* must be unquoted and match the downcased string */
+ if (!lval->word.quoted && strcmp(lval->word.ident, keyword) == 0)
+ return true;
+ }
+ else if (token == T_DATUM)
+ {
+ /* like the T_WORD case, but also reject composite identifiers */
+ /* (hence an unreserved word followed by "." will not be recognized) */
+ if (!lval->word.quoted && lval->word.ident != NULL &&
+ strcmp(lval->word.ident, keyword) == 0)
+ return true;
+ }
+ return false; /* not the keyword */
+}
+
+/*
+ * Convenience routine to complain when we expected T_DATUM and got
+ * something else. "tok" must be the current token, since we also
+ * look at yylval and yylloc.
+ */
+static void
+token_is_not_variable(int tok)
+{
+ if (tok == T_WORD)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("\"%s\" is not a known variable",
+ yylval.word.ident),
+ parser_errposition(yylloc)));
+ else if (tok == T_CWORD)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("\"%s\" is not a known variable",
+ NameListToString(yylval.cword.idents)),
+ parser_errposition(yylloc)));
+ else
+ yyerror("syntax error");
+}
/* Convenience routine to read an expression with one possible terminator */
PLpgSQL_expr *
/* Should always be called with LookupIdentifiers off */
Assert(!plpgsql_LookupIdentifiers);
- initStringInfo(&ds);
-
/* Often there will be a lookahead token, but if not, get one */
if (tok == YYEMPTY)
tok = yylex();
startlocation = yylloc;
/*
- * If we have a single, double, or triple identifier, check for %TYPE
+ * If we have a simple or composite identifier, check for %TYPE
* and %ROWTYPE constructs.
*/
if (tok == T_WORD)
{
- appendStringInfoString(&ds, yytext);
+ char *dtname = yylval.word.ident;
+
tok = yylex();
if (tok == '%')
{
tok = yylex();
- if (pg_strcasecmp(yytext, "type") == 0)
+ if (tok_is_keyword(tok, &yylval, "type"))
{
- result = plpgsql_parse_wordtype(ds.data);
+ result = plpgsql_parse_wordtype(dtname);
if (result)
- {
- pfree(ds.data);
return result;
- }
}
- else if (pg_strcasecmp(yytext, "rowtype") == 0)
+ else if (tok_is_keyword(tok, &yylval, "rowtype"))
{
- result = plpgsql_parse_wordrowtype(ds.data);
+ result = plpgsql_parse_wordrowtype(dtname);
if (result)
- {
- pfree(ds.data);
return result;
- }
}
}
}
- else if (tok == T_DBLWORD)
+ else if (tok == T_CWORD)
{
- appendStringInfoString(&ds, yytext);
+ List *dtnames = yylval.cword.idents;
+
tok = yylex();
if (tok == '%')
{
tok = yylex();
- if (pg_strcasecmp(yytext, "type") == 0)
- {
- result = plpgsql_parse_dblwordtype(ds.data);
- if (result)
- {
- pfree(ds.data);
- return result;
- }
- }
- else if (pg_strcasecmp(yytext, "rowtype") == 0)
+ if (tok_is_keyword(tok, &yylval, "type"))
{
- result = plpgsql_parse_dblwordrowtype(ds.data);
+ result = plpgsql_parse_cwordtype(dtnames);
if (result)
- {
- pfree(ds.data);
return result;
- }
}
- }
- }
- else if (tok == T_TRIPWORD)
- {
- appendStringInfoString(&ds, yytext);
- tok = yylex();
- if (tok == '%')
- {
- tok = yylex();
- if (pg_strcasecmp(yytext, "type") == 0)
+ else if (tok_is_keyword(tok, &yylval, "rowtype"))
{
- result = plpgsql_parse_tripwordtype(ds.data);
+ result = plpgsql_parse_cwordrowtype(dtnames);
if (result)
- {
- pfree(ds.data);
return result;
- }
}
- /* there's no tripword rowtype construct */
}
}
- /* flush temporary usage of ds for rowtype checks */
- resetStringInfo(&ds);
-
while (tok != ';')
{
if (tok == 0)
}
/* set up ds to contain complete typename text */
+ initStringInfo(&ds);
plpgsql_append_source_text(&ds, startlocation, yylloc);
type_name = ds.data;
fetch->expr = NULL;
fetch->returns_multiple_rows = false;
- /*
- * 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)
+ if (tok_is_keyword(tok, &yylval, "next"))
{
/* use defaults */
}
- else if (pg_strcasecmp(yytext, "prior") == 0)
+ else if (tok_is_keyword(tok, &yylval, "prior"))
{
fetch->direction = FETCH_BACKWARD;
}
- else if (pg_strcasecmp(yytext, "first") == 0)
+ else if (tok_is_keyword(tok, &yylval, "first"))
{
fetch->direction = FETCH_ABSOLUTE;
}
- else if (pg_strcasecmp(yytext, "last") == 0)
+ else if (tok_is_keyword(tok, &yylval, "last"))
{
fetch->direction = FETCH_ABSOLUTE;
fetch->how_many = -1;
}
- else if (pg_strcasecmp(yytext, "absolute") == 0)
+ else if (tok_is_keyword(tok, &yylval, "absolute"))
{
fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
NULL);
check_FROM = false;
}
- else if (pg_strcasecmp(yytext, "relative") == 0)
+ else if (tok_is_keyword(tok, &yylval, "relative"))
{
fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
NULL);
check_FROM = false;
}
- else if (pg_strcasecmp(yytext, "all") == 0)
+ else if (tok == K_ALL)
{
fetch->how_many = FETCH_ALL;
fetch->returns_multiple_rows = true;
}
- else if (pg_strcasecmp(yytext, "forward") == 0)
+ else if (tok_is_keyword(tok, &yylval, "forward"))
{
complete_direction(fetch, &check_FROM);
}
- else if (pg_strcasecmp(yytext, "backward") == 0)
+ else if (tok_is_keyword(tok, &yylval, "backward"))
{
fetch->direction = FETCH_BACKWARD;
complete_direction(fetch, &check_FROM);
break;
case T_DATUM:
- if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.datum->dtype == PLPGSQL_DTYPE_REC)
- new->retvarno = yylval.datum->dno;
+ if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
+ new->retvarno = yylval.wdatum.datum->dno;
else
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
switch (yylex())
{
case T_DATUM:
- if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.datum->dtype == PLPGSQL_DTYPE_REC)
- new->retvarno = yylval.datum->dno;
+ if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
+ new->retvarno = yylval.wdatum.datum->dno;
else
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
}
+/* convenience routine to fetch the name of a T_DATUM */
+static char *
+NameOfDatum(PLwdatum *wdatum)
+{
+ if (wdatum->ident)
+ return wdatum->ident;
+ Assert(wdatum->idents != NIL);
+ return NameListToString(wdatum->idents);
+}
+
static void
check_assignable(PLpgSQL_datum *datum, int location)
{
switch (tok)
{
case T_DATUM:
- if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW)
+ if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW)
{
- check_assignable(yylval.datum, yylloc);
- *row = (PLpgSQL_row *) yylval.datum;
+ check_assignable(yylval.wdatum.datum, yylloc);
+ *row = (PLpgSQL_row *) yylval.wdatum.datum;
}
- else if (yylval.datum->dtype == PLPGSQL_DTYPE_REC)
+ else if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
{
- check_assignable(yylval.datum, yylloc);
- *rec = (PLpgSQL_rec *) yylval.datum;
+ check_assignable(yylval.wdatum.datum, yylloc);
+ *rec = (PLpgSQL_rec *) yylval.wdatum.datum;
}
else
{
- *row = read_into_scalar_list(yytext, yylval.datum, yylloc);
+ *row = read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
+ yylval.wdatum.datum, yylloc);
}
break;
default:
- 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."),
- parser_errposition(yylloc)));
+ /* just to give a better message than "syntax error" */
+ token_is_not_variable(tok);
}
}
* scalars.
*/
static PLpgSQL_row *
-read_into_scalar_list(const char *initial_name,
+read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
int initial_location)
{
int tok;
check_assignable(initial_datum, initial_location);
- fieldnames[0] = pstrdup(initial_name);
+ fieldnames[0] = initial_name;
varnos[0] = initial_datum->dno;
nfields = 1;
switch (tok)
{
case T_DATUM:
- check_assignable(yylval.datum, yylloc);
- if (yylval.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.datum->dtype == PLPGSQL_DTYPE_REC)
+ check_assignable(yylval.wdatum.datum, yylloc);
+ if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
- yytext),
+ NameOfDatum(&(yylval.wdatum))),
parser_errposition(yylloc)));
- fieldnames[nfields] = pstrdup(yytext);
- varnos[nfields++] = yylval.datum->dno;
+ fieldnames[nfields] = NameOfDatum(&(yylval.wdatum));
+ varnos[nfields++] = yylval.wdatum.datum->dno;
break;
default:
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("\"%s\" is not a known variable",
- yytext),
- parser_errposition(yylloc)));
+ /* just to give a better message than "syntax error" */
+ token_is_not_variable(tok);
}
}
* have it at hand already, we may as well pass it in.
*/
static PLpgSQL_row *
-make_scalar_list1(const char *initial_name,
+make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno, int location)
{
row->nfields = 1;
row->fieldnames = palloc(sizeof(char *));
row->varnos = palloc(sizeof(int));
- row->fieldnames[0] = pstrdup(initial_name);
+ row->fieldnames[0] = initial_name;
row->varnos[0] = initial_datum->dno;
plpgsql_adddatum((PLpgSQL_datum *)row);
return result;
}
-static char *
-check_label(const char *yytxt)
-{
- char *label_name;
-
- plpgsql_convert_ident(yytxt, &label_name, 1);
- if (plpgsql_ns_lookup_label(plpgsql_ns_top(), label_name) == NULL)
- yyerror("label does not exist");
- return label_name;
-}
-
+/*
+ * Check block starting and ending labels match.
+ */
static void
check_labels(const char *start_label, const char *end_label, int end_location)
{
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
- if (pg_strcasecmp(yytext, "errcode") == 0)
+ if (tok_is_keyword(tok, &yylval, "errcode"))
opt->opt_type = PLPGSQL_RAISEOPTION_ERRCODE;
- else if (pg_strcasecmp(yytext, "message") == 0)
+ else if (tok_is_keyword(tok, &yylval, "message"))
opt->opt_type = PLPGSQL_RAISEOPTION_MESSAGE;
- else if (pg_strcasecmp(yytext, "detail") == 0)
+ else if (tok_is_keyword(tok, &yylval, "detail"))
opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL;
- else if (pg_strcasecmp(yytext, "hint") == 0)
+ else if (tok_is_keyword(tok, &yylval, "hint"))
opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
else
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unrecognized RAISE statement option \"%s\"",
- yytext),
- parser_errposition(yylloc)));
+ yyerror("unrecognized RAISE statement option");
if (yylex() != K_ASSIGN)
yyerror("syntax error, expected \"=\"");
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.143 2009/11/09 00:26:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.144 2009/11/10 02:13:13 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1);
- /*
- * Do a lookup in the current namespace stack
- */
- nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- cp[0], NULL, NULL,
- NULL);
- pfree(cp[0]);
-
- if (nse != NULL)
+ /* No lookup if disabled */
+ if (plpgsql_LookupIdentifiers)
{
- switch (nse->itemtype)
+ /*
+ * Do a lookup in the current namespace stack
+ */
+ nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
+ cp[0], NULL, NULL,
+ NULL);
+
+ if (nse != NULL)
{
- case PLPGSQL_NSTYPE_VAR:
- case PLPGSQL_NSTYPE_ROW:
- case PLPGSQL_NSTYPE_REC:
- plpgsql_yylval.datum = plpgsql_Datums[nse->itemno];
- return T_DATUM;
+ switch (nse->itemtype)
+ {
+ case PLPGSQL_NSTYPE_VAR:
+ case PLPGSQL_NSTYPE_ROW:
+ case PLPGSQL_NSTYPE_REC:
+ plpgsql_yylval.wdatum.datum = plpgsql_Datums[nse->itemno];
+ plpgsql_yylval.wdatum.ident = cp[0];
+ plpgsql_yylval.wdatum.quoted = (word[0] == '"');
+ plpgsql_yylval.wdatum.idents = NIL;
+ return T_DATUM;
- default:
- elog(ERROR, "unrecognized plpgsql itemtype: %d", nse->itemtype);
+ default:
+ elog(ERROR, "unrecognized plpgsql itemtype: %d",
+ nse->itemtype);
+ }
}
}
* Nothing found - up to now it's a word without any special meaning for
* us.
*/
+ plpgsql_yylval.word.ident = cp[0];
+ plpgsql_yylval.word.quoted = (word[0] == '"');
return T_WORD;
}
{
PLpgSQL_nsitem *ns;
char *cp[2];
+ List *idents;
int nnames;
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 2);
- /*
- * Do a lookup in the current namespace stack
- */
- ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- cp[0], cp[1], NULL,
- &nnames);
- if (ns == NULL)
- {
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DBLWORD;
- }
+ idents = list_make2(makeString(cp[0]),
+ makeString(cp[1]));
- switch (ns->itemtype)
+ /* No lookup if disabled */
+ if (plpgsql_LookupIdentifiers)
{
- case PLPGSQL_NSTYPE_VAR:
- /* Block-qualified reference to scalar variable. */
- plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DATUM;
-
- case PLPGSQL_NSTYPE_REC:
- if (nnames == 1)
+ /*
+ * Do a lookup in the current namespace stack
+ */
+ ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
+ cp[0], cp[1], NULL,
+ &nnames);
+ if (ns != NULL)
+ {
+ switch (ns->itemtype)
{
- /*
- * First word is a record name, so second word must be a field
- * in this record.
- */
- PLpgSQL_recfield *new;
-
- new = palloc(sizeof(PLpgSQL_recfield));
- new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = pstrdup(cp[1]);
- new->recparentno = ns->itemno;
-
- plpgsql_adddatum((PLpgSQL_datum *) new);
+ case PLPGSQL_NSTYPE_VAR:
+ /* Block-qualified reference to scalar variable. */
+ plpgsql_yylval.wdatum.datum = plpgsql_Datums[ns->itemno];
+ plpgsql_yylval.wdatum.ident = NULL;
+ plpgsql_yylval.wdatum.quoted = false; /* not used */
+ plpgsql_yylval.wdatum.idents = idents;
+ return T_DATUM;
+
+ case PLPGSQL_NSTYPE_REC:
+ if (nnames == 1)
+ {
+ /*
+ * First word is a record name, so second word must be
+ * a field in this record.
+ */
+ PLpgSQL_recfield *new;
- plpgsql_yylval.datum = (PLpgSQL_datum *) new;
+ new = palloc(sizeof(PLpgSQL_recfield));
+ new->dtype = PLPGSQL_DTYPE_RECFIELD;
+ new->fieldname = pstrdup(cp[1]);
+ new->recparentno = ns->itemno;
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DATUM;
- }
- else
- {
- /* Block-qualified reference to record variable. */
- plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DATUM;
- }
+ plpgsql_adddatum((PLpgSQL_datum *) new);
- case PLPGSQL_NSTYPE_ROW:
- if (nnames == 1)
- {
- /*
- * First word is a row name, so second word must be a field in
- * this row.
- */
- PLpgSQL_row *row;
- int i;
+ plpgsql_yylval.wdatum.datum = (PLpgSQL_datum *) new;
+ }
+ else
+ {
+ /* Block-qualified reference to record variable. */
+ plpgsql_yylval.wdatum.datum = plpgsql_Datums[ns->itemno];
+ }
+ plpgsql_yylval.wdatum.ident = NULL;
+ plpgsql_yylval.wdatum.quoted = false; /* not used */
+ plpgsql_yylval.wdatum.idents = idents;
+ return T_DATUM;
- row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
- for (i = 0; i < row->nfields; i++)
- {
- if (row->fieldnames[i] &&
- strcmp(row->fieldnames[i], cp[1]) == 0)
+ case PLPGSQL_NSTYPE_ROW:
+ if (nnames == 1)
+ {
+ /*
+ * First word is a row name, so second word must be a
+ * field in this row.
+ */
+ PLpgSQL_row *row;
+ int i;
+
+ row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
+ for (i = 0; i < row->nfields; i++)
+ {
+ if (row->fieldnames[i] &&
+ strcmp(row->fieldnames[i], cp[1]) == 0)
+ {
+ plpgsql_yylval.wdatum.datum = plpgsql_Datums[row->varnos[i]];
+ plpgsql_yylval.wdatum.ident = NULL;
+ plpgsql_yylval.wdatum.quoted = false; /* not used */
+ plpgsql_yylval.wdatum.idents = idents;
+ return T_DATUM;
+ }
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("row \"%s\" has no field \"%s\"",
+ cp[0], cp[1])));
+ }
+ else
{
- plpgsql_yylval.datum = plpgsql_Datums[row->varnos[i]];
- pfree(cp[0]);
- pfree(cp[1]);
+ /* Block-qualified reference to row variable. */
+ plpgsql_yylval.wdatum.datum = plpgsql_Datums[ns->itemno];
+ plpgsql_yylval.wdatum.ident = NULL;
+ plpgsql_yylval.wdatum.quoted = false; /* not used */
+ plpgsql_yylval.wdatum.idents = idents;
return T_DATUM;
}
- }
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("row \"%s\" has no field \"%s\"",
- cp[0], cp[1])));
- }
- else
- {
- /* Block-qualified reference to row variable. */
- plpgsql_yylval.datum = plpgsql_Datums[ns->itemno];
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DATUM;
- }
- default:
- break;
+ default:
+ break;
+ }
+ }
}
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DBLWORD;
+ /* Nothing found */
+ plpgsql_yylval.cword.idents = idents;
+ return T_CWORD;
}
{
PLpgSQL_nsitem *ns;
char *cp[3];
+ List *idents;
int nnames;
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 3);
- /*
- * Do a lookup in the current namespace stack. Must find a qualified
- * reference.
- */
- ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- cp[0], cp[1], cp[2],
- &nnames);
- if (ns == NULL || nnames != 2)
- {
- pfree(cp[0]);
- pfree(cp[1]);
- pfree(cp[2]);
- return T_TRIPWORD;
- }
+ idents = list_make3(makeString(cp[0]),
+ makeString(cp[1]),
+ makeString(cp[2]));
- switch (ns->itemtype)
+ /* No lookup if disabled */
+ if (plpgsql_LookupIdentifiers)
{
- case PLPGSQL_NSTYPE_REC:
- {
- /*
- * words 1/2 are a record name, so third word must be a field
- * in this record.
- */
- PLpgSQL_recfield *new;
-
- new = palloc(sizeof(PLpgSQL_recfield));
- new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = pstrdup(cp[2]);
- new->recparentno = ns->itemno;
-
- plpgsql_adddatum((PLpgSQL_datum *) new);
-
- plpgsql_yylval.datum = (PLpgSQL_datum *) new;
-
- pfree(cp[0]);
- pfree(cp[1]);
- pfree(cp[2]);
-
- return T_DATUM;
- }
-
- case PLPGSQL_NSTYPE_ROW:
+ /*
+ * Do a lookup in the current namespace stack. Must find a qualified
+ * reference, else ignore.
+ */
+ ns = plpgsql_ns_lookup(plpgsql_ns_top(), false,
+ cp[0], cp[1], cp[2],
+ &nnames);
+ if (ns != NULL && nnames == 2)
+ {
+ switch (ns->itemtype)
{
- /*
- * words 1/2 are a row name, so third word must be a field in
- * this row.
- */
- PLpgSQL_row *row;
- int i;
+ case PLPGSQL_NSTYPE_REC:
+ {
+ /*
+ * words 1/2 are a record name, so third word must be a
+ * field in this record.
+ */
+ PLpgSQL_recfield *new;
+
+ new = palloc(sizeof(PLpgSQL_recfield));
+ new->dtype = PLPGSQL_DTYPE_RECFIELD;
+ new->fieldname = pstrdup(cp[2]);
+ new->recparentno = ns->itemno;
+
+ plpgsql_adddatum((PLpgSQL_datum *) new);
+
+ plpgsql_yylval.wdatum.datum = (PLpgSQL_datum *) new;
+ plpgsql_yylval.wdatum.ident = NULL;
+ plpgsql_yylval.wdatum.quoted = false; /* not used */
+ plpgsql_yylval.wdatum.idents = idents;
+ return T_DATUM;
+ }
- row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
- for (i = 0; i < row->nfields; i++)
+ case PLPGSQL_NSTYPE_ROW:
{
- if (row->fieldnames[i] &&
- strcmp(row->fieldnames[i], cp[2]) == 0)
+ /*
+ * words 1/2 are a row name, so third word must be a field
+ * in this row.
+ */
+ PLpgSQL_row *row;
+ int i;
+
+ row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
+ for (i = 0; i < row->nfields; i++)
{
- plpgsql_yylval.datum = plpgsql_Datums[row->varnos[i]];
-
- pfree(cp[0]);
- pfree(cp[1]);
- pfree(cp[2]);
-
- return T_DATUM;
+ if (row->fieldnames[i] &&
+ strcmp(row->fieldnames[i], cp[2]) == 0)
+ {
+ plpgsql_yylval.wdatum.datum = plpgsql_Datums[row->varnos[i]];
+ plpgsql_yylval.wdatum.ident = NULL;
+ plpgsql_yylval.wdatum.quoted = false; /* not used */
+ plpgsql_yylval.wdatum.idents = idents;
+ return T_DATUM;
+ }
}
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("row \"%s.%s\" has no field \"%s\"",
+ cp[0], cp[1], cp[2])));
}
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("row \"%s.%s\" has no field \"%s\"",
- cp[0], cp[1], cp[2])));
- }
- default:
- break;
+ default:
+ break;
+ }
+ }
}
- pfree(cp[0]);
- pfree(cp[1]);
- pfree(cp[2]);
- return T_TRIPWORD;
+ /* Nothing found */
+ plpgsql_yylval.cword.idents = idents;
+ return T_CWORD;
}
* ----------
*/
PLpgSQL_type *
-plpgsql_parse_wordtype(const char *word)
+plpgsql_parse_wordtype(char *ident)
{
PLpgSQL_type *dtype;
PLpgSQL_nsitem *nse;
HeapTuple typeTup;
- char *cp[1];
-
- /* Do case conversion and word separation */
- plpgsql_convert_ident(word, cp, 1);
/*
* Do a lookup in the current namespace stack
*/
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- cp[0], NULL, NULL,
+ ident, NULL, NULL,
NULL);
if (nse != NULL)
{
- pfree(cp[0]);
switch (nse->itemtype)
{
case PLPGSQL_NSTYPE_VAR:
* Word wasn't found in the namespace stack. Try to find a data type
* with that name, but ignore shell types and complex types.
*/
- typeTup = LookupTypeName(NULL, makeTypeName(cp[0]), NULL);
+ typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL);
if (typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
typeStruct->typrelid != InvalidOid)
{
ReleaseSysCache(typeTup);
- pfree(cp[0]);
return NULL;
}
dtype = build_datatype(typeTup, -1);
ReleaseSysCache(typeTup);
- pfree(cp[0]);
return dtype;
}
* Nothing found - up to now it's a word without any special meaning for
* us.
*/
- pfree(cp[0]);
return NULL;
}
/* ----------
- * plpgsql_parse_dblwordtype Same lookup for word.word%TYPE
+ * plpgsql_parse_cwordtype Same lookup for compositeword%TYPE
* ----------
*/
PLpgSQL_type *
-plpgsql_parse_dblwordtype(const char *word)
+plpgsql_parse_cwordtype(List *idents)
{
PLpgSQL_type *dtype = NULL;
PLpgSQL_nsitem *nse;
+ const char *fldname;
Oid classOid;
HeapTuple classtup = NULL;
HeapTuple attrtup = NULL;
HeapTuple typetup = NULL;
Form_pg_class classStruct;
Form_pg_attribute attrStruct;
- char *cp[2];
MemoryContext oldCxt;
/* Avoid memory leaks in the long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
- /* Do case conversion and word separation */
- plpgsql_convert_ident(word, cp, 2);
+ if (list_length(idents) == 2)
+ {
+ /*
+ * Do a lookup in the current namespace stack.
+ * We don't need to check number of names matched, because we will
+ * only consider scalar variables.
+ */
+ nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
+ strVal(linitial(idents)),
+ strVal(lsecond(idents)),
+ NULL,
+ NULL);
- /*
- * Do a lookup in the current namespace stack.
- * We don't need to check number of names matched, because we will only
- * consider scalar variables.
- */
- nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
- cp[0], cp[1], NULL,
- NULL);
+ if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
+ {
+ dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+ goto done;
+ }
- if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
+ /*
+ * First word could also be a table name
+ */
+ classOid = RelnameGetRelid(strVal(linitial(idents)));
+ if (!OidIsValid(classOid))
+ goto done;
+ fldname = strVal(lsecond(idents));
+ }
+ else if (list_length(idents) == 3)
{
- dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
- goto done;
+ RangeVar *relvar;
+
+ relvar = makeRangeVar(strVal(linitial(idents)),
+ strVal(lsecond(idents)),
+ -1);
+ classOid = RangeVarGetRelid(relvar, true);
+ if (!OidIsValid(classOid))
+ goto done;
+ fldname = strVal(lthird(idents));
}
-
- /*
- * First word could also be a table name
- */
- classOid = RelnameGetRelid(cp[0]);
- if (!OidIsValid(classOid))
- goto done;
-
- classtup = SearchSysCache(RELOID,
- ObjectIdGetDatum(classOid),
- 0, 0, 0);
- if (!HeapTupleIsValid(classtup))
- goto done;
- classStruct = (Form_pg_class) GETSTRUCT(classtup);
-
- /*
- * It must be a relation, sequence, view, or type
- */
- if (classStruct->relkind != RELKIND_RELATION &&
- classStruct->relkind != RELKIND_SEQUENCE &&
- classStruct->relkind != RELKIND_VIEW &&
- classStruct->relkind != RELKIND_COMPOSITE_TYPE)
- goto done;
-
- /*
- * Fetch the named table field and its type
- */
- attrtup = SearchSysCacheAttName(classOid, cp[1]);
- if (!HeapTupleIsValid(attrtup))
- goto done;
- attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
-
- typetup = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(attrStruct->atttypid),
- 0, 0, 0);
- if (!HeapTupleIsValid(typetup))
- elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
-
- /*
- * Found that - build a compiler type struct in the caller's cxt and
- * return it
- */
- MemoryContextSwitchTo(oldCxt);
- dtype = build_datatype(typetup, attrStruct->atttypmod);
- MemoryContextSwitchTo(compile_tmp_cxt);
-
-done:
- if (HeapTupleIsValid(classtup))
- ReleaseSysCache(classtup);
- if (HeapTupleIsValid(attrtup))
- ReleaseSysCache(attrtup);
- if (HeapTupleIsValid(typetup))
- ReleaseSysCache(typetup);
-
- MemoryContextSwitchTo(oldCxt);
- return dtype;
-}
-
-/* ----------
- * plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE
- * ----------
- */
-PLpgSQL_type *
-plpgsql_parse_tripwordtype(const char *word)
-{
- PLpgSQL_type *dtype = NULL;
- Oid classOid;
- HeapTuple classtup = NULL;
- HeapTuple attrtup = NULL;
- HeapTuple typetup = NULL;
- Form_pg_class classStruct;
- Form_pg_attribute attrStruct;
- char *cp[3];
- RangeVar *relvar;
- MemoryContext oldCxt;
-
- /* Avoid memory leaks in the long-term function context */
- oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
-
- /* Do case conversion and word separation */
- plpgsql_convert_ident(word, cp, 3);
-
- relvar = makeRangeVar(cp[0], cp[1], -1);
- classOid = RangeVarGetRelid(relvar, true);
- if (!OidIsValid(classOid))
+ else
goto done;
classtup = SearchSysCache(RELOID,
/*
* Fetch the named table field and its type
*/
- attrtup = SearchSysCacheAttName(classOid, cp[2]);
+ attrtup = SearchSysCacheAttName(classOid, fldname);
if (!HeapTupleIsValid(attrtup))
goto done;
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
* ----------
*/
PLpgSQL_type *
-plpgsql_parse_wordrowtype(const char *word)
+plpgsql_parse_wordrowtype(char *ident)
{
- PLpgSQL_type *dtype;
Oid classOid;
- char *cp[1];
-
- /* Do case conversion and word separation */
- plpgsql_convert_ident(word, cp, 1);
/* Lookup the relation */
- classOid = RelnameGetRelid(cp[0]);
+ classOid = RelnameGetRelid(ident);
if (!OidIsValid(classOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation \"%s\" does not exist", cp[0])));
+ errmsg("relation \"%s\" does not exist", ident)));
/* Build and return the row type struct */
- dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1);
-
- pfree(cp[0]);
-
- return dtype;
+ return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
}
/* ----------
- * plpgsql_parse_dblwordrowtype Scanner found word.word%ROWTYPE.
+ * plpgsql_parse_cwordrowtype Scanner found compositeword%ROWTYPE.
* So word must be a namespace qualified table name.
* ----------
*/
PLpgSQL_type *
-plpgsql_parse_dblwordrowtype(const char *word)
+plpgsql_parse_cwordrowtype(List *idents)
{
- PLpgSQL_type *dtype;
Oid classOid;
- char *cp[2];
RangeVar *relvar;
MemoryContext oldCxt;
+ if (list_length(idents) != 2)
+ return NULL;
+
/* Avoid memory leaks in long-term function context */
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
- /* Do case conversion and word separation */
- plpgsql_convert_ident(word, cp, 2);
-
/* Lookup the relation */
- relvar = makeRangeVar(cp[0], cp[1], -1);
+ relvar = makeRangeVar(strVal(linitial(idents)),
+ strVal(lsecond(idents)),
+ -1);
classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation \"%s.%s\" does not exist", cp[0], cp[1])));
+ errmsg("relation \"%s.%s\" does not exist",
+ strVal(linitial(idents)), strVal(lsecond(idents)))));
MemoryContextSwitchTo(oldCxt);
/* Build and return the row type struct */
- dtype = plpgsql_build_datatype(get_rel_type_id(classOid), -1);
-
- return dtype;
+ return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
}
/*