Files
openGauss-server/src/common/pl/plpgsql/src/pl_scanner.cpp
nnuanyang bb29f96079 declare
declare cursor & condition

plpgsql_parse_declare change

condition keyword

strcasecmo change

error message

customCondition change
2022-12-01 01:48:29 -08:00

1483 lines
55 KiB
C++

/* -------------------------------------------------------------------------
*
* pl_scanner.c
* lexical scanning for PL/pgSQL
*
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/pl/plpgsql/src/pl_scanner.c
*
* -------------------------------------------------------------------------
*/
#include "utils/builtins.h"
#include "utils/plpgsql.h"
#include "utils/pl_package.h"
#include "catalog/pg_type.h"
#include "catalog/gs_package.h"
#include "mb/pg_wchar.h"
#include "parser/scanner.h"
#include "pl_gram.hpp" /* must be after parser/scanner.h */
#define PG_KEYWORD(a, b, c) {a, b, c},
#define LENGTH_OF_DOT_AND_STR_END 4
#define INT32_STRING_SIZE 12
/*
* A word about keywords:
*
* We keep reserved and unreserved keywords in separate arrays. The
* reserved keywords are passed to the core scanner, so they will be
* recognized before (and instead of) any variable name. Unreserved
* words are checked for separately, after determining that the identifier
* isn't a known variable name. If plpgsql_IdentifierLookup is DECLARE then
* no variable names will be recognized, so the unreserved words always work.
* (Note in particular that this helps us avoid reserving keywords that are
* only needed in DECLARE sections.)
*
* In certain contexts it is desirable to prefer recognizing an unreserved
* keyword over recognizing a variable name. Those cases are handled in
* gram.y using tok_is_keyword().
*
* For the most part, the reserved keywords are those that start a PL/pgSQL
* statement (and so would conflict with an assignment to a variable of the
* same name). We also don't sweat it much about reserving keywords that
* are reserved in the core grammar. Try to avoid reserving other words.
*/
/*
* Lists of keyword (name, token-value, category) entries.
*
* !!WARNING!!: These lists must be sorted by ASCII name, because binary
* search is used to locate entries.
*
* Be careful not to put the same word in both lists. Also be sure that
* gram.y's unreserved_keyword production agrees with the second list.
*/
static const ScanKeyword reserved_keywords[] = {
PG_KEYWORD("all", K_ALL, RESERVED_KEYWORD)
PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD)
PG_KEYWORD("by", K_BY, RESERVED_KEYWORD)
PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD)
PG_KEYWORD("close", K_CLOSE, RESERVED_KEYWORD)
PG_KEYWORD("collate", K_COLLATE, RESERVED_KEYWORD)
PG_KEYWORD("declare", K_DECLARE, RESERVED_KEYWORD)
PG_KEYWORD("default", K_DEFAULT, RESERVED_KEYWORD)
PG_KEYWORD("delete", K_DELETE, RESERVED_KEYWORD)
PG_KEYWORD("diagnostics", K_DIAGNOSTICS, RESERVED_KEYWORD)
PG_KEYWORD("else", K_ELSE, RESERVED_KEYWORD)
PG_KEYWORD("elseif", K_ELSIF, RESERVED_KEYWORD)
PG_KEYWORD("elsif", K_ELSIF, RESERVED_KEYWORD)
PG_KEYWORD("end", K_END, RESERVED_KEYWORD)
PG_KEYWORD("exception", K_EXCEPTION, RESERVED_KEYWORD)
PG_KEYWORD("execute", K_EXECUTE, RESERVED_KEYWORD)
PG_KEYWORD("exit", K_EXIT, RESERVED_KEYWORD)
PG_KEYWORD("fetch", K_FETCH, RESERVED_KEYWORD)
PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD)
PG_KEYWORD("forall", K_FORALL, RESERVED_KEYWORD)
PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD)
PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD)
PG_KEYWORD("function", K_FUNCTION, RESERVED_KEYWORD)
PG_KEYWORD("get", K_GET, RESERVED_KEYWORD)
PG_KEYWORD("goto", K_GOTO, RESERVED_KEYWORD)
PG_KEYWORD("if", K_IF, RESERVED_KEYWORD)
PG_KEYWORD("in", K_IN, RESERVED_KEYWORD)
PG_KEYWORD("insert", K_INSERT, RESERVED_KEYWORD)
PG_KEYWORD("into", K_INTO, RESERVED_KEYWORD)
PG_KEYWORD("limit", K_LIMIT, RESERVED_KEYWORD)
PG_KEYWORD("loop", K_LOOP, RESERVED_KEYWORD)
PG_KEYWORD("move", K_MOVE, RESERVED_KEYWORD)
PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD)
PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD)
PG_KEYWORD("of", K_OF, RESERVED_KEYWORD)
PG_KEYWORD("open", K_OPEN, RESERVED_KEYWORD)
PG_KEYWORD("or", K_OR, RESERVED_KEYWORD)
PG_KEYWORD("out", K_OUT, RESERVED_KEYWORD)
PG_KEYWORD("procedure", K_PROCEDURE, RESERVED_KEYWORD)
PG_KEYWORD("raise", K_RAISE, RESERVED_KEYWORD)
PG_KEYWORD("ref", K_REF, RESERVED_KEYWORD)
PG_KEYWORD("return", K_RETURN, RESERVED_KEYWORD)
PG_KEYWORD("select", K_SELECT, RESERVED_KEYWORD)
PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD)
PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD)
PG_KEYWORD("to", K_TO, RESERVED_KEYWORD)
PG_KEYWORD("update", K_UPDATE, RESERVED_KEYWORD)
PG_KEYWORD("using", K_USING, RESERVED_KEYWORD)
PG_KEYWORD("when", K_WHEN, RESERVED_KEYWORD)
PG_KEYWORD("while", K_WHILE, RESERVED_KEYWORD)
};
static const int num_reserved_keywords = lengthof(reserved_keywords);
static const ScanKeyword unreserved_keywords[] = {
PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD)
PG_KEYWORD("alter", K_ALTER, UNRESERVED_KEYWORD)
PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
PG_KEYWORD("as", K_AS, UNRESERVED_KEYWORD)
PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("bulk", K_BULK, UNRESERVED_KEYWORD)
PG_KEYWORD("call", K_CALL, UNRESERVED_KEYWORD)
PG_KEYWORD("collect", K_COLLECT, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", K_COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("condition", K_CONDITION, UNRESERVED_KEYWORD)
PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD)
PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD)
PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD)
PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
PG_KEYWORD("distinct", K_DISTINCT, UNRESERVED_KEYWORD)
PG_KEYWORD("do", K_DO, UNRESERVED_KEYWORD)
PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD)
PG_KEYWORD("except", K_EXCEPT, UNRESERVED_KEYWORD)
PG_KEYWORD("exceptions", K_EXCEPTIONS, UNRESERVED_KEYWORD)
PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD)
PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("function", K_FUNCTION, UNRESERVED_KEYWORD)
PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD)
PG_KEYWORD("immediate", K_IMMEDIATE, UNRESERVED_KEYWORD)
PG_KEYWORD("index", K_INDEX, UNRESERVED_KEYWORD)
PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD)
PG_KEYWORD("instantiation", K_INSTANTIATION, UNRESERVED_KEYWORD)
PG_KEYWORD("intersect", K_INTERSECT, UNRESERVED_KEYWORD)
PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD)
PG_KEYWORD("iterate", K_ITERATE, UNRESERVED_KEYWORD)
PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD)
PG_KEYWORD("leave", K_LEAVE, UNRESERVED_KEYWORD)
PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD)
PG_KEYWORD("merge", K_MERGE, UNRESERVED_KEYWORD)
PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD)
PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("multiset", K_MULTISET, UNRESERVED_KEYWORD)
PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
PG_KEYWORD("package", K_PACKAGE, UNRESERVED_KEYWORD)
PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD)
PG_KEYWORD("pragma", K_PRAGMA, UNRESERVED_KEYWORD)
PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
PG_KEYWORD("procedure", K_PROCEDURE, UNRESERVED_KEYWORD)
PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD)
PG_KEYWORD("record", K_RECORD, UNRESERVED_KEYWORD)
PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("release", K_RELEASE, UNRESERVED_KEYWORD)
PG_KEYWORD("repeat", K_REPEAT, UNRESERVED_KEYWORD)
PG_KEYWORD("replace", K_REPLACE, UNRESERVED_KEYWORD)
PG_KEYWORD("result_oid", K_RESULT_OID, UNRESERVED_KEYWORD)
PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD)
PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD)
PG_KEYWORD("rollback", K_ROLLBACK, UNRESERVED_KEYWORD)
PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD)
PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("save", K_SAVE, UNRESERVED_KEYWORD)
PG_KEYWORD("savepoint", K_SAVEPOINT, UNRESERVED_KEYWORD)
PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD)
PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD)
PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD)
PG_KEYWORD("stacked", K_STACKED, UNRESERVED_KEYWORD)
PG_KEYWORD("sys_refcursor", K_SYS_REFCURSOR, UNRESERVED_KEYWORD)
PG_KEYWORD("table", K_TABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("union", K_UNION, UNRESERVED_KEYWORD)
PG_KEYWORD("until", K_UNTIL, UNRESERVED_KEYWORD)
PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD)
PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT, UNRESERVED_KEYWORD)
PG_KEYWORD("varray", K_VARRAY, UNRESERVED_KEYWORD)
PG_KEYWORD("warning", K_WARNING, UNRESERVED_KEYWORD)
PG_KEYWORD("with", K_WITH, UNRESERVED_KEYWORD)
};
static const int num_unreserved_keywords = lengthof(unreserved_keywords);
static const struct PlpgsqlKeywordValue keywordsValue = {
.procedure = K_PROCEDURE,
.function = K_FUNCTION,
.begin = K_BEGIN,
.select = K_SELECT,
.update = K_UPDATE,
.insert = K_INSERT,
.Delete = K_DELETE,
.merge = K_MERGE
};
/* Auxiliary data about a token (other than the token type) */
typedef struct {
YYSTYPE lval; /* semantic information */
YYLTYPE lloc; /* offset in scanbuf */
int leng; /* length in bytes */
} TokenAuxData;
THR_LOCAL TokenAuxData pushback_auxdata[MAX_PUSHBACKS];
/* Internal functions */
static int internal_yylex(TokenAuxData* auxdata);
static void push_back_token(int token, TokenAuxData* auxdata);
static void location_lineno_init(void);
static int get_self_defined_tok(int tok_flag);
static int plpgsql_parse_cursor_attribute(int* loc);
static int plpgsql_parse_declare(int* loc);
static void RecordDependencyOfPkg(PLpgSQL_package* pkg, Oid pkgOid, Oid currCompPkgOid);
static PLpgSQL_datum* SearchPackageDatum(PLpgSQL_package* pkg, Oid pkgOid, Oid currCompPkgOid,
const char* pkgname, const char* objname);
/*
* This is the yylex routine called from the PL/pgSQL grammar.
* It is a wrapper around the core lexer, with the ability to recognize
* PL/pgSQL variables and return them as special T_DATUM tokens. If a
* word or compound word does not match any variable name, or if matching
* is turned off by plpgsql_IdentifierLookup, it is returned as
* T_WORD or T_CWORD respectively, or as an unreserved keyword if it
* matches one of those.
*/
int plpgsql_yylex(void)
{
int tok1;
int loc = 0;
TokenAuxData aux1;
const ScanKeyword* kw = NULL;
/* enum flag for returning specified tokens */
int tok_flag = -1;
int dbl_tok_flag = -1;
int trip_tok_flag = -1;
int quad_tok_flag = -1;
/* parse cursor attribute, return token and location */
tok1 = plpgsql_parse_cursor_attribute(&loc);
if (tok1 != -1) {
plpgsql_yylloc = loc;
return tok1;
}
if (u_sess->attr.attr_sql.sql_compatibility == B_FORMAT) {
/* parse declare condition */
tok1 = plpgsql_parse_declare(&loc);
if (tok1 != -1) {
plpgsql_yylloc = loc;
return tok1;
}
}
tok1 = internal_yylex(&aux1);
if (tok1 == IDENT || tok1 == PARAM || tok1 == T_SQL_BULK_EXCEPTIONS) {
int tok2;
TokenAuxData aux2;
char* tok1_val = NULL;
if(tok1 == PARAM)
tok1_val = aux1.lval.str;
tok2 = internal_yylex(&aux2);
if (tok2 == '.') {
int tok3;
TokenAuxData aux3;
tok3 = internal_yylex(&aux3);
if (tok3 == IDENT || (tok1 == IDENT && (tok3 == K_DELETE || tok3 == K_CLOSE || tok3 == K_OPEN))) {
int tok4;
TokenAuxData aux4;
tok4 = internal_yylex(&aux4);
if (tok4 == '.') {
int tok5;
TokenAuxData aux5;
tok5 = internal_yylex(&aux5);
if (tok5 == IDENT || (tok5 == K_DELETE && tok3 == IDENT && tok1 == IDENT)) {
int tok6;
TokenAuxData aux6;
tok6 = internal_yylex(&aux6);
if (tok6 == '.') {
int tok7;
TokenAuxData aux7;
tok7 = internal_yylex(&aux7);
if (tok7 == IDENT ||
(tok7 == K_DELETE && tok5 == IDENT && tok3 == IDENT && tok1 == IDENT)) {
if (plpgsql_parse_quadword(aux1.lval.str, aux3.lval.str, aux5.lval.str, aux7.lval.str,
&aux1.lval.wdatum, &aux1.lval.cword, &quad_tok_flag)) {
if (quad_tok_flag != -1) {
tok1 = get_self_defined_tok(quad_tok_flag);
} else {
tok1 = T_DATUM;
}
} else {
tok1 = T_CWORD;
}
} else {
/* not A.B.C.D, so just process A.B.C */
push_back_token(tok7, &aux7);
push_back_token(tok6, &aux6);
if (plpgsql_parse_tripword(aux1.lval.str, aux3.lval.str, aux5.lval.str,
&aux1.lval.wdatum, &aux1.lval.cword, &trip_tok_flag)) {
if (trip_tok_flag != -1) {
tok1 = get_self_defined_tok(trip_tok_flag);
} else {
tok1 = T_DATUM;
}
} else {
tok1 = T_CWORD;
}
}
} else {
/* not A.B.C.D, so just process A.B.C */
push_back_token(tok6, &aux6);
if (plpgsql_parse_tripword(aux1.lval.str, aux3.lval.str, aux5.lval.str,
&aux1.lval.wdatum, &aux1.lval.cword, &trip_tok_flag)) {
if (trip_tok_flag != -1) {
tok1 = get_self_defined_tok(trip_tok_flag);
} else {
tok1 = T_DATUM;
}
} else {
tok1 = T_CWORD;
}
}
} else {
/* not A.B.C, so just process A.B */
push_back_token(tok5, &aux5);
push_back_token(tok4, &aux4);
if (plpgsql_parse_dblword(
aux1.lval.str, aux3.lval.str, &aux1.lval.wdatum, &aux1.lval.cword, &dbl_tok_flag)) {
if (dbl_tok_flag != -1) {
tok1 = get_self_defined_tok(dbl_tok_flag);
} else {
tok1 = T_DATUM;
}
} else {
tok1 = T_CWORD;
}
}
} else {
/* not A.B.C, so just process A.B */
push_back_token(tok4, &aux4);
if (plpgsql_parse_dblword(
aux1.lval.str, aux3.lval.str, &aux1.lval.wdatum, &aux1.lval.cword, &dbl_tok_flag)) {
if (dbl_tok_flag != -1) {
tok1 = get_self_defined_tok(dbl_tok_flag);
} else {
tok1 = T_DATUM;
}
} else {
tok1 = T_CWORD;
}
}
} else {
/* not A.B, so just process A */
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
if (plpgsql_parse_word(
aux1.lval.str, u_sess->plsql_cxt.curr_compile_context->core_yy->scanbuf + aux1.lloc, &aux1.lval.wdatum,
&aux1.lval.word, &tok_flag)) {
/* Get self defined token */
if (tok_flag != -1) {
tok1 = get_self_defined_tok(tok_flag);
} else {
tok1 = T_DATUM;
}
} else if (!aux1.lval.word.quoted &&
(kw = ScanKeywordLookup(
aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) {
aux1.lval.keyword = kw->name;
tok1 = kw->value;
} else {
tok1 = T_WORD;
}
}
} else {
/* not A.B, so just process A */
push_back_token(tok2, &aux2);
if (plpgsql_parse_word(
aux1.lval.str, u_sess->plsql_cxt.curr_compile_context->core_yy->scanbuf + aux1.lloc, &aux1.lval.wdatum,
&aux1.lval.word, &tok_flag)) {
/* Get self defined token */
if (tok_flag != -1) {
tok1 = get_self_defined_tok(tok_flag);
} else {
tok1 = T_DATUM;
}
} else if (!aux1.lval.word.quoted &&
(kw = ScanKeywordLookup(aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) {
aux1.lval.keyword = kw->name;
tok1 = kw->value;
} else if (aux1.lval.str[0] == ':' && tok1 == PARAM) {
/* Check place holder, it should be like :({identifier}|{integer})
* It is a placeholder in exec statement. */
if(u_sess->attr.attr_sql.sql_compatibility == B_FORMAT)
{
if(pg_strcasecmp(tok1_val, ":loop") == 0)
tok1 = T_LABELLOOP;
else if(pg_strcasecmp(tok1_val, ":while") == 0)
tok1 = T_LABELWHILE;
else if(pg_strcasecmp(tok1_val, ":repeat") == 0)
tok1 = T_LABELREPEAT;
else
tok1 = T_PLACEHOLDER;
}
else
{
tok1 = T_PLACEHOLDER;
}
} else {
tok1 = T_WORD;
}
}
} else {
/* Not a potential plpgsql variable name, just return the data */
}
plpgsql_yylval = aux1.lval;
plpgsql_yylloc = aux1.lloc;
u_sess->plsql_cxt.curr_compile_context->plpgsql_yyleng = aux1.leng;
return tok1;
}
int plpgsql_yylex_single(void)
{
int tok1;
int loc = 0;
TokenAuxData aux1;
const ScanKeyword* kw = NULL;
/* enum flag for returning specified tokens */
int tok_flag = -1;
/* parse cursor attribute, return token and location */
tok1 = plpgsql_parse_cursor_attribute(&loc);
if (tok1 != -1) {
plpgsql_yylloc = loc;
return tok1;
}
tok1 = internal_yylex(&aux1);
if (tok1 == IDENT || tok1 == PARAM) {
if (plpgsql_parse_word(
aux1.lval.str, u_sess->plsql_cxt.curr_compile_context->core_yy->scanbuf + aux1.lloc, &aux1.lval.wdatum,
&aux1.lval.word, &tok_flag)) {
/* Get self defined token */
if (tok_flag != -1) {
tok1 = get_self_defined_tok(tok_flag);
} else {
tok1 = T_DATUM;
}
} else if (!aux1.lval.word.quoted &&
(kw = ScanKeywordLookup(
aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) {
aux1.lval.keyword = kw->name;
tok1 = kw->value;
} else {
tok1 = T_WORD;
}
} else {
/* Not a potential plpgsql variable name, just return the data */
}
plpgsql_yylval = aux1.lval;
plpgsql_yylloc = aux1.lloc;
u_sess->plsql_cxt.curr_compile_context->plpgsql_yyleng = aux1.leng;
return tok1;
}
/*
* Internal yylex function. This wraps the core lexer and adds one feature:
* a token pushback stack. We also make a couple of trivial single-token
* translations from what the core lexer does to what we want, in particular
* interfacing from the core_YYSTYPE to YYSTYPE union.
*/
static int internal_yylex(TokenAuxData* auxdata)
{
int token;
const char* yytext = NULL;
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
if (curr_compile->num_pushbacks > 0) {
curr_compile->num_pushbacks--;
token = curr_compile->pushback_token[curr_compile->num_pushbacks];
*auxdata = pushback_auxdata[curr_compile->num_pushbacks];
} else {
token = core_yylex(&auxdata->lval.core_yystype, &auxdata->lloc, curr_compile->yyscanner);
/* remember the length of yytext before it gets changed */
yytext = curr_compile->core_yy->scanbuf + auxdata->lloc;
auxdata->leng = strlen(yytext);
/* Check for << >> and #, which the core considers operators */
if (token == Op) {
if (strcmp(auxdata->lval.str, "<<") == 0) {
token = LESS_LESS;
} else if (strcmp(auxdata->lval.str, ">>") == 0) {
token = GREATER_GREATER;
} else if (strcmp(auxdata->lval.str, "#") == 0) {
token = '#';
}
} else if (token == PARAM) {
/* The core returns PARAM as ival, but we treat it like IDENT */
auxdata->lval.str = pstrdup(yytext);
}
}
return token;
}
/*
* Push back a token to be re-read by next internal_yylex() call.
*/
static void push_back_token(int token, TokenAuxData* auxdata)
{
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
if (curr_compile->num_pushbacks >= MAX_PUSHBACKS) {
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_INVALID_OPTION),
errmsg("too many tokens %d pushed back, max push back token is: %d",
curr_compile->num_pushbacks,
MAX_PUSHBACKS)));
}
curr_compile->pushback_token[curr_compile->num_pushbacks] = token;
pushback_auxdata[curr_compile->num_pushbacks] = *auxdata;
curr_compile->num_pushbacks++;
}
/*
* Push back a single token to be re-read by next plpgsql_yylex() call.
*
* NOTE: this does not cause yylval or yylloc to "back up". Also, it
* is not a good idea to push back a token code other than what you read.
*/
void plpgsql_push_back_token(int token)
{
TokenAuxData auxdata;
auxdata.lval = plpgsql_yylval;
auxdata.lloc = plpgsql_yylloc;
auxdata.leng = u_sess->plsql_cxt.curr_compile_context->plpgsql_yyleng;
push_back_token(token, &auxdata);
}
/*
* Append the function text starting at startlocation and extending to
* (not including) endlocation onto the existing contents of "buf".
*/
void plpgsql_append_source_text(StringInfo buf, int startlocation, int endlocation)
{
AssertEreport(startlocation <= endlocation, MOD_PLSQL, "start should be less or equal end.");
appendBinaryStringInfo(buf, u_sess->plsql_cxt.curr_compile_context->scanorig + startlocation,
endlocation - startlocation);
}
/*
* Append array type name after each array element.
*/
void plpgsql_append_object_typename(StringInfo buf, PLpgSQL_type *var_type)
{
errno_t ret;
char* typcast = "::";
char* left = "(";
char* right = ")";
char* dot = ",";
appendBinaryStringInfo(buf, typcast, 2);
int len = strlen(var_type->typname) + strlen(var_type->typnamespace) + LENGTH_OF_DOT_AND_STR_END;
char* typname = (char*)palloc(len);
ret = strcpy_s(typname, len, var_type->typnamespace);
securec_check(ret, "\0", "\0");
ret = strcat_s(typname, len, ".\"");
securec_check(ret, "\0", "\0");
ret = strcat_s(typname, len, var_type->typname);
securec_check(ret, "\0", "\0");
ret = strcat_s(typname, len, "\"");
securec_check(ret, "\0", "\0");
appendBinaryStringInfo(buf, typname, strlen(typname));
if (var_type->atttypmod != -1) {
appendBinaryStringInfo(buf, left, 1);
if (var_type->typoid == NUMERICOID) {
int32 typmod = var_type->atttypmod - VARHDRSZ;
char* precision = (char*)palloc(INT32_STRING_SIZE);
char* scale = (char*)palloc(INT32_STRING_SIZE);
pg_ltoa((int32)(((uint32)(typmod) >> 16) & 0xffff), precision);
pg_ltoa((int32)(((uint32)typmod) & 0xffff), scale);
appendBinaryStringInfo(buf, precision, strlen(precision));
appendBinaryStringInfo(buf, dot, 1);
appendBinaryStringInfo(buf, scale, strlen(scale));
} else {
char* typMod = (char*)palloc(INT32_STRING_SIZE);
pg_ltoa(var_type->atttypmod, typMod);
appendBinaryStringInfo(buf, typMod, strlen(typMod));
}
appendBinaryStringInfo(buf, right, 1);
}
pfree_ext(typname);
}
/*
* Replace all identified array bounds(usually parentheses) with brackets.
*/
void plpgsql_process_stmt_array(StringInfo buf, List* bracket_loc)
{
ListCell* lc = NULL;
if (bracket_loc == NULL) {
return;
}
/* brackets always comes in pairs */
AssertEreport(list_length(bracket_loc) % 2 == 0, MOD_PLSQL, "statement contains mismatched parentheses.");
int counter = 1;
int divider = list_length(bracket_loc) / 2;
foreach(lc, bracket_loc) {
buf->data[lfirst_int(lc)] = (counter <= divider) ? '[' : ']';
counter++;
}
}
/*
* Peek two tokens ahead in the input stream. The first token and its
* location the query are returned in *tok1_p and *tok1_loc, second token
* and its location in *tok2_p and *tok2_loc.
*
* NB: no variable or unreserved keyword lookup is performed here, they will
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
void plpgsql_peek2(int* tok1_p, int* tok2_p, int* tok1_loc, int* tok2_loc)
{
int tok1, tok2;
TokenAuxData aux1, aux2;
tok1 = internal_yylex(&aux1);
tok2 = internal_yylex(&aux2);
*tok1_p = tok1;
if (tok1_loc != NULL) {
*tok1_loc = aux1.lloc;
}
*tok2_p = tok2;
if (tok2_loc != NULL) {
*tok2_loc = aux2.lloc;
}
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
}
/*
* plpgsql_scanner_errposition
* Report an error cursor position, if possible.
*
* This is expected to be used within an ereport() call. The return value
* is a dummy (always 0, in fact).
*
* Note that this can only be used for messages emitted during initial
* parsing of a plpgsql function, since it requires the curr_compile->scanorig string
* to still be available.
*/
int plpgsql_scanner_errposition(int location)
{
int pos;
if (location < 0 || u_sess->plsql_cxt.curr_compile_context->scanorig == NULL) {
return 0; /* no-op if location is unknown */
}
/* Convert byte offset to character number */
pos = pg_mbstrlen_with_len(u_sess->plsql_cxt.curr_compile_context->scanorig, location) + 1;
/* And pass it to the ereport mechanism */
(void)internalerrposition(pos);
/* Also pass the function body string */
return internalerrquery(u_sess->plsql_cxt.curr_compile_context->scanorig);
}
/*
* plpgsql_yyerror
* Report a lexer or grammar error.
*
* The message's cursor position refers to the current token (the one
* last returned by plpgsql_yylex()).
* This is OK for syntax error messages from the Bison parser, because Bison
* parsers report error as soon as the first unparsable token is reached.
* Beware of using yyerror for other purposes, as the cursor position might
* be misleading!
*/
void plpgsql_yyerror(const char* message, bool isError)
{
char* yytext = u_sess->plsql_cxt.curr_compile_context->core_yy->scanbuf + plpgsql_yylloc;
int errstate = 0;
#ifndef ENABLE_MULTIPLE_NODES
if (u_sess->attr.attr_common.plsql_show_all_error && !isError) {
errstate = NOTICE;
} else {
errstate = ERROR;
}
#else
errstate = ERROR;
#endif
int lines = 0;
int rc = CompileWhich();
if (rc != PLPGSQL_COMPILE_NULL) {
lines = GetProcedureLineNumberInPackage(u_sess->plsql_cxt.curr_compile_context->core_yy->scanbuf, plpgsql_yylloc);
addErrorList(message, lines);
}
if (*yytext == '\0') {
ereport(errstate,
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: %s is typically the translation of "syntax error" */
errmsg("%s at end of input", _(message)),
plpgsql_scanner_errposition(plpgsql_yylloc)));
} else {
/*
* If we have done any lookahead then flex will have restored the
* character after the end-of-token. Zap it again so that we report
* only the single token here. This modifies scanbuf but we no longer
* care about that.
*/
yytext[u_sess->plsql_cxt.curr_compile_context->plpgsql_yyleng] = '\0';
ereport(errstate,
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: first %s is typically the translation of "syntax error" */
errmsg("%s at or near \"%s\"", _(message), yytext),
plpgsql_scanner_errposition(plpgsql_yylloc)));
}
}
/*
* Given a location (a byte offset in the function source text),
* return a line number.
*
* We expect that this is typically called for a sequence of increasing
* location values, so optimize accordingly by tracking the endpoints
* of the "current" line.
*/
int plpgsql_location_to_lineno(int location)
{
const char* loc = NULL;
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
if (location < 0 || curr_compile->scanorig == NULL) {
return 0; /* garbage in, garbage out */
}
loc = curr_compile->scanorig + location;
/* be correct, but not fast, if input location goes backwards */
if (loc < curr_compile->cur_line_start) {
location_lineno_init();
}
while (curr_compile->cur_line_end != NULL && loc > curr_compile->cur_line_end) {
curr_compile->cur_line_start = curr_compile->cur_line_end + 1;
u_sess->plsql_cxt.curr_compile_context->cur_line_num++;
curr_compile->cur_line_end = strchr(curr_compile->cur_line_start, '\n');
}
return u_sess->plsql_cxt.curr_compile_context->cur_line_num;
}
char* plpgsql_get_curline_query()
{
int len = 0;
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
if (curr_compile->cur_line_end == NULL) {
/* no '\n' in whole procedure source text */
len = strlen(curr_compile->cur_line_start);
} else {
len = curr_compile->cur_line_end - curr_compile->cur_line_start;
}
char* curQuery = NULL;
if (len > 0) {
curQuery = (char*)palloc0(sizeof(char) * (len + 1));
int rc = memcpy_s(curQuery, len + 1, curr_compile->cur_line_start, len);
securec_check(rc, "\0", "\0");
}
return curQuery;
}
/* initialize or reset the state for plpgsql_location_to_lineno */
static void location_lineno_init(void)
{
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
curr_compile->cur_line_start = curr_compile->scanorig;
curr_compile->cur_line_num = 1;
curr_compile->cur_line_end = strchr(curr_compile->cur_line_start, '\n');
}
/* return the most recently computed lineno */
int plpgsql_latest_lineno(void)
{
return u_sess->plsql_cxt.curr_compile_context->cur_line_num;
}
/*
* Called before any actual parsing is done
*
* Note: the passed "str" must remain valid until plpgsql_scanner_finish().
* Although it is not fed directly to flex, we need the original string
* to cite in error messages.
*/
void plpgsql_scanner_init(const char* str)
{
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
/* Start up the core scanner */
curr_compile->yyscanner =
scanner_init(str, curr_compile->core_yy, reserved_keywords, num_reserved_keywords);
curr_compile->core_yy->isPlpgsqlKeyWord = true;
curr_compile->core_yy->plKeywordValue = &keywordsValue;
/*
* curr_compile->scanorig points to the original string, which unlike the scanner's
* scanbuf won't be modified on-the-fly by flex. Notice that although
* yytext points into scanbuf, we rely on being able to apply locations
* (offsets from string start) to curr_compile->scanorig as well.
*/
curr_compile->scanorig = str;
/* Other setup */
curr_compile->plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
curr_compile->num_pushbacks = 0;
location_lineno_init();
/*
* Note, we do plpgsql SQLs parsing under "PL/pgSQL Function" memory context,
* so it is safe to reset it to NIL in plpgsql execution startup time, even
* in case of execution error-out, it better to help avoid access to in garbage
* pointer
*/
curr_compile->goto_labels = NIL;
}
/*
* Called after parsing is done to clean up after plpgsql_scanner_init()
*/
void plpgsql_scanner_finish(void)
{
Assert(u_sess->plsql_cxt.curr_compile_context);
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
/* release storage */
scanner_finish(curr_compile->yyscanner);
/* avoid leaving any dangling pointers */
curr_compile->yyscanner = NULL;
curr_compile->scanorig = NULL;
}
/*
* convert self defined token flag to token
*/
static int get_self_defined_tok(int tok_flag)
{
Assert(tok_flag != -1);
switch (tok_flag) {
case PLPGSQL_TOK_REFCURSOR:
return T_REFCURSOR;
case PLPGSQL_TOK_VARRAY:
return T_VARRAY;
case PLPGSQL_TOK_VARRAY_FIRST:
return T_ARRAY_FIRST;
case PLPGSQL_TOK_VARRAY_LAST:
return T_ARRAY_LAST;
case PLPGSQL_TOK_VARRAY_COUNT:
return T_ARRAY_COUNT;
case PLPGSQL_TOK_VARRAY_EXTEND:
return T_ARRAY_EXTEND;
case PLPGSQL_TOK_VARRAY_EXISTS:
return T_ARRAY_EXISTS;
case PLPGSQL_TOK_VARRAY_PRIOR:
return T_ARRAY_PRIOR;
case PLPGSQL_TOK_VARRAY_NEXT:
return T_ARRAY_NEXT;
case PLPGSQL_TOK_VARRAY_DELETE:
return T_ARRAY_DELETE;
case PLPGSQL_TOK_VARRAY_TRIM:
return T_ARRAY_TRIM;
case PLPGSQL_TOK_VARRAY_VAR:
return T_VARRAY_VAR;
case PLPGSQL_TOK_RECORD:
return T_RECORD;
case PLPGSQL_TOK_TABLE:
return T_TABLE;
case PLPGSQL_TOK_TABLE_VAR:
return T_TABLE_VAR;
case PLPGSQL_TOK_PACKAGE_VARIABLE:
return T_PACKAGE_VARIABLE;
default:
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unknown plpgsql token: %d", tok_flag)));
}
/*never reach here*/
return -1;
}
/*
* parse the cursor attributes and get token.
* if match the cursor attributes, return token,
* and param loc return the location.
* if not match, return -1,
* and push back tokens which were get before.
*/
static int plpgsql_parse_cursor_attribute(int* loc)
{
TokenAuxData aux1;
TokenAuxData aux2;
TokenAuxData aux3;
TokenAuxData aux4;
TokenAuxData aux5;
int tok1;
int tok2;
int tok3;
int tok4;
int tok5;
int token = -1;
PLpgSQL_nsitem* ns = NULL;
bool pkgCursor = false;
if (u_sess->parser_cxt.in_package_function_compile) {
return token;
}
/* coverity warning clear */
tok1 = internal_yylex(&aux1);
if (tok1 != IDENT && tok1 != PARAM) {
push_back_token(tok1, &aux1);
return token;
}
tok2 = internal_yylex(&aux2);
if (tok2 != '%' && tok2 != '.') {
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
return token;
}
if (tok2 == '.') {
pkgCursor = true;
}
tok3 = internal_yylex(&aux3);
if (tok3 != IDENT) {
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
return token;
}
if (pkgCursor) {
tok4 = internal_yylex(&aux4);
if (tok4 != '%') {
push_back_token(tok4, &aux4);
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
return token;
}
tok5 = internal_yylex(&aux5);
if (tok5 != IDENT) {
push_back_token(tok5, &aux5);
push_back_token(tok4, &aux4);
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
return token;
}
}
if (!pkgCursor) {
/* match implicit cursor attributes */
if (strncasecmp(aux1.lval.str, "SQL", 3) == 0) {
if (strncasecmp(aux3.lval.str, "ISOPEN", 6) == 0) {
token = T_SQL_ISOPEN;
}
if (strncasecmp(aux3.lval.str, "FOUND", 5) == 0) {
token = T_SQL_FOUND;
}
if (strncasecmp(aux3.lval.str, "NOTFOUND", 8) == 0) {
token = T_SQL_NOTFOUND;
}
if (strncasecmp(aux3.lval.str, "ROWCOUNT", 8) == 0) {
token = T_SQL_ROWCOUNT;
}
if (strncasecmp(aux3.lval.str, "BULK_EXCEPTIONS", strlen("BULK_EXCEPTIONS")) == 0) {
token = T_SQL_BULK_EXCEPTIONS;
}
}
/* match explicit cursor attributes */
if (strncasecmp(aux1.lval.str, "SQL", 3) != 0) {
if (strncasecmp(aux3.lval.str, "ISOPEN", 6) == 0) {
token = T_CURSOR_ISOPEN;
}
if (strncasecmp(aux3.lval.str, "FOUND", 5) == 0) {
token = T_CURSOR_FOUND;
}
if (strncasecmp(aux3.lval.str, "NOTFOUND", 8) == 0) {
token = T_CURSOR_NOTFOUND;
}
if (strncasecmp(aux3.lval.str, "ROWCOUNT", 8) == 0) {
token = T_CURSOR_ROWCOUNT;
}
}
} else {
/* match package cursor attributes */
if (strncasecmp(aux3.lval.str, "SQL", 3) != 0) {
if (strncasecmp(aux5.lval.str, "ISOPEN", 6) == 0) {
token = T_PACKAGE_CURSOR_ISOPEN;
}
if (strncasecmp(aux5.lval.str, "FOUND", 5) == 0) {
token = T_PACKAGE_CURSOR_FOUND;
}
if (strncasecmp(aux5.lval.str, "NOTFOUND", 8) == 0) {
token = T_PACKAGE_CURSOR_NOTFOUND;
}
if (strncasecmp(aux5.lval.str, "ROWCOUNT", 8) == 0) {
token = T_PACKAGE_CURSOR_ROWCOUNT;
}
}
}
switch (token) {
case T_SQL_ISOPEN:
case T_SQL_FOUND:
case T_SQL_NOTFOUND:
case T_SQL_ROWCOUNT:
/* get the cursor attribute location */
*loc = aux1.lloc;
break;
case T_SQL_BULK_EXCEPTIONS:
push_back_token(token, &aux3);
return -1;
break;
case T_CURSOR_ISOPEN:
case T_CURSOR_FOUND:
case T_CURSOR_NOTFOUND:
case T_CURSOR_ROWCOUNT:
/* check the valid of cursor variable */
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, aux1.lval.str, NULL, NULL, NULL);
if (ns != NULL && ns->itemtype == PLPGSQL_NSTYPE_VAR) {
PLpgSQL_var* var = (PLpgSQL_var*)u_sess->plsql_cxt.curr_compile_context->plpgsql_Datums[ns->itemno];
if (!(var != NULL && var->datatype && var->datatype->typoid == REFCURSOROID)) {
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("%s isn't a cursor", var ? var->refname : "")));
}
aux1.lval.ival = var->dno;
} else {
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("undefined cursor: %s", aux1.lval.str)));
}
/* get the cursor attribute location */
*loc = aux1.lloc;
plpgsql_yylval = aux1.lval;
break;
case T_PACKAGE_CURSOR_ISOPEN:
case T_PACKAGE_CURSOR_FOUND:
case T_PACKAGE_CURSOR_NOTFOUND:
case T_PACKAGE_CURSOR_ROWCOUNT:
/* check the valid of cursor variable */
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, aux1.lval.str, aux3.lval.str, NULL, NULL);
if (ns == NULL) {
List *idents = list_make2(makeString(aux1.lval.str), makeString(aux3.lval.str));
int dno = plpgsql_pkg_add_unknown_var_to_namespace(idents);
if (dno != -1) {
ns = plpgsql_ns_lookup(plpgsql_ns_top(), false, aux1.lval.str, aux3.lval.str, NULL, NULL);
}
list_free_deep(idents);
}
if (ns != NULL && ns->itemtype == PLPGSQL_NSTYPE_VAR) {
PLpgSQL_var* var = (PLpgSQL_var*)u_sess->plsql_cxt.curr_compile_context->plpgsql_Datums[ns->itemno];
if (!(var != NULL && var->datatype && var->datatype->typoid == REFCURSOROID)) {
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("%s.%s isn't a cursor", aux1.lval.str, aux3.lval.str)));
}
aux1.lval.wdatum.ident = aux1.lval.str;
aux1.lval.wdatum.dno = var->dno;
} else {
ereport(ERROR,
(errmodule(MOD_PLSQL),
errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("undefined cursor: %s.%s", aux1.lval.str, aux3.lval.str)));
}
/* get the cursor attribute location */
*loc = aux1.lloc;
plpgsql_yylval = aux1.lval;
break;
default:
/* not match, push back tokens which were get before */
if (pkgCursor) {
push_back_token(tok5, &aux5);
push_back_token(tok4, &aux4);
}
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
break;
}
return token;
}
static int plpgsql_parse_declare(int* loc)
{
TokenAuxData aux1;
int tok1 = -1;
int token = -1;
if (u_sess->parser_cxt.in_package_function_compile) {
return token;
}
tok1 = internal_yylex(&aux1);
if (tok1 == K_DECLARE) {
TokenAuxData aux2;
TokenAuxData aux3;
int tok2 = -1;
int tok3 = -1;
tok2 = internal_yylex(&aux2);
tok3 = internal_yylex(&aux3);
if (tok2 != IDENT || tok3 != IDENT) {
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
return token;
}
if (strcasecmp(aux3.lval.str, "cursor") == 0) {
token = T_DECLARE_CURSOR;
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
/* get the declare attribute location */
*loc = aux1.lloc;
plpgsql_yylval = aux1.lval;
} else if (strcasecmp(aux3.lval.str, "condition") == 0) {
token = T_DECLARE_CONDITION;
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
/* get the declare attribute location */
*loc = aux1.lloc;
plpgsql_yylval = aux1.lval;
} else {
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
}
} else {
push_back_token(tok1, &aux1);
}
return token;
}
/*
* a convenient method to see if the next two tokens are what we expected
*/
bool plpgsql_is_token_match2(int token, int token_next)
{
TokenAuxData aux;
int tok = -1;
int tok_next = -1;
tok = plpgsql_yylex();
if (token == tok) {
plpgsql_push_back_token(tok);
/* use internal_yylex to get the aux data, so it can be pushed back. */
tok = internal_yylex(&aux);
/* coverity warning clear */
if (tok >= INT_MAX) {
return false;
}
tok_next = plpgsql_yylex();
if (tok_next >= INT_MAX) {
return false;
}
plpgsql_push_back_token(tok_next);
push_back_token(tok, &aux);
if (token_next == tok_next) {
return true;
} else {
return false;
}
} else {
plpgsql_push_back_token(tok);
return false;
}
}
/*
* a convenient method to see if the next token is what we expected
*/
bool plpgsql_is_token_match(int token)
{
int tok = -1;
tok = plpgsql_yylex();
if (tok == token) {
plpgsql_push_back_token(tok);
return true;
}
plpgsql_push_back_token(tok);
return false;
}
/*
* a convenient method to see if the next token is keyword
*/
bool plpgsql_is_token_keyword(int token)
{
#define MIN(A, B) ((B) < (A) ? (B) : (A))
#define MAX(A, B) ((B) > (A) ? (B) : (A))
if ((token >= MIN(reserved_keywords[0].value, unreserved_keywords[0].value)) &&
(token <= MAX(reserved_keywords[num_reserved_keywords - 1].value,
unreserved_keywords[num_unreserved_keywords - 1].value))) {
return true;
} else {
return false;
}
}
static PLpgSQL_package* GetCompileListPkg(Oid pkgOid)
{
List* compPkgList = u_sess->plsql_cxt.compile_context_list;
ListCell *item = NULL;
foreach(item, compPkgList) {
PLpgSQL_compile_context* compPkg = (PLpgSQL_compile_context*)lfirst(item);
if (compPkg->plpgsql_curr_compile_package &&
compPkg->plpgsql_curr_compile_package->pkg_oid == pkgOid) {
return compPkg->plpgsql_curr_compile_package;
}
}
return NULL;
}
static PLpgSQL_package* compilePackageSpec(Oid pkgOid, bool isNestedCompile)
{
/*
* First, search the package in the compile list,
* if exists, just return the compiling package.
*/
PLpgSQL_package* pkg = GetCompileListPkg(pkgOid);
if (unlikely(pkg != NULL)) {
return pkg;
}
int oldCompileStatus = getCompileStatus();
HeapTuple pkgTuple = SearchSysCache1(PACKAGEOID, ObjectIdGetDatum(pkgOid));
bool isnull = false;
if (isNestedCompile) {
if (u_sess->plsql_cxt.curr_compile_context->plpgsql_curr_compile_package != NULL ||
u_sess->plsql_cxt.curr_compile_context->plpgsql_curr_compile != NULL) {
CompileStatusSwtichTo(COMPILIE_PKG);
} else {
ereport(ERROR, (errmodule(MOD_PLSQL), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("nest compile package can't be null.")));
}
} else {
if (oldCompileStatus == NONE_STATUS) {
CompileStatusSwtichTo(COMPILIE_PKG);
} else {
ereport(ERROR, (errmodule(MOD_PLSQL), errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("compile status should be none status.")));
}
}
TokenAuxData temp_pushback_auxdata[MAX_PUSHBACKS];
for (int i = 0; i < MAX_PUSHBACKS; i++) {
temp_pushback_auxdata[i] = pushback_auxdata[i];
}
YYSTYPE temp_lval = plpgsql_yylval;
YYLTYPE temp_lloc = plpgsql_yylloc;
/* compile package spec */
if (HeapTupleIsValid(pkgTuple)) {
(void)SysCacheGetAttr(PACKAGEOID, pkgTuple, Anum_gs_package_pkgbodyinitsrc, &isnull);
}
/* if package don't have instantiation string,we don't need compile package body */
if (isnull) {
pkg = plpgsql_package_validator(pkgOid, true, false);
} else {
pkg = plpgsql_package_validator(pkgOid, false, false);
}
ReleaseSysCache(pkgTuple);
CompileStatusSwtichTo(oldCompileStatus);
for (int i = 0; i < MAX_PUSHBACKS; i++) {
pushback_auxdata[i] = temp_pushback_auxdata[i];
}
plpgsql_yylval = temp_lval;
plpgsql_yylloc = temp_lloc;
return pkg;
}
PLpgSQL_datum* GetPackageDatum(List* name, bool* isSamePackage)
{
char* objname = NULL;
char* pkgname = NULL;
char* schemaname = NULL;
PLpgSQL_package* pkg = NULL;
Oid pkgOid = InvalidOid;
Oid namespaceId = InvalidOid;
Oid currentCompilePkgOid = InvalidOid;
DeconstructQualifiedName(name, &schemaname, &objname, &pkgname);
if (schemaname != NULL) {
namespaceId = LookupNamespaceNoError(schemaname);
}
if (pkgname == NULL) {
return NULL;
}
/* namespace and pkgname has checked. */
pkgOid = PackageNameGetOid(pkgname, namespaceId);
if (!OidIsValid(pkgOid)) {
return NULL;
}
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
/* when create the function parse the in out paramters, curr_compile is null */
if (curr_compile == NULL) {
pkg = compilePackageSpec(pkgOid, false);
if (pkg == NULL) {
return NULL;
}
struct PLpgSQL_nsitem* nse = plpgsql_ns_lookup(pkg->public_ns, false, pkgname, objname, NULL, NULL);
if (nse == NULL || nse->itemtype == PLPGSQL_NSTYPE_REFCURSOR) {
return NULL;
}
return pkg->datums[nse->itemno];
}
if (curr_compile->plpgsql_curr_compile_package != NULL) {
currentCompilePkgOid = curr_compile->plpgsql_curr_compile_package->pkg_oid;
if (currentCompilePkgOid == pkgOid) {
pkg = curr_compile->plpgsql_curr_compile_package;
if (isSamePackage != NULL) {
*isSamePackage = true;
}
}
}
if (u_sess->plsql_cxt.need_pkg_dependencies) {
MemoryContext temp = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER));
u_sess->plsql_cxt.pkg_dependencies =
list_append_unique_oid(u_sess->plsql_cxt.pkg_dependencies, pkgOid);
MemoryContextSwitchTo(temp);
}
if (pkg == NULL) {
MemoryContext temp = MemoryContextSwitchTo(curr_compile->compile_tmp_cxt);
pkg = compilePackageSpec(pkgOid, true);
MemoryContextSwitchTo(temp);
}
/* when compilePackageSpec, the curr compile package maybe invalided, need to check it */
bool currCompPkgInvalid = curr_compile->plpgsql_curr_compile_package != NULL && currentCompilePkgOid != pkgOid
&& curr_compile->plpgsql_curr_compile_package->pkg_cxt == NULL;
if (currCompPkgInvalid) {
ereport(ERROR,
(errmodule(MOD_PLSQL), errcode(ERRCODE_PLPGSQL_ERROR),
errmsg("concurrent error when compile package."),
errdetail("when compile package, it has been invalidated by other session."),
errcause("excessive concurrency"),
erraction("reduce concurrency and retry")));
}
PLpgSQL_datum* resultDatum = SearchPackageDatum(pkg, pkgOid, currentCompilePkgOid, pkgname, objname);
if (resultDatum == NULL) {
return NULL;
}
RecordDependencyOfPkg(pkg, pkgOid, currentCompilePkgOid);
return resultDatum;
}
static void RecordDependencyOfPkg(PLpgSQL_package* pkg, Oid pkgOid, Oid currCompPkgOid)
{
if (pkgOid == currCompPkgOid) {
/* same package, need not record dependency */
return;
}
/* record dependcy of func or package */
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
if (curr_compile->plpgsql_curr_compile == NULL) {
MemoryContext temp = MemoryContextSwitchTo(curr_compile->plpgsql_curr_compile_package->pkg_cxt);
record_pkg_function_dependency(
pkg, &curr_compile->plpgsql_curr_compile_package->invalItems, InvalidOid, pkgOid);
(void)MemoryContextSwitchTo(temp);
} else {
MemoryContext temp = MemoryContextSwitchTo(curr_compile->plpgsql_curr_compile->fn_cxt);
record_pkg_function_dependency(pkg, &curr_compile->plpgsql_curr_compile->invalItems, InvalidOid, pkgOid);
(void)MemoryContextSwitchTo(temp);
}
}
static PLpgSQL_datum* SearchPackageDatum(PLpgSQL_package* pkg, Oid pkgOid, Oid currCompPkgOid,
const char* pkgname, const char* objname)
{
PLpgSQL_compile_context* curr_compile = u_sess->plsql_cxt.curr_compile_context;
struct PLpgSQL_nsitem* nse = NULL;
if (currCompPkgOid == pkgOid) {
/* same package, if not compile package function search top first */
if (curr_compile->plpgsql_curr_compile == NULL) {
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false, objname, NULL, NULL, NULL);
}
if (nse != NULL) {
if (nse->itemtype == PLPGSQL_NSTYPE_REFCURSOR) {
return NULL;
}
return curr_compile->plpgsql_Datums[nse->itemno];
} else {
/* search public second */
nse = plpgsql_ns_lookup(pkg->public_ns, false, pkgname, objname, NULL, NULL);
}
if (nse == NULL) {
/* search private third */
nse = plpgsql_ns_lookup(pkg->private_ns, false, pkgname, objname, NULL, NULL);
}
} else {
nse = plpgsql_ns_lookup(pkg->public_ns, false, pkgname, objname, NULL, NULL);
}
if (nse == NULL || nse->itemtype == PLPGSQL_NSTYPE_REFCURSOR) {
return NULL;
}
return pkg->datums[nse->itemno];
}
bool pushed_bulk_exception()
{
return plpgsql_yylval.wdatum.ident != NULL &&
strncasecmp(plpgsql_yylval.wdatum.ident, "BULK_EXCEPTIONS", strlen("BULK_EXCEPTIONS")) == 0;
}
/* Check if save exception statement is followed by DML */
void CheckSaveExceptionsDML(int errstate)
{
TokenAuxData aux1;
TokenAuxData aux2;
TokenAuxData aux3;
int tok1 = internal_yylex(&aux1);
int tok2 = internal_yylex(&aux2);
int tok3 = internal_yylex(&aux3);
/* check merge statement individually since internal_yylex itself wouldn't resolve unreserved keyword */
if (tok3 != K_INSERT && tok3 != K_UPDATE && tok3 != K_DELETE && tok3 != K_SELECT &&
strcmp(aux3.lval.str, "merge")) {
const char* message = "FORALL must follow DML statement.";
InsertErrorMessage(message, plpgsql_yylloc);
ereport(errstate,
(errcode(ERRCODE_FORALL_NEED_DML),
errmsg("FORALL must follow DML statement.")));
}
push_back_token(tok3, &aux3);
push_back_token(tok2, &aux2);
push_back_token(tok1, &aux1);
}