%{
#include "pl_compiler.h"
#include "ast_cl.h"
#include "typedef_cl.h"
#include "base_compiler.h"
#include "call_cl.h"
#include "cursor_cl.h"
#include "decl_cl.h"
#include "lines_cl.h"
#include "pl_udt.h"
#include "pl_common.h"
#include "pl_gram.h"
#include "gramparse.h"
#include "cond_parser.h"
#include "dml_cl.h"
#include "dml_parser.h"
#include "func_parser.h"
#include "pl_dc.h"
#include "ogsql_dependency.h"
#include "ogsql_privilege.h"
#include "pragma_cl.h"
/* Location tracking support --- simpler than bison's default */
#define YYLLOC_DEFAULT(Current, Rhs, N) \
do { \
if (N) \
(Current) = (Rhs)[1]; \
else \
(Current) = (Rhs)[0]; \
} while (0)
#ifdef YYLEX_PARAM
# define YYLEX yylex (YYLEX_PARAM)
#else
# define YYLEX yylex (yyscanner)
#endif
#define parser_yyerror(msg) \
do { \
plsql_yyerror(yyscanner, msg); \
YYABORT; \
} while (0)
typedef struct st_pl_bison_exception_choice {
pl_exception_t except;
sql_text_t name;
} pl_bison_exception_choice_t;
typedef struct st_pl_bison_cursor_arg {
const char *name;
type_word_t *type;
expr_tree_t *def_expr;
source_location_t loc;
} pl_bison_cursor_arg_t;
typedef enum en_pl_bison_fragment_type {
PL_BISON_FRAGMENT_EXPR_LIST,
PL_BISON_FRAGMENT_EXPR_TREE,
PL_BISON_FRAGMENT_COND_TREE
} pl_bison_fragment_type_t;
extern int plsql_yylex(core_yyscan_t yyscanner);
extern void plsql_push_back_token(int token, core_yyscan_t yyscanner);
static void plsql_yyerror(core_yyscan_t yyscanner, const char* message);
static status_t compile_assign_left_from_sql(sql_stmt_t *stmt, text_t *src, expr_node_t **left);
static status_t parse_expr_from_sql(sql_stmt_t *stmt, text_t *src, pl_line_normal_t *line);
static status_t parse_call_from_sql(sql_stmt_t *stmt, text_t *src, pl_line_normal_t *line, source_location_t loc);
static status_t read_return_sql_construct(sql_stmt_t *stmt, text_t *src, pl_line_return_t *line);
static status_t make_type_word(pl_compiler_t *compiler, type_word_t **type, char *str,
galist_t *typemode, source_location_t loc, bool32 is_name_typemode, bool32 pl_type, bool32 pl_rowtype);
static status_t compile_dynamic_sql_expr(sql_stmt_t *stmt, text_t *src, expr_tree_t **expr);
static status_t compile_static_sql_context(sql_stmt_t *stmt, text_t *src, key_wid_t key_wid, source_location_t loc,
galist_t *input, pl_into_t *into, sql_context_t **context);
static status_t compile_static_sql_line(sql_stmt_t *stmt, text_t *src, key_wid_t key_wid, source_location_t loc,
pl_line_sql_t *line);
static status_t compile_execute_immediate_stmt(core_yyscan_t yyscanner, source_location_t loc);
static status_t compile_execute_into_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler, pl_into_t *into,
int *endtoken);
static status_t compile_execute_bulk_into_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler, pl_into_t *into,
int *endtoken);
static status_t compile_execute_using_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler,
pl_line_execute_t *line, int *endtoken);
static bool32 pl_bison_type_is_sys_refcursor(type_word_t *type);
static status_t init_sys_refcursor_decl(pl_compiler_t *compiler, plv_decl_t *decl, source_location_t loc);
static status_t compile_sys_refcursor_decl(pl_compiler_t *compiler, char *name, source_location_t loc);
static status_t compile_cursor_decl(pl_compiler_t *compiler, char *name, galist_t *args, text_t *query,
source_location_t loc);
static status_t compile_into_var_list(pl_compiler_t *compiler, galist_t *output, expr_node_t *node,
source_location_t loc);
static status_t find_cursor_decl_by_node(pl_compiler_t *compiler, expr_node_t *node, source_location_t loc,
plv_decl_t **decl);
static status_t find_top_loop(pl_compiler_t *compiler, source_location_t loc, pl_line_ctrl_t **line);
static status_t find_named_loop(pl_compiler_t *compiler, source_location_t loc, const char *name,
pl_line_ctrl_t **line);
static status_t get_valid_expr_tree(sql_stmt_t *stmt, text_t *src, expr_tree_t **expr);
static status_t get_valid_cond_tree(sql_stmt_t *stmt, text_t *src, cond_tree_t **cond);
static status_t get_valid_call_tree(sql_stmt_t *stmt, text_t *src, expr_tree_t **expr);
static status_t pl_bison_make_parse_text(sql_stmt_t *stmt, const char *prefix, text_t *body, const char *suffix,
sql_text_t *sql_text);
static status_t pl_bison_column_to_proc_node(sql_stmt_t *stmt, expr_node_t *proc);
static bool tok_is_keyword(int token, union YYSTYPE *lval, int kw_token, const char *kw_str);
static char *pl_token_text(int token, union YYSTYPE *lval);
static char *pl_type_token_text(core_yyscan_t yyscanner, int token, union YYSTYPE *lval);
static bool32 pl_token_text_equal(core_yyscan_t yyscanner, int token, union YYSTYPE *lval, const char *expected);
static status_t pl_read_interval_datatype(core_yyscan_t yyscanner, sql_stmt_t *stmt, char **typename,
galist_t **typemode, galist_t **second_typemode, int *tok);
static text_t *current_label_name(pl_compiler_t *compiler);
static status_t check_end_name(const text_t *expected, const char *actual, source_location_t loc);
static status_t check_current_loop_end_name(pl_compiler_t *compiler, const char *actual, source_location_t loc);
static status_t compile_label_stmt(pl_compiler_t *compiler, const char *name, source_location_t loc);
static status_t compile_goto_stmt(pl_compiler_t *compiler, const char *name, source_location_t loc);
static status_t compile_raise_stmt(pl_compiler_t *compiler, const char *name, source_location_t loc);
static status_t compile_exit_or_continue_stmt(sql_stmt_t *stmt, bool32 is_continue, const char *label_name,
text_t *cond_src, source_location_t loc);
static status_t compile_case_start(sql_stmt_t *stmt, text_t *selector_src, pl_line_case_t **case_line);
static status_t compile_case_when(sql_stmt_t *stmt, text_t *cond_src, pl_line_when_case_t **when_line);
static status_t finish_case_stmt(pl_compiler_t *compiler, galist_t *when_lines, source_location_t loc);
static status_t compile_exception_start(pl_compiler_t *compiler, source_location_t loc,
pl_line_except_t **except_line);
static status_t compile_exception_choice(pl_compiler_t *compiler, const char *name, source_location_t loc,
void **choice);
static status_t compile_exception_when(sql_stmt_t *stmt, galist_t *choices, pl_line_when_t **when_line);
static status_t finish_exception_when(pl_compiler_t *compiler);
static status_t finish_exception_section(pl_compiler_t *compiler, source_location_t loc);
static status_t compile_cursor_arg_decl(pl_compiler_t *compiler, plv_decl_t *cursor, galist_t *decls,
const char *name, type_word_t *type, expr_tree_t *def_expr, source_location_t loc);
static status_t compile_open_cursor_args_stmt(core_yyscan_t yyscanner, expr_node_t *cursor_node,
source_location_t loc);
static status_t compile_open_for_stmt(core_yyscan_t yyscanner, expr_node_t *cursor_node, source_location_t loc);
static status_t compile_fetch_bulk_stmt(core_yyscan_t yyscanner, expr_node_t *cursor_node, source_location_t loc);
static char *pl_bison_identifier_at(core_yyscan_t yyscanner, int offset);
static status_t compile_for_start_stmt(core_yyscan_t yyscanner, const char *index_name, source_location_t loc,
pl_line_for_t **for_line);
static status_t compile_forall_stmt(core_yyscan_t yyscanner, const char *index_name, text_t *lower_src,
source_location_t loc);
static text_t *read_sql_expression(int until, core_yyscan_t yyscanner);
static text_t *read_sql_expression2(int until, int until2, core_yyscan_t yyscanner, int *endtoken);
static text_t *read_sql_construct_from(int start_offset,
int until,
int until2,
int until3,
int until4,
int until5,
int until6,
core_yyscan_t yyscanner,
int *endtoken);
static text_t *read_sql_construct(int until,
int until2,
int until3,
int until4,
int until5,
int until6,
core_yyscan_t yyscanner,
int *endtoken);
union YYSTYPE; /* need forward reference for tok_is_keyword */
%}
%expect 0
%name-prefix "plsql_yy"
%locations
%parse-param {core_yyscan_t yyscanner}
%lex-param {core_yyscan_t yyscanner}
%union {
core_YYSTYPE core_yystype;
/* Keep scalar token fields layout-compatible with core_YYSTYPE. */
int ival;
const char *keyword;
PLword word;
expr_tree_t *expr;
expr_node_t *node;
text_t *text;
type_word_t *type;
char *str;
void *res;
bool boolean;
galist_t *list;
record_attr_t *record_attr;
}
%type <keyword> unreserved_keyword
%type <res> loop_start while_start for_start case_start case_when_clause case_when_header cursor_arg
%type <expr> decl_defval decl_rec_defval cursor_arg_defval
%type <text> expr_until_semi expr_until_then expr_until_loop expr_until_range expr_until_when
%type <text> decl_rec_defval_expr cursor_query cursor_arg_defval_expr
%type <type> decl_datatype
%type <type> opt_collection_index
%type <str> decl_varname simple_name label_name opt_block_end_name opt_loop_end_name opt_exit_label for_index_name
%type <boolean> decl_notnull
%type <list> record_attr_list into_var_list case_when_list exception_choice_list cursor_arg_decls cursor_arg_list
%type <record_attr> record_attr
%token <str> IDENT FCONST SCONST XCONST Op CmpOp COMMENTSTRING SET_USER_IDENT SET_IDENT UNDERSCORE_CHARSET FCONST_F FCONST_D
OPER_CAT OPER_LSHIFT OPER_RSHIFT
%token <ival> ICONST PARAM
%token LEX_ERROR_TOKEN
%token TYPECAST ORA_JOINOP DOT_DOT COLON_EQUALS PARA_EQUALS SET_IDENT_SESSION SET_IDENT_GLOBAL NULLS_FIRST NULLS_LAST
%token <str> SIZE_B SIZE_KB SIZE_MB SIZE_GB SIZE_TB SIZE_PB SIZE_EB
%token <word> T_WORD
%token <node> T_DATUM /* a VAR */
%token <keyword> K_ABSOLUTE
%token <keyword> K_ALIAS
%token <keyword> K_ALL
%token <keyword> K_ALTER
%token <keyword> K_ARRAY
%token <keyword> K_AS
%token <keyword> K_BACKWARD
%token <keyword> K_BEGIN
%token <keyword> K_BULK
%token <keyword> K_BY
%token <keyword> K_CALL
%token <keyword> K_CASE
%token <keyword> K_CATALOG_NAME
%token <keyword> K_CLASS_ORIGIN
%token <keyword> K_CLOSE
%token <keyword> K_COLLATE
%token <keyword> K_COLLECT
%token <keyword> K_COLUMN_NAME
%token <keyword> K_COMMIT
%token <keyword> K_CONDITION
%token <keyword> K_CONSTANT
%token <keyword> K_CONSTRAINT_CATALOG
%token <keyword> K_CONSTRAINT_NAME
%token <keyword> K_CONSTRAINT_SCHEMA
%token <keyword> K_CONTINUE
%token <keyword> K_CURRENT
%token <keyword> K_CURSOR
%token <keyword> K_CURSOR_NAME
%token <keyword> K_DEBUG
%token <keyword> K_DECLARE
%token <keyword> K_DEFAULT
%token <keyword> K_DELETE
%token <keyword> K_DETAIL
%token <keyword> K_DETERMINISTIC
%token <keyword> K_DIAGNOSTICS
%token <keyword> K_DISTINCT
%token <keyword> K_DO
%token <keyword> K_DUMP
%token <keyword> K_ELSE
%token <keyword> K_ELSIF
%token <keyword> K_END
%token <keyword> K_ERRCODE
%token <keyword> K_ERROR
%token <keyword> K_EXCEPT
%token <keyword> K_EXCEPTION
%token <keyword> K_EXCEPTIONS
%token <keyword> K_EXECUTE
%token <keyword> K_EXIT
%token <keyword> K_FALSE
%token <keyword> K_FETCH
%token <keyword> K_FIRST
%token <keyword> K_FOR
%token <keyword> K_FORALL
%token <keyword> K_FOREACH
%token <keyword> K_FORWARD
%token <keyword> K_FOUND
%token <keyword> K_FROM
%token <keyword> K_FUNCTION
%token <keyword> K_GET
%token <keyword> K_GOTO
%token <keyword> K_HANDLER
%token <keyword> K_HINT
%token <keyword> K_IF
%token <keyword> K_IMMEDIATE
%token <keyword> K_INSTANTIATION
%token <keyword> K_IN
%token <keyword> K_INDEX
%token <keyword> K_INFO
%token <keyword> K_INSERT
%token <keyword> K_INTERSECT
%token <keyword> K_INTO
%token <keyword> K_IS
%token <keyword> K_ITERATE
%token <keyword> K_LAST
%token <keyword> K_LEAVE
%token <keyword> K_LIMIT
%token <keyword> K_LOG
%token <keyword> K_LOOP
%token <keyword> K_MERGE
%token <keyword> K_MESSAGE
%token <keyword> K_MESSAGE_TEXT
%token <keyword> K_MOVE
%token <keyword> K_MULTISET
%token <keyword> K_MULTISETS
%token <keyword> K_MYSQL_ERRNO
%token <keyword> K_NUMBER
%token <keyword> K_NEXT
%token <keyword> K_NO
%token <keyword> K_NOT
%token <keyword> K_NOTICE
%token <keyword> K_NULL
%token <keyword> K_OF
%token <keyword> K_OPEN
%token <keyword> K_OPTION
%token <keyword> K_OR
%token <keyword> K_OUT
%token <keyword> K_PACKAGE
%token <keyword> K_PERFORM
%token <keyword> K_PIPE
%token <keyword> K_PG_EXCEPTION_CONTEXT
%token <keyword> K_PG_EXCEPTION_DETAIL
%token <keyword> K_PG_EXCEPTION_HINT
%token <keyword> K_PRAGMA
%token <keyword> K_PRIOR
%token <keyword> K_PROCEDURE
%token <keyword> K_QUERY
%token <keyword> K_RAISE
%token <keyword> K_RECORD
%token <keyword> K_REF
%token <keyword> K_RELATIVE
%token <keyword> K_RELEASE
%token <keyword> K_REPEAT
%token <keyword> K_REPLACE
%token <keyword> K_RESULT_OID
%token <keyword> K_RESIGNAL
%token <keyword> K_RETURN
%token <keyword> K_RETURNED_SQLSTATE
%token <keyword> K_REVERSE
%token <keyword> K_ROLLBACK
%token <keyword> K_ROW
%token <keyword> K_ROWTYPE
%token <keyword> K_ROW_COUNT
%token <keyword> K_SAVE
%token <keyword> K_SAVEPOINT
%token <keyword> K_SCHEMA_NAME
%token <keyword> K_SELECT
%token <keyword> K_SCROLL
%token <keyword> K_SIGNAL
%token <keyword> K_SLICE
%token <keyword> K_SQLEXCEPTION
%token <keyword> K_SQLSTATE
%token <keyword> K_SQLWARNING
%token <keyword> K_STACKED
%token <keyword> K_STRICT
%token <keyword> K_SUBCLASS_ORIGIN
%token <keyword> K_SUBTYPE
%token <keyword> K_SYS_REFCURSOR
%token <keyword> K_TABLE
%token <keyword> K_TABLE_NAME
%token <keyword> K_THEN
%token <keyword> K_TO
%token <keyword> K_TRUE
%token <keyword> K_TYPE
%token <keyword> K_UNION
%token <keyword> K_UNTIL
%token <keyword> K_UPDATE
%token <keyword> K_USE_COLUMN
%token <keyword> K_USE_VARIABLE
%token <keyword> K_USING
%token <keyword> K_VARIABLE_CONFLICT
%token <keyword> K_VARRAY
%token <keyword> K_WARNING
%token <keyword> K_WHEN
%token <keyword> K_WHILE
%token <keyword> K_WITH
%%
pl_body:
pl_function
;
pl_function:
pl_top_block
;
/*
* Procedure/function bodies may start with declarations directly after AS/IS,
* while nested executable blocks must start with DECLARE or BEGIN to avoid
* treating ordinary statements as declaration prefixes.
*/
pl_top_block:
opt_declare_keyword declare_sect_b pl_top_block_body
;
pl_block:
K_DECLARE declare_sect_b pl_block_body
| pl_block_body
;
pl_top_block_body:
block_body_core opt_top_block_end_semi
;
pl_block_body:
block_body_core ';'
;
block_body_core:
K_BEGIN
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
pl_line_begin_t *line = NULL;
text_t *label_name = current_label_name(compiler);
text_t block_name = (label_name == NULL) ? CM_NULL_TEXT : *label_name;
plc_alloc_line(compiler, sizeof(pl_line_begin_t), LINE_BEGIN, (pl_line_ctrl_t **)&line);
plc_push(compiler, (pl_line_ctrl_t *)line, &block_name);
plc_convert_typedecl(compiler, compiler->decls);
line->decls = compiler->decls;
line->type_decls = compiler->type_decls;
line->name = label_name;
if (compiler->body == NULL) {
compiler->body = line;
}
}
proc_sect opt_exception_sect K_END opt_block_end_name
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
pl_line_ctrl_t *line = NULL;
pl_line_begin_t *begin_line = (pl_line_begin_t *)plc_get_current_beginln(compiler);
text_t *expected_name = NULL;
if (begin_line == NULL) {
parser_yyerror("current block expected");
} else {
expected_name = begin_line->name;
if (expected_name == NULL && compiler->stack.depth == 1 && compiler->obj != NULL) {
expected_name = &compiler->obj->name;
}
if (check_end_name(expected_name, $6, @6.loc) != OG_SUCCESS) {
parser_yyerror("Undefined symbol");
}
}
compiler->line_loc = @5.loc;
plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END, (pl_line_ctrl_t **)&line);
if (begin_line != NULL) {
begin_line->end = line;
}
if (plc_pop(compiler, compiler->line_loc, PBE_END, NULL) != OG_SUCCESS) {
parser_yyerror("pop block failed");
}
}
;
/*
* The outer SQL parser may consume the statement delimiter before handing PL
* text to this grammar. Nested blocks keep the explicit terminator in
* pl_block_body.
*/
opt_top_block_end_semi:
';'
| /* EMPTY */
;
opt_declare_keyword:
K_DECLARE
| /* EMPTY */
;
opt_block_end_name:
T_WORD { $$ = $1.ident; }
| /* EMPTY */ { $$ = NULL; }
;
opt_loop_end_name:
label_name { $$ = $1; }
| /* EMPTY */ { $$ = NULL; }
;
opt_exception_sect:
/* EMPTY */
| K_EXCEPTION
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
pl_line_except_t *line = NULL;
if (compile_exception_start(compiler, @1.loc, &line) != OG_SUCCESS) {
parser_yyerror("compile exception failed");
}
}
exception_when_list
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (finish_exception_section(compiler, @1.loc) != OG_SUCCESS) {
parser_yyerror("finish exception failed");
}
}
;
exception_when_list:
exception_when_clause
| exception_when_list exception_when_clause
;
exception_when_clause:
K_WHEN exception_choice_list K_THEN
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_line_when_t *line = NULL;
if (compile_exception_when(stmt, $2, &line) != OG_SUCCESS) {
parser_yyerror("compile exception when failed");
}
}
proc_sect
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (finish_exception_when(compiler) != OG_SUCCESS) {
parser_yyerror("finish exception when failed");
}
}
;
exception_choice_list:
label_name
{
void *choice = NULL;
galist_t *list = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
if (sql_create_list(stmt, &list) != OG_SUCCESS ||
compile_exception_choice(compiler, $1, @1.loc, &choice) != OG_SUCCESS ||
cm_galist_insert(list, choice) != OG_SUCCESS) {
parser_yyerror("compile exception choice failed");
}
$$ = list;
}
| exception_choice_list K_OR label_name
{
void *choice = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_exception_choice(compiler, $3, @3.loc, &choice) != OG_SUCCESS ||
cm_galist_insert($1, choice) != OG_SUCCESS) {
parser_yyerror("compile exception choice failed");
}
$$ = $1;
}
;
proc_sect:
proc_stmts
;
proc_stmts:
proc_stmt
| proc_stmts proc_stmt
;
proc_stmt:
pl_block
| stmt_label
| label_stmts
;
label_stmts:
label_stmt
;
label_stmt:
stmt_assign
| stmt_return
| stmt_null
| stmt_if
| stmt_proc_call
| stmt_execute_immediate
| stmt_loop
| stmt_while
| stmt_for
| stmt_exit
| stmt_continue
| stmt_open
| stmt_fetch
| stmt_close
| stmt_sql
| stmt_commit
| stmt_rollback
| stmt_savepoint
| stmt_case
| stmt_goto
| stmt_raise
| stmt_forall
;
stmt_label:
label_open label_name label_close
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_label_stmt(compiler, $2, @2.loc) != OG_SUCCESS) {
parser_yyerror("compile label failed");
}
}
;
label_open:
OPER_LSHIFT
| Op
{
if (strcmp($1, "<<") != 0) {
parser_yyerror("expected <<");
}
}
;
label_close:
OPER_RSHIFT
| Op
{
if (strcmp($1, ">>") != 0) {
parser_yyerror("expected >>");
}
}
;
label_name:
simple_name { $$ = $1; }
;
stmt_null:
K_NULL ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
pl_line_ctrl_t *line = NULL;
if (plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_NULL, &line) != OG_SUCCESS) {
parser_yyerror("compile null failed");
}
}
;
stmt_assign:
T_DATUM
{
pl_line_normal_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
expr_node_t *left = $1;
text_t *left_src = NULL;
text_t *expr_src = NULL;
bool32 is_proc_call = OG_FALSE;
int tok;
if (yychar == YYEMPTY) {
tok = YYLEX;
} else {
tok = yychar;
yychar = YYEMPTY;
}
if (tok != COLON_EQUALS) {
plsql_push_back_token(tok, yyscanner);
left_src = read_sql_construct_from(@1.offset, COLON_EQUALS, ';', 0, 0, 0, 0, yyscanner, &tok);
if (tok == ';') {
if (plc_alloc_line(compiler, sizeof(pl_line_normal_t), LINE_SETVAL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile procedure call failed");
}
if (parse_call_from_sql(stmt, left_src, line, @1.loc) != OG_SUCCESS) {
YYABORT;
}
if (plc_clone_expr_node(compiler, &line->proc) != OG_SUCCESS) {
parser_yyerror("clone procedure call failed");
}
is_proc_call = OG_TRUE;
} else if (tok != COLON_EQUALS) {
parser_yyerror("':=' expected");
} else if (compile_assign_left_from_sql(stmt, left_src, &left) != OG_SUCCESS) {
parser_yyerror("compile assignment target failed");
}
}
if (!is_proc_call) {
expr_src = read_sql_expression(';', yyscanner);
if (plc_alloc_line(compiler, sizeof(pl_line_normal_t), LINE_SETVAL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile assignment failed");
}
line->left = left;
if (parse_expr_from_sql(stmt, expr_src, line) != OG_SUCCESS ||
plc_clone_expr_node(compiler, &line->left) != OG_SUCCESS ||
plc_clone_expr_tree(compiler, &line->expr) != OG_SUCCESS ||
plc_clone_cond_tree(compiler, &line->cond) != OG_SUCCESS) {
parser_yyerror("compile assignment failed");
}
}
}
;
stmt_return:
K_RETURN expr_until_semi
{
pl_line_return_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
plv_decl_t *decl = NULL;
plv_id_t ret_vid = {
.block = 0,
.id = 0,
.input_id = 0
};
if (plc_alloc_line(compiler, sizeof(pl_line_return_t), LINE_RETURN,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile return failed");
}
if ($2->len == 0) {
if (compiler->type == PL_FUNCTION) {
parser_yyerror("function return value expected");
}
line->expr = NULL;
} else {
if (read_return_sql_construct(stmt, $2, line) != OG_SUCCESS) {
parser_yyerror("compile return expr failed");
}
decl = plc_find_param_by_id(compiler, ret_vid);
if (decl == NULL) {
parser_yyerror("Undefined symbol");
}
if (plc_check_decl_as_left(compiler, decl, @1.loc, NULL) != OG_SUCCESS) {
parser_yyerror("invalid return target");
}
if (plc_verify_expr(compiler, line->expr) != OG_SUCCESS) {
parser_yyerror("verify expr failed");
}
if (plc_clone_expr_tree(compiler, &line->expr) != OG_SUCCESS) {
parser_yyerror("clone expr failed");
}
if (!sql_is_skipped_expr(line->expr)) {
if (plc_verify_stack_var_assign(compiler, decl, line->expr) != OG_SUCCESS) {
parser_yyerror("verify return assign failed");
}
}
}
}
;
stmt_if: stmt_if_expr stmt_elsifs stmt_else K_END K_IF ';'
{
pl_line_ctrl_t *line = NULL;
pl_line_ctrl_t *pop_line = NULL;
pl_line_if_t *if_line = NULL;
pl_line_elsif_t *elsif_line = NULL;
pl_line_else_t *else_line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
compiler->line_loc = @4.loc;
plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END_IF, (pl_line_ctrl_t **)&line);
plc_pop(compiler, compiler->line_loc, PBE_END_IF, &pop_line);
if (pop_line->type == LINE_IF) {
if_line = (pl_line_if_t *)pop_line;
if_line->f_line = line;
if_line->next = line;
} else if (pop_line->type == LINE_ELIF) {
elsif_line = (pl_line_elsif_t *)pop_line;
elsif_line->f_line = line;
elsif_line->next = line;
while (elsif_line->if_line->ctrl.type != LINE_IF) {
elsif_line = (pl_line_elsif_t *)elsif_line->if_line;
elsif_line->next = line;
}
if_line = (pl_line_if_t *)elsif_line->if_line;
if_line->next = line;
} else { /* pop_line is a LINE_ELSE */
else_line = (pl_line_else_t*)pop_line;
if (else_line->if_line->ctrl.type == LINE_IF) {
if_line = (pl_line_if_t *)else_line->if_line;
if_line->next = line;
} else {
elsif_line = (pl_line_elsif_t *)else_line->if_line;
elsif_line->next = line;
while (elsif_line->if_line->ctrl.type != LINE_IF) {
elsif_line = (pl_line_elsif_t *)elsif_line->if_line;
elsif_line->next = line;
}
if_line = (pl_line_if_t *)elsif_line->if_line;
if_line->next = line;
}
}
}
;
stmt_proc_call:
T_WORD
{
pl_line_normal_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
plc_alloc_line(compiler, sizeof(pl_line_normal_t), LINE_SETVAL, (pl_line_ctrl_t **)&line);
if (parse_call_from_sql(stmt, src, line, @1.loc) != OG_SUCCESS) {
YYABORT;
}
if (plc_clone_expr_node(compiler, &line->proc) != OG_SUCCESS) {
parser_yyerror("clone procedure call failed");
}
}
;
stmt_execute_immediate:
K_EXECUTE K_IMMEDIATE
{
if (compile_execute_immediate_stmt(yyscanner, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile execute immediate failed");
}
}
;
loop_start:
K_LOOP
{
pl_line_loop_t *line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
text_t *label_name = current_label_name(compiler);
text_t loop_name = (label_name == NULL) ? CM_NULL_TEXT : *label_name;
if (plc_alloc_line(compiler, sizeof(pl_line_loop_t), LINE_LOOP,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile loop failed");
}
if (plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &loop_name) != OG_SUCCESS) {
parser_yyerror("push loop failed");
}
line->stack_line = CURR_BLOCK_BEGIN(compiler);
$$ = line;
}
;
stmt_loop:
loop_start proc_sect K_END K_LOOP opt_loop_end_name ';'
{
pl_line_loop_t *loop_line = (pl_line_loop_t *)$1;
pl_line_end_loop_t *end_line = NULL;
pl_line_ctrl_t *pop_line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
compiler->line_loc = @3.loc;
if (plc_alloc_line(compiler, sizeof(pl_line_end_loop_t), LINE_END_LOOP,
(pl_line_ctrl_t **)&end_line) != OG_SUCCESS) {
parser_yyerror("compile end loop failed");
}
if (check_current_loop_end_name(compiler, $5, @5.loc) != OG_SUCCESS) {
parser_yyerror("Undefined symbol");
}
if (plc_pop(compiler, compiler->line_loc, PBE_END_LOOP, &pop_line) != OG_SUCCESS) {
parser_yyerror("pop loop failed");
}
end_line->loop = pop_line;
loop_line->next = (pl_line_ctrl_t *)end_line;
}
;
while_start:
K_WHILE expr_until_loop
{
pl_line_while_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *label_name = current_label_name(compiler);
text_t loop_name = (label_name == NULL) ? CM_NULL_TEXT : *label_name;
if (plc_alloc_line(compiler, sizeof(pl_line_while_t), LINE_WHILE,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile while failed");
}
if (get_valid_cond_tree(stmt, $2, &line->cond) != OG_SUCCESS ||
plc_verify_cond(compiler, line->cond) != OG_SUCCESS ||
plc_clone_cond_tree(compiler, &line->cond) != OG_SUCCESS) {
parser_yyerror("compile while condition failed");
}
if (plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &loop_name) != OG_SUCCESS) {
parser_yyerror("push while failed");
}
line->name = label_name;
line->stack_line = CURR_BLOCK_BEGIN(compiler);
$$ = line;
}
;
stmt_while:
while_start proc_sect K_END K_LOOP opt_loop_end_name ';'
{
pl_line_while_t *while_line = (pl_line_while_t *)$1;
pl_line_end_loop_t *end_line = NULL;
pl_line_ctrl_t *pop_line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
compiler->line_loc = @3.loc;
if (plc_alloc_line(compiler, sizeof(pl_line_end_loop_t), LINE_END_LOOP,
(pl_line_ctrl_t **)&end_line) != OG_SUCCESS) {
parser_yyerror("compile end while failed");
}
if (check_current_loop_end_name(compiler, $5, @5.loc) != OG_SUCCESS) {
parser_yyerror("Undefined symbol");
}
if (plc_pop(compiler, compiler->line_loc, PBE_END_LOOP, &pop_line) != OG_SUCCESS) {
parser_yyerror("pop while failed");
}
end_line->loop = pop_line;
while_line->next = (pl_line_ctrl_t *)end_line;
}
;
for_index_name:
T_WORD
{
$$ = $1.ident;
}
| T_DATUM
{
$$ = pl_bison_identifier_at(yyscanner, @1.offset);
if ($$ == NULL) {
parser_yyerror("compile for loop variable failed");
}
}
;
for_start:
K_FOR for_index_name K_IN
{
pl_line_for_t *line = NULL;
if (compile_for_start_stmt(yyscanner, $2, @1.loc, &line) != OG_SUCCESS) {
parser_yyerror("compile for loop failed");
}
$$ = line;
}
;
stmt_for:
for_start proc_sect K_END K_LOOP opt_loop_end_name ';'
{
pl_line_for_t *for_line = (pl_line_for_t *)$1;
pl_line_end_loop_t *end_line = NULL;
pl_line_ctrl_t *pop_line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
compiler->line_loc = @3.loc;
if (plc_alloc_line(compiler, sizeof(pl_line_end_loop_t), LINE_END_LOOP,
(pl_line_ctrl_t **)&end_line) != OG_SUCCESS) {
parser_yyerror("compile end for failed");
}
if (check_current_loop_end_name(compiler, $5, @5.loc) != OG_SUCCESS) {
parser_yyerror("Undefined symbol");
}
if (plc_pop(compiler, compiler->line_loc, PBE_END_LOOP, &pop_line) != OG_SUCCESS) {
parser_yyerror("pop for loop failed");
}
end_line->loop = pop_line;
for_line->next = (pl_line_ctrl_t *)end_line;
}
;
stmt_exit:
K_EXIT opt_exit_label ';'
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (compile_exit_or_continue_stmt(stmt, OG_FALSE, $2, NULL, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile exit failed");
}
}
| K_EXIT opt_exit_label K_WHEN expr_until_semi
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (compile_exit_or_continue_stmt(stmt, OG_FALSE, $2, $4, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile exit when failed");
}
}
;
stmt_continue:
K_CONTINUE opt_exit_label ';'
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (compile_exit_or_continue_stmt(stmt, OG_TRUE, $2, NULL, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile continue failed");
}
}
| K_CONTINUE opt_exit_label K_WHEN expr_until_semi
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (compile_exit_or_continue_stmt(stmt, OG_TRUE, $2, $4, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile continue when failed");
}
}
;
opt_exit_label:
label_name { $$ = $1; }
| /* EMPTY */ { $$ = NULL; }
;
stmt_open:
K_OPEN T_DATUM ';'
{
pl_line_open_t *line = NULL;
plv_decl_t *decl = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (find_cursor_decl_by_node(compiler, $2, @2.loc, &decl) != OG_SUCCESS) {
parser_yyerror("cursor expected");
}
if (decl->cursor.ogx->is_sysref || decl->cursor.ogx->context == NULL) {
parser_yyerror("explicit cursor expected");
}
if (plc_alloc_line(compiler, sizeof(pl_line_open_t), LINE_OPEN,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile open cursor failed");
}
line->vid = decl->vid;
line->exprs = NULL;
}
| K_OPEN T_DATUM '('
{
if (compile_open_cursor_args_stmt(yyscanner, $2, @2.loc) != OG_SUCCESS) {
parser_yyerror("compile open cursor args failed");
}
}
';'
| K_OPEN T_DATUM K_FOR
{
if (compile_open_for_stmt(yyscanner, $2, @2.loc) != OG_SUCCESS) {
parser_yyerror("compile open for failed");
}
}
;
cursor_query:
{
$$ = read_sql_expression(';', yyscanner);
}
;
stmt_fetch:
K_FETCH T_DATUM K_INTO into_var_list ';'
{
pl_line_fetch_t *line = NULL;
plv_decl_t *decl = NULL;
expr_node_t *into_node = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (find_cursor_decl_by_node(compiler, $2, @2.loc, &decl) != OG_SUCCESS) {
parser_yyerror("cursor expected");
}
if (plc_alloc_line(compiler, sizeof(pl_line_fetch_t), LINE_FETCH,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile fetch failed");
}
line->vid = decl->vid;
line->into.output = $4;
line->into.prefetch_rows = INTO_COMMON_PREFETCH_COUNT;
line->into.into_type = INTO_AS_VALUE;
line->into.is_bulk = OG_FALSE;
if (line->into.output->count == 1) {
into_node = (expr_node_t *)cm_galist_get(line->into.output, 0);
if (NODE_DATATYPE(into_node) == OG_TYPE_RECORD) {
line->into.into_type = INTO_AS_REC;
} else if (NODE_DATATYPE(into_node) == OG_TYPE_OBJECT) {
parser_yyerror("type mismatch found at OBJECT type between anonymous record and INTO variables");
}
}
if (decl->cursor.ogx->is_sysref == OG_FALSE && decl->cursor.ogx->context != NULL &&
plc_verify_into_clause(decl->cursor.ogx->context, &line->into, @1.loc) != OG_SUCCESS) {
parser_yyerror("verify fetch into failed");
}
}
| K_FETCH T_DATUM K_BULK
{
if (compile_fetch_bulk_stmt(yyscanner, $2, @2.loc) != OG_SUCCESS) {
parser_yyerror("compile fetch bulk collect failed");
}
}
;
stmt_close:
K_CLOSE T_DATUM ';'
{
pl_line_close_t *line = NULL;
plv_decl_t *decl = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (find_cursor_decl_by_node(compiler, $2, @2.loc, &decl) != OG_SUCCESS) {
parser_yyerror("cursor expected");
}
if (plc_alloc_line(compiler, sizeof(pl_line_close_t), LINE_CLOSE,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile close failed");
}
line->vid = decl->vid;
}
;
into_var_list:
T_DATUM
{
galist_t *list = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (plc_init_galist(compiler, &list) != OG_SUCCESS ||
compile_into_var_list(compiler, list, $1, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile into variable failed");
}
$$ = list;
}
| into_var_list ',' T_DATUM
{
if (compile_into_var_list(
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler,
$1, $3, @3.loc) != OG_SUCCESS) {
parser_yyerror("compile into variable failed");
}
$$ = $1;
}
;
stmt_sql:
K_SELECT
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_SELECT, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
| K_INSERT
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_INSERT, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
| K_UPDATE
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_UPDATE, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
| K_DELETE
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_DELETE, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
| K_MERGE
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_MERGE, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
| K_REPLACE
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_REPLACE, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
| K_WITH
{
pl_line_sql_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
text_t *src = read_sql_construct_from(@1.offset, ';', 0, 0, 0, 0, 0, yyscanner, NULL);
if (plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL,
(pl_line_ctrl_t **)&line) != OG_SUCCESS ||
plc_init_galist(compiler, &line->input) != OG_SUCCESS ||
compile_static_sql_line(stmt, src, KEY_WORD_WITH, @1.loc, line) != OG_SUCCESS) {
YYABORT;
}
}
;
stmt_commit:
K_COMMIT ';'
{
pl_line_ctrl_t *line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_COMMIT, &line) != OG_SUCCESS) {
parser_yyerror("compile commit failed");
}
}
;
stmt_rollback:
K_ROLLBACK ';'
{
pl_line_rollback_t *line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (plc_alloc_line(compiler, sizeof(pl_line_rollback_t), LINE_ROLLBACK,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile rollback failed");
}
line->savepoint = CM_NULL_TEXT;
}
| K_ROLLBACK K_TO T_WORD ';'
{
pl_line_rollback_t *line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
text_t name;
if (plc_alloc_line(compiler, sizeof(pl_line_rollback_t), LINE_ROLLBACK,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile rollback failed");
}
cm_str2text($3.ident, &name);
if (pl_copy_text(compiler->entity, &name, &line->savepoint) != OG_SUCCESS) {
parser_yyerror("copy rollback savepoint failed");
}
}
| K_ROLLBACK K_TO K_SAVEPOINT T_WORD ';'
{
pl_line_rollback_t *line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
text_t name;
if (plc_alloc_line(compiler, sizeof(pl_line_rollback_t), LINE_ROLLBACK,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile rollback failed");
}
cm_str2text($4.ident, &name);
if (pl_copy_text(compiler->entity, &name, &line->savepoint) != OG_SUCCESS) {
parser_yyerror("copy rollback savepoint failed");
}
}
;
stmt_savepoint:
K_SAVEPOINT T_WORD ';'
{
pl_line_savepoint_t *line = NULL;
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
text_t name;
if (plc_alloc_line(compiler, sizeof(pl_line_savepoint_t), LINE_SAVEPOINT,
(pl_line_ctrl_t **)&line) != OG_SUCCESS) {
parser_yyerror("compile savepoint failed");
}
cm_str2text($2.ident, &name);
if (pl_copy_text(compiler->entity, &name, &line->savepoint) != OG_SUCCESS) {
parser_yyerror("copy savepoint failed");
}
}
;
stmt_case:
case_start case_when_list stmt_else K_END K_CASE ';'
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (finish_case_stmt(compiler, $2, @4.loc) != OG_SUCCESS) {
parser_yyerror("compile end case failed");
}
}
;
case_start:
K_CASE expr_until_when
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_line_case_t *line = NULL;
if (compile_case_start(stmt, $2, &line) != OG_SUCCESS) {
parser_yyerror("compile case failed");
}
$$ = line;
}
;
case_when_list:
case_when_clause
{
galist_t *list = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (sql_create_list(stmt, &list) != OG_SUCCESS ||
cm_galist_insert(list, $1) != OG_SUCCESS) {
parser_yyerror("record case when failed");
}
$$ = list;
}
| case_when_list K_WHEN case_when_clause
{
if (cm_galist_insert($1, $3) != OG_SUCCESS) {
parser_yyerror("record case when failed");
}
$$ = $1;
}
;
case_when_clause:
case_when_header proc_sect { $$ = $1; }
;
case_when_header:
expr_until_then
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_line_when_case_t *line = NULL;
if (compile_case_when(stmt, $1, &line) != OG_SUCCESS) {
parser_yyerror("compile case when failed");
}
$$ = line;
}
;
stmt_goto:
K_GOTO label_name ';'
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_goto_stmt(compiler, $2, @2.loc) != OG_SUCCESS) {
parser_yyerror("compile goto failed");
}
}
;
stmt_raise:
K_RAISE ';'
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_raise_stmt(compiler, NULL, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile raise failed");
}
}
| K_RAISE label_name ';'
{
pl_compiler_t *compiler =
(pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_raise_stmt(compiler, $2, @2.loc) != OG_SUCCESS) {
parser_yyerror("compile raise failed");
}
}
;
stmt_forall:
K_FORALL T_WORD K_IN expr_until_range
{
if (compile_forall_stmt(yyscanner, $2.ident, $4, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile forall failed");
}
}
;
stmt_if_expr:
K_IF expr_until_then
{
pl_line_if_t *line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
plc_alloc_line(compiler, sizeof(pl_line_if_t), LINE_IF, (pl_line_ctrl_t **)&line);
if (get_valid_cond_tree(stmt, $2, &line->cond) != OG_SUCCESS) {
parser_yyerror("invalid condition expr");
}
if (plc_verify_cond(compiler, line->cond) != OG_SUCCESS) {
parser_yyerror("verify condition expr failed");
}
plc_clone_cond_tree(compiler, &line->cond);
plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT);
line->t_line = NULL;
}
proc_sect
;
stmt_elsifs: /* EMPTY */
| stmt_elsifs K_ELSIF expr_until_then
{
pl_line_elsif_t *line = NULL;
pl_line_ctrl_t *brother_line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
compiler->line_loc = @2.loc;
plc_alloc_line(compiler, sizeof(pl_line_elsif_t), LINE_ELIF, (pl_line_ctrl_t **)&line);
if (get_valid_cond_tree(stmt, $3, &line->cond) != OG_SUCCESS) {
parser_yyerror("invalid condition expr");
}
if (plc_verify_cond(compiler, line->cond) != OG_SUCCESS) {
parser_yyerror("verify condition expr failed");
}
plc_clone_cond_tree(compiler, &line->cond);
plc_pop(compiler, compiler->line_loc, PBE_ELIF, (pl_line_ctrl_t **)&brother_line);
line->if_line = (pl_line_if_t *)brother_line;
line->if_line->f_line = (pl_line_ctrl_t *)line;
line->t_line = NULL;
plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT);
}
proc_sect
;
stmt_else: /* EMPTY */
| K_ELSE
{
pl_line_ctrl_t *brother_line = NULL;
pl_line_else_t *else_line = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
compiler->line_loc = @1.loc;
plc_alloc_line(compiler, sizeof(pl_line_else_t), LINE_ELSE, (pl_line_ctrl_t **)&else_line);
plc_pop(compiler, compiler->line_loc, PBE_ELSE, (pl_line_ctrl_t **)&brother_line);
else_line->if_line = (pl_line_if_t *)brother_line;
else_line->if_line->f_line = (pl_line_ctrl_t *)else_line;
plc_push_ctl(compiler, (pl_line_ctrl_t *)else_line, &CM_NULL_TEXT);
}
proc_sect
;
expr_until_semi:
{
$$ = read_sql_expression(';', yyscanner);
}
;
expr_until_then:
{
$$ = read_sql_expression(K_THEN, yyscanner);
}
;
expr_until_loop:
{
$$ = read_sql_expression(K_LOOP, yyscanner);
}
;
expr_until_range:
{
$$ = read_sql_expression(DOT_DOT, yyscanner);
}
;
expr_until_when:
{
$$ = read_sql_expression(K_WHEN, yyscanner);
}
;
declare_sect_b:
decl_stmts
| /* EMPTY */
;
decl_stmts:
decl_stmt
| decl_stmts decl_stmt
;
decl_defval: ';'
{ $$ = NULL; }
| decl_defkey expr_until_semi
{
expr_tree_t *expr = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (get_valid_expr_tree(stmt, $2, &expr) != OG_SUCCESS) {
parser_yyerror("invalid default expr");
}
$$ = expr;
}
;
decl_rec_defval:
{ $$ = NULL; }
| decl_defkey decl_rec_defval_expr
{
expr_tree_t *expr = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (get_valid_expr_tree(stmt, $2, &expr) != OG_SUCCESS) {
parser_yyerror("invalid default expr");
}
$$ = expr;
}
;
decl_rec_defval_expr:
{
int tok;
$$ = read_sql_expression2(',', ')', yyscanner, &tok);
plsql_push_back_token(tok, yyscanner);
}
;
decl_defkey: COLON_EQUALS
| K_DEFAULT
;
decl_stmt:
decl_varname decl_datatype decl_notnull decl_defval
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
galist_t *decls = compiler->decls;
plv_decl_t *decl = NULL;
type_word_t *type = $2;
bool32 nullable = $3;
text_t name_text;
bool32 is_sys_refcursor = pl_bison_type_is_sys_refcursor(type);
if (cm_galist_new(decls, sizeof(plv_decl_t), (void **)&decl) != OG_SUCCESS) {
parser_yyerror("alloc declaration failed");
}
errno_t rc = memset_s(decl, sizeof(plv_decl_t), 0, sizeof(plv_decl_t));
knl_securec_check(rc);
decl->vid.block = (int16)compiler->stack.depth;
decl->vid.id = (uint16)(decls->count - 1); // not overflow
decl->loc = @1.loc;
decl->drct = PLV_DIR_NONE;
decl->nullable = (bool8)nullable;
cm_str2text($1, &name_text);
if (pl_copy_text(compiler->entity, &name_text, &decl->name) != OG_SUCCESS) {
parser_yyerror("copy declaration name failed");
}
if (is_sys_refcursor) {
if (!nullable) {
OG_SRC_THROW_ERROR(@1.loc, ERR_INVALID_DATA_TYPE,
"sys_refcursor not null declaration is not supported");
parser_yyerror("compile sys_refcursor failed");
}
if ($4 != NULL) {
parser_yyerror("sys_refcursor default value is not supported");
}
if (init_sys_refcursor_decl(compiler, decl, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile sys_refcursor failed");
}
} else if (!nullable && $4 == NULL) {
OG_SRC_THROW_ERROR(@1.loc, ERR_INVALID_DATA_TYPE,
"type defining, not null declaration must have default value");
parser_yyerror("not null declaration must have default value");
} else if (type->pl_rowtype || type->pl_type) {
plattr_assist_t plattr_ass;
plattr_ass.type = DECL_INHERIT;
plattr_ass.decl = decl;
plattr_ass.decls = compiler->decls;
plattr_ass.is_args = OG_TRUE;
if (plc_bison_compile_plv_type(compiler, &plattr_ass, type) != OG_SUCCESS) {
parser_yyerror("compile pl type failed");
}
if (plc_check_decl_datatype(compiler, decl, OG_FALSE) != OG_SUCCESS) {
parser_yyerror("check pl type failed");
}
} else {
bool32 result = OG_FALSE;
/* check userdef type in block */
if (plc_bison_try_compile_local_type(compiler, decl, type, &result) != OG_SUCCESS) {
parser_yyerror("try compile local type failed");
}
if (!result) {
decl->type = PLV_VAR;
if (plc_bison_compile_type(compiler, PLC_PMODE(decl->drct), &decl->variant.type,
type) != OG_SUCCESS) {
parser_yyerror("compile type failed");
}
if (plc_check_datatype(compiler, &decl->variant.type, OG_FALSE) != OG_SUCCESS) {
parser_yyerror("check type failed");
}
if (decl->variant.type.is_array) {
decl->type = PLV_ARRAY;
}
}
}
if (!is_sys_refcursor && plc_bison_compile_default_def(compiler, decl, $4) != OG_SUCCESS) {
parser_yyerror("verify default expr failed");
}
}
| decl_varname K_EXCEPTION ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
plv_decl_t *decl = NULL;
text_t name_text;
if (cm_galist_new(compiler->decls, sizeof(plv_decl_t), (void **)&decl) != OG_SUCCESS) {
parser_yyerror("alloc exception decl failed");
}
decl->vid.block = (int16)compiler->stack.depth;
decl->vid.id = (uint16)(compiler->decls->count - 1);
decl->loc = @1.loc;
decl->type = PLV_EXCPT;
cm_str2text($1, &name_text);
if (pl_copy_text(compiler->entity, &name_text, &decl->name) != OG_SUCCESS) {
parser_yyerror("copy exception name failed");
}
decl->excpt.is_userdef = OG_TRUE;
decl->excpt.err_code = OG_INVALID_INT32;
}
| decl_varname K_SYS_REFCURSOR ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_sys_refcursor_decl(compiler, $1, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile sys_refcursor failed");
}
}
| K_CURSOR decl_varname cursor_arg_decls K_IS cursor_query
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_cursor_decl(compiler, $2, $3, $5, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile cursor failed");
}
}
| K_CURSOR decl_varname cursor_arg_decls ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_cursor_decl(compiler, $2, $3, NULL, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile cursor failed");
}
}
| K_CURSOR decl_varname K_IS cursor_query
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_cursor_decl(compiler, $2, NULL, $4, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile cursor failed");
}
}
| K_CURSOR decl_varname ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (compile_cursor_decl(compiler, $2, NULL, NULL, @1.loc) != OG_SUCCESS) {
parser_yyerror("compile cursor failed");
}
}
| K_TYPE decl_varname K_IS K_RECORD '(' record_attr_list ')' ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (plc_bison_compile_type_def(compiler, $2, $6, @1.loc, MATCH_RECORD) != OG_SUCCESS) {
parser_yyerror("parse record type failed");
}
}
| K_TYPE decl_varname K_IS K_REF K_CURSOR ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
if (plc_bison_compile_type_def(compiler, $2, NULL, @1.loc, MATCH_REF) != OG_SUCCESS) {
parser_yyerror("parse ref cursor type failed");
}
}
| K_TYPE decl_varname K_IS K_TABLE K_OF decl_datatype opt_collection_index ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
plc_bison_collection_type_def_t type_def = { $2, $6, $7, 0, @1.loc, MATCH_TABLE };
if (plc_bison_compile_collection_type_def(compiler, &type_def) != OG_SUCCESS) {
parser_yyerror("parse table type failed");
}
}
| K_TYPE decl_varname K_IS K_VARRAY '(' ICONST ')' K_OF decl_datatype ';'
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
plc_bison_collection_type_def_t type_def = { $2, $9, NULL, (uint32)$6, @1.loc, MATCH_VARRAY };
if (plc_bison_compile_collection_type_def(compiler, &type_def) != OG_SUCCESS) {
parser_yyerror("parse varray type failed");
}
}
;
opt_collection_index:
K_INDEX K_BY decl_datatype { $$ = $3; }
| /* EMPTY */ { $$ = NULL; }
;
cursor_arg_decls:
'(' cursor_arg_list ')' { $$ = $2; }
| '(' ')' { $$ = NULL; parser_yyerror("cursor parameter expected"); }
;
cursor_arg_list:
cursor_arg
{
galist_t *list = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (sql_create_list(stmt, &list) != OG_SUCCESS ||
cm_galist_insert(list, $1) != OG_SUCCESS) {
parser_yyerror("create cursor arg list failed");
}
$$ = list;
}
| cursor_arg_list ',' cursor_arg
{
if (cm_galist_insert($1, $3) != OG_SUCCESS) {
parser_yyerror("append cursor arg failed");
}
$$ = $1;
}
;
cursor_arg:
simple_name opt_cursor_arg_in decl_datatype cursor_arg_defval
{
pl_bison_cursor_arg_t *arg = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (sql_alloc_mem(stmt->context, sizeof(pl_bison_cursor_arg_t), (void **)&arg) != OG_SUCCESS) {
parser_yyerror("alloc cursor arg failed");
}
arg->name = $1;
arg->type = $3;
arg->def_expr = $4;
arg->loc = @1.loc;
$$ = arg;
}
;
opt_cursor_arg_in:
K_IN
| /* EMPTY */
;
cursor_arg_defval:
decl_defkey cursor_arg_defval_expr
{
expr_tree_t *expr = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (get_valid_expr_tree(stmt, $2, &expr) != OG_SUCCESS) {
parser_yyerror("invalid cursor arg default expr");
}
$$ = expr;
}
| /* EMPTY */ { $$ = NULL; }
;
cursor_arg_defval_expr:
{
int tok;
$$ = read_sql_expression2(',', ')', yyscanner, &tok);
plsql_push_back_token(tok, yyscanner);
}
;
decl_notnull: K_NOT K_NULL
{ $$ = OG_FALSE; }
| /* EMPTY */
{ $$ = OG_TRUE; }
;
record_attr_list: record_attr
{
galist_t *list = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
if (sql_create_list(stmt, &list) != OG_SUCCESS) {
parser_yyerror("create list failed");
}
if (cm_galist_insert(list, $1) != OG_SUCCESS) {
parser_yyerror("insert list failed");
}
$$ = list;
}
| record_attr_list ',' record_attr
{
galist_t *list = $1;
if (cm_galist_insert(list, $3) != OG_SUCCESS) {
parser_yyerror("insert list failed");
}
$$ = list;
}
;
record_attr: decl_varname decl_datatype decl_notnull decl_rec_defval
{
record_attr_t *attr_def = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
if (sql_alloc_mem(compiler->stmt->context, sizeof(record_attr_t), (void **)&attr_def) != OG_SUCCESS) {
parser_yyerror("alloc mem failed");
}
attr_def->name = $1;
attr_def->type = $2;
attr_def->nullable = $3;
attr_def->def_expr = $4;
attr_def->loc = @1.loc;
$$ = attr_def;
}
;
decl_datatype:
{
/* make a type_word_t */
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t*)stmt->pl_compiler;
int tok;
source_location_t loc;
char *typename = NULL;
type_word_t *type = NULL;
bool32 pl_type = OG_FALSE;
bool32 pl_rowtype = OG_FALSE;
bool32 is_interval = OG_FALSE;
bool32 is_name_typemode = OG_FALSE;
bool32 is_char = OG_FALSE;
galist_t *typemode = NULL;
galist_t *second_typemode = NULL;
text_t *text = NULL;
/*
* This empty production can be reduced after bison has already read the
* datatype as lookahead. Consume yychar first so declarations like
* "v int := 0" do not skip "int" and start parsing at ":=".
*/
if (yychar == YYEMPTY) {
tok = YYLEX;
} else {
tok = yychar;
yychar = YYEMPTY;
}
loc = yylloc.loc;
typename = pl_type_token_text(yyscanner, tok, &yylval);
if (typename == NULL) {
parser_yyerror("expected datatype");
}
if (cm_strcmpi(typename, "interval") == 0) {
is_interval = OG_TRUE;
if (pl_read_interval_datatype(yyscanner, stmt, &typename, &typemode,
&second_typemode, &tok) != OG_SUCCESS) {
parser_yyerror("parse interval datatype failed");
}
} else {
while ((tok = YYLEX) == '.') {
tok = YYLEX;
char *sub_name = pl_type_token_text(yyscanner, tok, &yylval);
if (sub_name == NULL) {
parser_yyerror("expected identifier after '.'");
}
if (typemode == NULL) {
sql_create_list(stmt, &typemode);
}
is_name_typemode = OG_TRUE;
cm_galist_new(typemode, sizeof(text_t), (pointer_t *)&text);
cm_str2text(sub_name, text);
}
if (tok == '(') {
expr_tree_t *expr = NULL;
is_name_typemode = OG_FALSE;
if (sql_create_list(stmt, &typemode) != OG_SUCCESS) {
parser_yyerror("create typemode failed");
}
for (;;) {
tok = YYLEX;
if (tok != ICONST) {
parser_yyerror("expected type modifier");
}
if (sql_create_int_const_expr(stmt, &expr, yylval.ival, yylloc.loc) != OG_SUCCESS ||
cm_galist_insert(typemode, expr) != OG_SUCCESS) {
parser_yyerror("append type modifier failed");
}
tok = YYLEX;
if (pl_token_text_equal(yyscanner, tok, &yylval, "char")) {
is_char = OG_TRUE;
tok = YYLEX;
} else if (pl_token_text_equal(yyscanner, tok, &yylval, "byte")) {
is_char = OG_FALSE;
tok = YYLEX;
}
if (tok == ')') {
break;
}
if (tok != ',') {
parser_yyerror("expected ',' or ')' in type modifier");
}
}
tok = YYLEX;
}
if (pl_token_text_equal(yyscanner, tok, &yylval, "unsigned")) {
if (cm_strcmpi(typename, "integer") == 0 || cm_strcmpi(typename, "int") == 0) {
typename = "uint";
} else if (cm_strcmpi(typename, "smallint") == 0 || cm_strcmpi(typename, "short") == 0) {
typename = "usmallint";
} else if (cm_strcmpi(typename, "tinyint") == 0) {
typename = "utinyint";
} else if (cm_strcmpi(typename, "bigint") == 0) {
typename = "ubigint";
} else if (cm_strcmpi(typename, "binary_integer") == 0) {
typename = "uint";
} else if (cm_strcmpi(typename, "binary_bigint") == 0) {
typename = "ubigint";
} else {
parser_yyerror("unexpected unsigned datatype");
}
tok = YYLEX;
}
}
if (tok == '%') {
tok = YYLEX;
if (tok_is_keyword(tok, &yylval, K_TYPE, "type")) {
pl_type = OG_TRUE;
} else if (tok_is_keyword(tok, &yylval, K_ROWTYPE, "rowtype")) {
pl_rowtype = OG_TRUE;
} else {
parser_yyerror("expected TYPE or ROWTYPE");
}
} else {
plsql_push_back_token(tok, yyscanner);
}
if (make_type_word(compiler, &type, typename, typemode, loc, is_name_typemode,
pl_type, pl_rowtype) != OG_SUCCESS) {
parser_yyerror("make type failed");
}
type->is_char = is_char;
if (is_interval) {
type->second_typemde = second_typemode;
}
$$ = type;
}
;
simple_name:
T_WORD { $$ = $1.ident; }
| unreserved_keyword
{
char *tmp = strdup($1);
if (tmp == NULL) {
parser_yyerror("alloc name failed");
}
$$ = tmp;
}
;
decl_varname:
T_WORD
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
text_t name;
cm_str2text($1.ident, &name);
bool32 result = OG_FALSE;
plc_check_duplicate(compiler->decls, (text_t *)&name, $1.quoted, &result);
if (result) {
parser_yyerror("duplicate varname");
}
$$ = $1.ident;
}
| unreserved_keyword
{
pl_compiler_t *compiler = (pl_compiler_t*)og_yyget_extra(yyscanner)->core_yy_extra.stmt->pl_compiler;
char *tmp = strdup($1);
text_t name;
cm_str2text(tmp, &name);
bool32 result = OG_FALSE;
plc_check_duplicate(compiler->decls, (text_t *)&name, OG_FALSE, &result);
if (result) {
parser_yyerror("duplicate varname");
}
$$ = tmp;
}
;
unreserved_keyword:
K_ABSOLUTE
| K_ALIAS
| K_ALTER
| K_ARRAY
| K_AS
| K_BACKWARD
| K_BULK
| K_CALL
| K_CATALOG_NAME
| K_CLASS_ORIGIN
| K_COLLECT
| K_COLUMN_NAME
| K_COMMIT
| K_CONDITION
| K_CONSTANT
| K_CONSTRAINT_CATALOG
| K_CONSTRAINT_NAME
| K_CONSTRAINT_SCHEMA
| K_CONTINUE
| K_CURRENT
| K_CURSOR_NAME
| K_DEBUG
| K_DETAIL
| K_DISTINCT
| K_DUMP
| K_ERRCODE
| K_ERROR
| K_EXCEPT
| K_EXCEPTIONS
| K_FALSE
| K_FIRST
| K_FORWARD
| K_FOUND
| K_HANDLER
| K_HINT
| K_IMMEDIATE
| K_INDEX
| K_INFO
| K_INTERSECT
| K_IS
| K_ITERATE
| K_LAST
| K_LEAVE
| K_LOG
| K_MERGE
| K_MESSAGE
| K_MESSAGE_TEXT
| K_MULTISET
| K_MYSQL_ERRNO
| K_NEXT
| K_NO
| K_NOTICE
| K_OPTION
| K_PACKAGE
| K_INSTANTIATION
| K_PERFORM
| K_PG_EXCEPTION_CONTEXT
| K_PG_EXCEPTION_DETAIL
| K_PG_EXCEPTION_HINT
| K_PIPE
| K_PRAGMA
| K_PRIOR
| K_QUERY
| K_RECORD
| K_RELATIVE
| K_RELEASE
| K_REPEAT
| K_RESIGNAL
| K_RESULT_OID
| K_RETURNED_SQLSTATE
| K_REVERSE
| K_ROLLBACK
| K_ROW
| K_ROW_COUNT
| K_ROWTYPE
| K_SAVE
| K_SCHEMA_NAME
| K_SCROLL
| K_SIGNAL
| K_SLICE
| K_SQLEXCEPTION
| K_SQLSTATE
| K_SQLWARNING
| K_STACKED
| K_SUBCLASS_ORIGIN
| K_SUBTYPE
| K_SYS_REFCURSOR
| K_TABLE
| K_TABLE_NAME
| K_TRUE
| K_UNION
| K_UNTIL
| K_USE_COLUMN
| K_USE_VARIABLE
| K_VARIABLE_CONFLICT
| K_VARRAY
| K_WARNING
| K_WITH
;
%%
static void plsql_yyerror(core_yyscan_t yyscanner, const char* message)
{
OG_SRC_THROW_ERROR(yylloc.loc, ERR_SQL_SYNTAX_ERROR, message);
return;
}
static bool tok_is_keyword(int token, union YYSTYPE *lval, int kw_token, const char *kw_str)
{
if (token == kw_token)
{
return true;
}
// else if (token == T_DATUM)
// {
/*
* It's a variable, so recheck the string name. Note we will not
* match composite names (hence an unreserved word followed by "."
* will not be recognized).
*/
// if (!lval->wdatum.quoted && lval->wdatum.ident != NULL &&
// strcmp(lval->wdatum.ident, kw_str) == 0)
// return true;
// }
return false; /* not the keyword */
}
static char *pl_token_text(int token, union YYSTYPE *lval)
{
if (token == YYEOF) {
return "EOF";
}
if (token == ';') {
return "';'";
}
if (token == ',') {
return "','";
}
if (token == '(') {
return "'('";
}
if (token == ')') {
return "')'";
}
if (token == T_WORD) {
return lval->word.ident;
}
if (token == IDENT) {
return lval->str;
}
if (lval->keyword != NULL) {
return (char *)lval->keyword;
}
return NULL;
}
static char *pl_copy_text_token(sql_stmt_t *stmt, text_t *text)
{
char *buf = NULL;
errno_t rc;
if (sql_alloc_mem(stmt->context, text->len + 1, (void **)&buf) != OG_SUCCESS) {
return NULL;
}
if (text->len != 0) {
rc = memcpy_s(buf, text->len + 1, text->str, text->len);
knl_securec_check(rc);
}
buf[text->len] = '\0';
return buf;
}
static char *pl_type_token_text(core_yyscan_t yyscanner, int token, union YYSTYPE *lval)
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
char *text = NULL;
int token_len;
char *src = NULL;
char *buf = NULL;
errno_t rc;
if (token == T_DATUM && lval->node != NULL) {
var_address_pair_t *pair = sql_get_last_addr_pair(lval->node);
if (pair != NULL && pair->type == UDT_STACK_ADDR && pair->stack->decl != NULL) {
return pl_copy_text_token(stmt, &pair->stack->decl->name);
}
}
text = pl_token_text(token, lval);
if (text != NULL) {
return text;
}
/*
* Declaration datatypes are read by the PL bison parser, while datatype
* keywords are tokenized by the shared SQL scanner. For SQL keywords that
* are not PL tokens, copy the original lexeme instead of depending on the
* core grammar token enum being visible from this generated parser.
*/
token_len = compiler->plsql_yyleng;
if (token_len <= 0) {
return NULL;
}
src = og_yyget_extra(yyscanner)->core_yy_extra.scanbuf + yylloc.offset;
if (!CM_IS_LETER(src[0]) && src[0] != '_') {
return NULL;
}
if (sql_alloc_mem(stmt->context, (uint32)token_len + 1, (void **)&buf) != OG_SUCCESS) {
return NULL;
}
rc = memcpy_s(buf, (uint32)token_len + 1, src, (uint32)token_len);
knl_securec_check(rc);
buf[token_len] = '\0';
return buf;
}
static bool32 pl_token_text_equal(core_yyscan_t yyscanner, int token, union YYSTYPE *lval, const char *expected)
{
char *text = pl_type_token_text(yyscanner, token, lval);
return (text != NULL && cm_strcmpi(text, expected) == 0);
}
static status_t pl_expect_type_token(core_yyscan_t yyscanner, int token, union YYSTYPE *lval, const char *expected)
{
char *actual = pl_type_token_text(yyscanner, token, lval);
if (actual != NULL && cm_strcmpi(actual, expected) == 0) {
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, expected, (actual == NULL) ? "token" : actual);
return OG_ERROR;
}
static status_t pl_append_type_modifier(sql_stmt_t *stmt, galist_t **typemode, int value, source_location_t loc)
{
expr_tree_t *expr = NULL;
if (*typemode == NULL) {
OG_RETURN_IFERR(sql_create_list(stmt, typemode));
}
OG_RETURN_IFERR(sql_create_int_const_expr(stmt, &expr, value, loc));
return cm_galist_insert(*typemode, expr);
}
static status_t pl_read_optional_type_modifier(core_yyscan_t yyscanner, sql_stmt_t *stmt,
galist_t **typemode, int *tok)
{
if (*tok != '(') {
return OG_SUCCESS;
}
for (;;) {
*tok = YYLEX;
if (*tok != ICONST) {
char *actual = pl_token_text(*tok, &yylval);
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "type modifier",
(actual == NULL) ? "token" : actual);
return OG_ERROR;
}
OG_RETURN_IFERR(pl_append_type_modifier(stmt, typemode, yylval.ival, yylloc.loc));
*tok = YYLEX;
if (*tok == ')') {
break;
}
if (*tok != ',') {
char *actual = pl_token_text(*tok, &yylval);
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "',' or ')'",
(actual == NULL) ? "token" : actual);
return OG_ERROR;
}
}
*tok = YYLEX;
return OG_SUCCESS;
}
static status_t pl_read_interval_year_type(core_yyscan_t yyscanner, sql_stmt_t *stmt,
galist_t **typemode, int *tok)
{
*tok = YYLEX;
OG_RETURN_IFERR(pl_read_optional_type_modifier(yyscanner, stmt, typemode, tok));
OG_RETURN_IFERR(pl_expect_type_token(yyscanner, *tok, &yylval, "to"));
*tok = YYLEX;
OG_RETURN_IFERR(pl_expect_type_token(yyscanner, *tok, &yylval, "month"));
*tok = YYLEX;
return OG_SUCCESS;
}
static status_t pl_read_interval_day_type(core_yyscan_t yyscanner, sql_stmt_t *stmt,
galist_t **typemode, galist_t **second_typemode, int *tok)
{
*tok = YYLEX;
OG_RETURN_IFERR(pl_read_optional_type_modifier(yyscanner, stmt, typemode, tok));
OG_RETURN_IFERR(pl_expect_type_token(yyscanner, *tok, &yylval, "to"));
*tok = YYLEX;
OG_RETURN_IFERR(pl_expect_type_token(yyscanner, *tok, &yylval, "second"));
*tok = YYLEX;
return pl_read_optional_type_modifier(yyscanner, stmt, second_typemode, tok);
}
static status_t pl_read_interval_datatype(core_yyscan_t yyscanner, sql_stmt_t *stmt, char **typename,
galist_t **typemode, galist_t **second_typemode, int *tok)
{
*tok = YYLEX;
if (pl_token_text_equal(yyscanner, *tok, &yylval, "year")) {
*typename = "interval year to month";
return pl_read_interval_year_type(yyscanner, stmt, typemode, tok);
}
if (pl_token_text_equal(yyscanner, *tok, &yylval, "day")) {
*typename = "interval day to second";
return pl_read_interval_day_type(yyscanner, stmt, typemode, second_typemode, tok);
}
char *actual = pl_type_token_text(yyscanner, *tok, &yylval);
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "DAY or YEAR", (actual == NULL) ? "token" : actual);
return OG_ERROR;
}
static void init_word_from_name(word_t *word, const char *name, source_location_t loc)
{
errno_t rc = memset_s(word, sizeof(word_t), 0, sizeof(word_t));
knl_securec_check(rc);
word->type = WORD_TYPE_VARIANT;
word->loc = loc;
word->text.loc = loc;
cm_str2text((char *)name, &word->text.value);
}
static text_t *current_label_name(pl_compiler_t *compiler)
{
pl_line_label_t *label = (pl_line_label_t *)compiler->last_line;
if (label == NULL || label->ctrl.type != LINE_LABEL) {
return NULL;
}
return &label->name;
}
static status_t check_end_name(const text_t *expected, const char *actual, source_location_t loc)
{
text_t actual_text;
if (actual == NULL) {
return OG_SUCCESS;
}
cm_str2text((char *)actual, &actual_text);
if (expected != NULL && cm_text_equal_ins((text_t *)expected, &actual_text)) {
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR(loc, ERR_UNDEFINED_SYMBOL_FMT, actual);
return OG_ERROR;
}
static status_t check_current_loop_end_name(pl_compiler_t *compiler, const char *actual, source_location_t loc)
{
if (compiler->control_stack.depth == 0) {
OG_SRC_THROW_ERROR(loc, ERR_PL_UNEXPECTED_FMT, "END LOOP");
return OG_ERROR;
}
return check_end_name(&compiler->control_stack.items[compiler->control_stack.depth - 1].name, actual, loc);
}
static status_t add_label_line(pl_compiler_t *compiler, pl_line_ctrl_t *line)
{
if (compiler->labels.count >= PL_MAX_BLOCK_DEPTH) {
OG_SRC_THROW_ERROR(line->loc, ERR_PL_EXCEED_LABEL_MAX, PL_MAX_BLOCK_DEPTH);
return OG_ERROR;
}
compiler->labels.lines[compiler->labels.count++] = line;
return OG_SUCCESS;
}
static status_t compile_label_stmt(pl_compiler_t *compiler, const char *name, source_location_t loc)
{
word_t word;
pl_line_label_t *line = NULL;
init_word_from_name(&word, name, loc);
OG_RETURN_IFERR(plc_label_name_verify(compiler, &word));
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_label_t), LINE_LABEL, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(pl_copy_object_name_ci(compiler->entity, word.type, (text_t *)&word.text, &line->name));
line->stack_line = CURR_BLOCK_BEGIN(compiler);
return add_label_line(compiler, (pl_line_ctrl_t *)line);
}
static status_t compile_goto_stmt(pl_compiler_t *compiler, const char *name, source_location_t loc)
{
word_t word;
pl_line_goto_t *line = NULL;
init_word_from_name(&word, name, loc);
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_goto_t), LINE_GOTO, (pl_line_ctrl_t **)&line));
return pl_copy_object_name_ci(compiler->entity, word.type, (text_t *)&word.text, &line->label);
}
static status_t compile_raise_decl(pl_compiler_t *compiler, plv_decl_t *decl, word_t *word,
pl_line_raise_t *raise_line)
{
int32 except_id;
if (decl != NULL) {
OG_RETURN_IFERR(pl_copy_name(compiler->entity, (text_t *)&word->text, &raise_line->excpt_name));
raise_line->excpt_info.is_userdef = decl->excpt.is_userdef;
raise_line->excpt_info.error_code =
(decl->excpt.err_code == INVALID_EXCEPTION) ? ERR_USER_DEFINED_EXCEPTION : (int32)decl->excpt.err_code;
raise_line->excpt_info.vid = decl->vid;
return OG_SUCCESS;
}
except_id = pl_get_exception_id(word);
if (except_id == INVALID_EXCEPTION) {
OG_SRC_THROW_ERROR(word->text.loc, ERR_PL_INVALID_EXCEPTION_FMT, W2S(word));
return OG_ERROR;
}
OG_RETURN_IFERR(pl_copy_name(compiler->entity, (text_t *)&word->text, &raise_line->excpt_name));
raise_line->excpt_info.is_userdef = OG_FALSE;
raise_line->excpt_info.error_code = except_id;
return OG_SUCCESS;
}
static status_t compile_raise_stmt(pl_compiler_t *compiler, const char *name, source_location_t loc)
{
word_t word;
pl_line_raise_t *line = NULL;
plv_decl_t *decl = NULL;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_raise_t), LINE_RAISE, (pl_line_ctrl_t **)&line));
if (name != NULL) {
init_word_from_name(&word, name, loc);
OG_RETURN_IFERR(plc_verify_word_as_var(compiler, &word));
plc_find_decl_ex(compiler, &word, PLV_EXCPT, NULL, &decl);
return compile_raise_decl(compiler, decl, &word, line);
}
for (int32 i = (int32)compiler->stack.depth - 1; i >= 0; i--) {
if (compiler->stack.items[i].entry->type == LINE_EXCEPTION) {
line->excpt_name = CM_NULL_TEXT;
line->excpt_info.is_userdef = OG_FALSE;
line->excpt_info.error_code = INVALID_EXCEPTION;
return OG_SUCCESS;
}
}
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT,
"a RAISE statement with no exception name must be inside an exception handler");
return OG_ERROR;
}
static status_t find_named_loop(pl_compiler_t *compiler, source_location_t loc, const char *name,
pl_line_ctrl_t **line)
{
text_t loop_name;
cm_str2text((char *)name, &loop_name);
for (int32 i = (int32)compiler->control_stack.depth - 1; i >= 0; i--) {
pl_line_type_t type = compiler->control_stack.items[i].entry->type;
if ((type == LINE_LOOP || type == LINE_WHILE || type == LINE_FOR) &&
cm_text_equal_ins(&compiler->control_stack.items[i].name, &loop_name)) {
*line = compiler->control_stack.items[i].entry;
return OG_SUCCESS;
}
}
*line = NULL;
OG_SRC_THROW_ERROR_EX(loc, ERR_PL_SYNTAX_ERROR_FMT, "no in loop name %s statement.", name);
return OG_ERROR;
}
static status_t compile_exit_or_continue_stmt(sql_stmt_t *stmt, bool32 is_continue, const char *label_name,
text_t *cond_src, source_location_t loc)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
pl_line_ctrl_t **next = NULL;
void **cond = NULL;
if (is_continue) {
pl_line_continue_t *line = NULL;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_continue_t), LINE_CONTINUE,
(pl_line_ctrl_t **)&line));
cond = &line->cond;
next = &line->next;
} else {
pl_line_exit_t *line = NULL;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_exit_t), LINE_EXIT, (pl_line_ctrl_t **)&line));
cond = &line->cond;
next = &line->next;
}
if (cond_src != NULL) {
OG_RETURN_IFERR(get_valid_cond_tree(stmt, cond_src, (cond_tree_t **)cond));
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)*cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)cond));
} else {
*cond = NULL;
}
return (label_name == NULL) ? find_top_loop(compiler, loc, next) :
find_named_loop(compiler, loc, label_name, next);
}
static status_t pl_bison_clone_fragment_tree(pl_compiler_t *compiler, pl_bison_fragment_type_t type, void *raw_tree,
void **tree)
{
expr_tree_t *expr = NULL;
cond_tree_t *cond = NULL;
galist_t *expr_list = NULL;
switch (type) {
case PL_BISON_FRAGMENT_EXPR_LIST:
expr_list = (galist_t *)raw_tree;
if (expr_list == NULL || expr_list->count == 0) {
OG_THROW_ERROR(ERR_INVALID_EXPRESSION);
return OG_ERROR;
}
expr = (expr_tree_t *)cm_galist_get(expr_list, 0);
if (expr == NULL) {
OG_THROW_ERROR(ERR_INVALID_EXPRESSION);
return OG_ERROR;
}
return sql_clone_expr_tree(compiler->entity, expr, (expr_tree_t **)tree, pl_alloc_mem);
case PL_BISON_FRAGMENT_EXPR_TREE:
expr = (expr_tree_t *)raw_tree;
if (expr == NULL || expr->root == NULL) {
OG_THROW_ERROR(ERR_INVALID_EXPRESSION);
return OG_ERROR;
}
return sql_clone_expr_tree(compiler->entity, expr, (expr_tree_t **)tree, pl_alloc_mem);
case PL_BISON_FRAGMENT_COND_TREE:
cond = (cond_tree_t *)raw_tree;
if (cond == NULL) {
OG_THROW_ERROR(ERR_INVALID_EXPRESSION);
return OG_ERROR;
}
return sql_clone_cond_tree(compiler->entity, cond, (cond_tree_t **)tree, pl_alloc_mem);
default:
OG_THROW_ERROR(ERR_INVALID_EXPRESSION);
return OG_ERROR;
}
}
static status_t pl_bison_parse_fragment_tree(sql_stmt_t *stmt, const char *prefix, text_t *src, const char *suffix,
pl_bison_fragment_type_t type, void **tree)
{
sql_text_t sql_text = { 0 };
sql_stmt_t *sub_stmt = NULL;
sql_stmt_t *save_curr_stmt = stmt->session->current_stmt;
void *raw_tree = NULL;
status_t status = OG_ERROR;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
sql_stmt_t *save_compiler_stmt = NULL;
if (compiler == NULL) {
OG_THROW_ERROR(ERR_INVALID_EXPRESSION);
return OG_ERROR;
}
*tree = NULL;
OGSQL_SAVE_STACK(stmt);
do {
OG_BREAK_IF_ERROR(pl_bison_make_parse_text(stmt, prefix, src, suffix, &sql_text));
OG_BREAK_IF_ERROR(sql_push(stmt, sizeof(sql_stmt_t), (void **)&sub_stmt));
sql_init_stmt(stmt->session, sub_stmt, stmt->id);
sub_stmt->pl_compiler = stmt->pl_compiler;
save_compiler_stmt = compiler->stmt;
sub_stmt->context = NULL;
sub_stmt->session->current_stmt = sub_stmt;
OG_BREAK_IF_ERROR(sql_alloc_context(sub_stmt));
sub_stmt->plsql_mode = stmt->plsql_mode;
sub_stmt->context->type = stmt->context->type;
sub_stmt->context->params = stmt->context->params;
sub_stmt->context->pname_count = stmt->context->pname_count;
compiler->stmt = sub_stmt;
OG_BREAK_IF_ERROR(raw_parser(sub_stmt, &sql_text, &raw_tree));
stmt->context->pname_count = sub_stmt->context->pname_count;
OG_BREAK_IF_ERROR(pl_bison_clone_fragment_tree(compiler, type, raw_tree, tree));
status = OG_SUCCESS;
} while (0);
if (save_compiler_stmt != NULL) {
compiler->stmt = save_compiler_stmt;
}
stmt->session->current_stmt = save_curr_stmt;
if (sub_stmt != NULL) {
sql_release_lob_info(sub_stmt);
sql_release_resource(sub_stmt, OG_TRUE);
sql_release_context(sub_stmt);
}
OGSQL_RESTORE_STACK(stmt);
return status;
}
static status_t get_valid_expr_tree(sql_stmt_t *stmt, text_t *src, expr_tree_t **expr)
{
*expr = NULL;
/*
* DEFAULT is an internal raw-parser entry selector. It lets PL fragments
* reuse the bison SQL expression grammar without depending on session-owned
* legacy parser state.
*/
return pl_bison_parse_fragment_tree(stmt, "DEFAULT ", src, "", PL_BISON_FRAGMENT_EXPR_LIST, (void **)expr);
}
static status_t get_valid_cond_tree(sql_stmt_t *stmt, text_t *src, cond_tree_t **cond)
{
*cond = NULL;
return pl_bison_parse_fragment_tree(stmt, "CHECK (", src, ")", PL_BISON_FRAGMENT_COND_TREE, (void **)cond);
}
static status_t get_valid_call_tree(sql_stmt_t *stmt, text_t *src, expr_tree_t **expr)
{
*expr = NULL;
/* PROCEDURE selects the SQL-bison entry for PL statement-level calls. */
return pl_bison_parse_fragment_tree(stmt, "PROCEDURE ", src, "", PL_BISON_FRAGMENT_EXPR_TREE, (void **)expr);
}
static status_t read_return_sql_construct(sql_stmt_t *stmt, text_t *src, pl_line_return_t *line)
{
return get_valid_expr_tree(stmt, src, &line->expr);
}
static status_t pl_bison_make_parse_text(sql_stmt_t *stmt, const char *prefix, text_t *body, const char *suffix,
sql_text_t *sql_text)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
uint32 prefix_len = (uint32)strlen(prefix);
uint32 suffix_len = (uint32)strlen(suffix);
uint32 sql_len;
char *sql_buf = NULL;
if (prefix_len > OG_MAX_UINT32 - suffix_len ||
body->len > OG_MAX_UINT32 - prefix_len - suffix_len) {
OG_THROW_ERROR(ERR_SQL_TOO_LONG, body->len);
return OG_ERROR;
}
sql_len = prefix_len + body->len + suffix_len;
if (sql_len == OG_MAX_UINT32) {
OG_THROW_ERROR(ERR_SQL_TOO_LONG, body->len);
return OG_ERROR;
}
OG_RETURN_IFERR(sql_stack_alloc(stmt, sql_len + 1, (void **)&sql_buf));
if (prefix_len != 0) {
MEMS_RETURN_IFERR(memcpy_s(sql_buf, sql_len + 1, prefix, prefix_len));
}
if (body->len != 0) {
MEMS_RETURN_IFERR(memcpy_s(sql_buf + prefix_len, sql_len + 1 - prefix_len, body->str, body->len));
}
if (suffix_len != 0) {
MEMS_RETURN_IFERR(memcpy_s(sql_buf + prefix_len + body->len, sql_len + 1 - prefix_len - body->len,
suffix, suffix_len));
}
sql_buf[sql_len] = '\0';
sql_text->str = sql_buf;
sql_text->len = sql_len;
sql_text->loc.line = 1;
sql_text->loc.column = 1;
if (compiler != NULL) {
sql_text->loc = compiler->line_loc;
}
sql_text->implicit = OG_FALSE;
return OG_SUCCESS;
}
static status_t compile_assign_left_from_sql(sql_stmt_t *stmt, text_t *src, expr_node_t **left)
{
expr_tree_t *expr = NULL;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(get_valid_expr_tree(stmt, src, &expr));
if (expr == NULL || expr->root == NULL) {
OG_THROW_ERROR(ERR_PL_SYNTAX_ERROR_FMT, "invalid assignment target");
return OG_ERROR;
}
OG_RETURN_IFERR(plc_check_var_as_left(compiler, expr->root, expr->loc, NULL));
*left = expr->root;
return OG_SUCCESS;
}
static status_t parse_expr_from_sql(sql_stmt_t *stmt, text_t *src, pl_line_normal_t *line)
{
pl_compiler_t *compiler = stmt->pl_compiler;
if (get_valid_expr_tree(stmt, src, &line->expr) == OG_SUCCESS) {
if (plc_verify_setval(compiler, line->left, line->expr) != OG_SUCCESS) {
line->expr = NULL;
return OG_ERROR;
}
return OG_SUCCESS;
}
cm_reset_error();
line->expr = NULL;
OG_RETURN_IFERR(get_valid_cond_tree(stmt, src, &line->cond));
OG_RETURN_IFERR(plc_verify_cond(compiler, line->cond));
return OG_SUCCESS;
}
static status_t parse_call_from_sql(sql_stmt_t *stmt, text_t *src, pl_line_normal_t *line, source_location_t loc)
{
expr_tree_t *call_expr = NULL;
expr_node_t *proc = NULL;
pl_compiler_t *compiler = stmt->pl_compiler;
OG_RETURN_IFERR(get_valid_call_tree(stmt, src, &call_expr));
proc = call_expr->root;
proc->loc = loc;
if (proc->type == EXPR_NODE_COLUMN) {
OG_RETURN_IFERR(pl_bison_column_to_proc_node(stmt, proc));
} else if (proc->type == EXPR_NODE_FUNC || proc->type == EXPR_NODE_USER_FUNC) {
proc->type = EXPR_NODE_PROC;
} else if (proc->type == EXPR_NODE_V_METHOD) {
/* Collection methods such as x.delete(...) are verified by plc_compile_call. */
} else if (proc->type != EXPR_NODE_PROC && proc->type != EXPR_NODE_USER_PROC) {
OG_SRC_THROW_ERROR(proc->loc, ERR_PL_SYNTAX_ERROR_FMT, "an undefined procedure was called");
return OG_ERROR;
}
return plc_compile_call(compiler, proc, line);
}
static void pl_bison_set_word_part(word_t *word, uint32 id, sql_text_t *text)
{
word->ex_words[id].type = WORD_TYPE_VARIANT;
word->ex_words[id].text = *text;
word->ex_count++;
}
static status_t pl_bison_column_to_proc_node(sql_stmt_t *stmt, expr_node_t *proc)
{
word_t word = { 0 };
column_word_t *column = &proc->word.column;
if (column->name.len == 0) {
OG_SRC_THROW_ERROR(proc->loc, ERR_PL_SYNTAX_ERROR_FMT, "an undefined procedure was called");
return OG_ERROR;
}
word.id = OG_INVALID_ID32;
word.type = WORD_TYPE_VARIANT;
word.ori_type = WORD_TYPE_VARIANT;
word.loc = proc->loc;
if (column->user.len != 0) {
word.text = column->user;
pl_bison_set_word_part(&word, 0, &column->table);
pl_bison_set_word_part(&word, 1, &column->name);
} else if (column->table.len != 0) {
word.text = column->table;
pl_bison_set_word_part(&word, 0, &column->name);
} else {
word.text = column->name;
}
OG_RETURN_IFERR(plc_prepare_noarg_call(&word));
proc->type = EXPR_NODE_PROC;
proc->argument = NULL;
return sql_word_as_func(stmt, &word, &proc->word);
}
static expr_tree_t *current_case_selector(pl_compiler_t *compiler)
{
for (int32 i = (int32)compiler->control_stack.depth - 1; i >= 0; i--) {
pl_line_ctrl_t *line = compiler->control_stack.items[i].entry;
if (line->type == LINE_CASE) {
return ((pl_line_case_t *)line)->selector;
}
if (line->type == LINE_WHEN_CASE) {
return ((pl_line_when_case_t *)line)->selector;
}
if (line->type == LINE_ELSE) {
pl_line_else_t *else_line = (pl_line_else_t *)line;
return ((pl_line_when_case_t *)else_line->if_line)->selector;
}
}
return NULL;
}
static status_t compile_case_start(sql_stmt_t *stmt, text_t *selector_src, pl_line_case_t **case_line)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_case_t), LINE_CASE, (pl_line_ctrl_t **)case_line));
if (selector_src->len == 0) {
(*case_line)->selector = NULL;
} else {
OG_RETURN_IFERR(get_valid_expr_tree(stmt, selector_src, &(*case_line)->selector));
OG_RETURN_IFERR(plc_verify_expr(compiler, (*case_line)->selector));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &(*case_line)->selector));
}
return plc_push_ctl(compiler, (pl_line_ctrl_t *)*case_line, &CM_NULL_TEXT);
}
static status_t compile_case_when(sql_stmt_t *stmt, text_t *cond_src, pl_line_when_case_t **when_line)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
pl_line_ctrl_t *brother_line = NULL;
expr_tree_t *selector = current_case_selector(compiler);
if (compiler->control_stack.depth == 0) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_UNEXPECTED_FMT, "CASE WHEN");
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_when_case_t), LINE_WHEN_CASE,
(pl_line_ctrl_t **)when_line));
if (selector == NULL) {
OG_RETURN_IFERR(get_valid_cond_tree(stmt, cond_src, (cond_tree_t **)&(*when_line)->cond));
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)(*when_line)->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)&(*when_line)->cond));
} else {
OG_RETURN_IFERR(get_valid_expr_tree(stmt, cond_src, (expr_tree_t **)&(*when_line)->cond));
OG_RETURN_IFERR(plc_verify_expr(compiler, (expr_tree_t *)(*when_line)->cond));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, (expr_tree_t **)&(*when_line)->cond));
}
OG_RETURN_IFERR(plc_pop(compiler, yylloc.loc, PBE_WHEN_CASE, &brother_line));
(*when_line)->if_line = (brother_line->type == LINE_CASE) ? NULL : (pl_line_if_t *)brother_line;
(*when_line)->t_line = NULL;
(*when_line)->selector = selector;
if (brother_line->type == LINE_WHEN_CASE) {
((pl_line_when_case_t *)brother_line)->f_line = (pl_line_ctrl_t *)*when_line;
}
return plc_push_ctl(compiler, (pl_line_ctrl_t *)*when_line, &CM_NULL_TEXT);
}
static status_t finish_case_stmt(pl_compiler_t *compiler, galist_t *when_lines, source_location_t loc)
{
pl_line_when_case_t *end_case = NULL;
pl_line_ctrl_t *brother_line = NULL;
expr_tree_t *selector = current_case_selector(compiler);
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_when_case_t), LINE_END_CASE,
(pl_line_ctrl_t **)&end_case));
OG_RETURN_IFERR(plc_pop(compiler, loc, PBE_END_CASE, &brother_line));
if (brother_line->type == LINE_WHEN_CASE) {
((pl_line_when_case_t *)brother_line)->f_line = (pl_line_ctrl_t *)end_case;
}
end_case->selector = selector;
for (uint32 i = 0; i < when_lines->count; i++) {
pl_line_when_case_t *when_line = (pl_line_when_case_t *)cm_galist_get(when_lines, i);
when_line->next = (pl_line_ctrl_t *)end_case;
}
return OG_SUCCESS;
}
static status_t compile_exception_start(pl_compiler_t *compiler, source_location_t loc,
pl_line_except_t **except_line)
{
pl_line_begin_t *begin_line = NULL;
text_t block_name = CM_NULL_TEXT;
for (int32 i = (int32)compiler->stack.depth - 1; i >= 0; i--) {
if (compiler->stack.items[i].entry->type == LINE_BEGIN) {
begin_line = (pl_line_begin_t *)compiler->stack.items[i].entry;
break;
}
}
if (begin_line == NULL) {
OG_SRC_THROW_ERROR(loc, ERR_PL_UNEXPECTED_FMT, "symbol exception");
return OG_ERROR;
}
if (begin_line->except != NULL) {
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT, "exception has existed in this \"begin-exception-end\"");
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_except_t), LINE_EXCEPTION,
(pl_line_ctrl_t **)except_line));
OG_RETURN_IFERR(plc_init_galist(compiler, &(*except_line)->excpts));
begin_line->except = (pl_line_ctrl_t *)*except_line;
return plc_push(compiler, (pl_line_ctrl_t *)*except_line, &block_name);
}
static status_t compile_exception_choice(pl_compiler_t *compiler, const char *name, source_location_t loc,
void **choice)
{
word_t word;
plv_decl_t *decl = NULL;
pl_bison_exception_choice_t *item = NULL;
init_word_from_name(&word, name, loc);
OG_RETURN_IFERR(plc_verify_word_as_var(compiler, &word));
plc_find_decl_ex(compiler, &word, PLV_EXCPT, NULL, &decl);
OG_RETURN_IFERR(pl_alloc_mem(compiler->entity, sizeof(pl_bison_exception_choice_t), (void **)&item));
OG_RETURN_IFERR(pl_copy_name(compiler->entity, (text_t *)&word.text, &item->name.value));
item->name.loc = loc;
OG_RETURN_IFERR(plc_compile_exception_set_except(decl, &word, &item->except));
*choice = item;
return OG_SUCCESS;
}
static status_t compile_exception_when(sql_stmt_t *stmt, galist_t *choices, pl_line_when_t **when_line)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
pl_line_begin_t *begin_line = NULL;
for (int32 i = (int32)compiler->stack.depth - 1; i >= 0; i--) {
if (compiler->stack.items[i].entry->type == LINE_BEGIN) {
begin_line = (pl_line_begin_t *)compiler->stack.items[i].entry;
break;
}
}
if (begin_line == NULL || begin_line->except == NULL) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_UNEXPECTED_FMT, "WHEN");
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_when_t), LINE_WHEN, (pl_line_ctrl_t **)when_line));
cm_galist_init(&(*when_line)->excepts, compiler->entity, pl_alloc_mem);
for (uint32 i = 0; i < choices->count; i++) {
pl_bison_exception_choice_t *choice = (pl_bison_exception_choice_t *)cm_galist_get(choices, i);
if (plc_find_line_except(compiler, *when_line, &choice->except, &choice->name) == OG_SUCCESS ||
plc_check_except_exists(compiler, ((pl_line_except_t *)begin_line->except)->excpts, &choice->except,
&choice->name) == OG_SUCCESS) {
return OG_ERROR;
}
if (choice->except.is_userdef == OG_FALSE && choice->except.error_code == OTHERS &&
(*when_line)->excepts.count > 0) {
OG_SRC_THROW_ERROR(choice->name.loc, ERR_PL_SYNTAX_ERROR_FMT,
"no choices may appear with choice OTHERS in an exception handler");
return OG_ERROR;
}
OG_RETURN_IFERR(cm_galist_insert(&(*when_line)->excepts, &choice->except));
}
return cm_galist_insert(((pl_line_except_t *)begin_line->except)->excpts, *when_line);
}
static status_t finish_exception_when(pl_compiler_t *compiler)
{
pl_line_ctrl_t *line_end = NULL;
return plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END_WHEN, &line_end);
}
static status_t finish_exception_section(pl_compiler_t *compiler, source_location_t loc)
{
pl_line_ctrl_t *except_end = NULL;
pl_line_ctrl_t *pop_line = NULL;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END_EXCEPTION, &except_end));
OG_RETURN_IFERR(plc_pop(compiler, loc, PBE_END_EXCEPTION, &pop_line));
((pl_line_except_t *)pop_line)->end = except_end;
return OG_SUCCESS;
}
static status_t compile_dynamic_sql_expr(sql_stmt_t *stmt, text_t *src, expr_tree_t **expr)
{
return get_valid_expr_tree(stmt, src, expr);
}
static bool32 pl_bison_is_ident_char(char c)
{
return (bool32)(CM_IS_LETER(c) || CM_IS_DIGIT(c) || c == '_' || c == '$' || c == '#');
}
static bool32 pl_bison_match_word_at(text_t *src, uint32 pos, const char *word)
{
uint32 word_len = (uint32)strlen(word);
if (pos + word_len > src->len) {
return OG_FALSE;
}
if (pos > 0 && pl_bison_is_ident_char(src->str[pos - 1])) {
return OG_FALSE;
}
if (pos + word_len < src->len && pl_bison_is_ident_char(src->str[pos + word_len])) {
return OG_FALSE;
}
return (cm_strcmpni(src->str + pos, word, word_len) == 0);
}
static void pl_bison_skip_quoted_text(text_t *src, uint32 *pos)
{
char quote = src->str[*pos];
(*pos)++;
while (*pos < src->len) {
if (src->str[*pos] == quote) {
if (quote == '\'' && *pos + 1 < src->len && src->str[*pos + 1] == quote) {
*pos += 2;
continue;
}
(*pos)++;
return;
}
(*pos)++;
}
}
static void pl_bison_skip_comment(text_t *src, uint32 *pos)
{
if (*pos + 1 >= src->len) {
return;
}
if (src->str[*pos] == '-' && src->str[*pos + 1] == '-') {
*pos += 2;
while (*pos < src->len && src->str[*pos] != '\n') {
(*pos)++;
}
return;
}
if (src->str[*pos] == '/' && src->str[*pos + 1] == '*') {
*pos += 2;
while (*pos + 1 < src->len) {
if (src->str[*pos] == '*' && src->str[*pos + 1] == '/') {
*pos += 2;
return;
}
(*pos)++;
}
}
}
static bool32 pl_bison_next_word_is(text_t *src, uint32 *pos, const char *word)
{
while (*pos < src->len && cm_is_space((int)src->str[*pos])) {
(*pos)++;
}
if (!pl_bison_match_word_at(src, *pos, word)) {
return OG_FALSE;
}
*pos += (uint32)strlen(word);
return OG_TRUE;
}
static bool32 pl_bison_find_top_word(text_t *src, const char *word, uint32 start, uint32 *pos)
{
int32 depth = 0;
for (uint32 i = start; i < src->len;) {
if (src->str[i] == '\'' || src->str[i] == '"' || src->str[i] == '`') {
pl_bison_skip_quoted_text(src, &i);
continue;
}
if (i + 1 < src->len &&
((src->str[i] == '-' && src->str[i + 1] == '-') || (src->str[i] == '/' && src->str[i + 1] == '*'))) {
pl_bison_skip_comment(src, &i);
continue;
}
if (src->str[i] == '(') {
depth++;
i++;
continue;
}
if (src->str[i] == ')') {
if (depth > 0) {
depth--;
}
i++;
continue;
}
if (depth == 0 && pl_bison_match_word_at(src, i, word)) {
*pos = i;
return OG_TRUE;
}
i++;
}
return OG_FALSE;
}
static key_wid_t pl_bison_leading_sql_keyword(text_t *src, key_wid_t default_key)
{
text_t trimmed = *src;
cm_trim_text(&trimmed);
if (pl_bison_match_word_at(&trimmed, 0, "select")) {
return KEY_WORD_SELECT;
}
if (pl_bison_match_word_at(&trimmed, 0, "with")) {
return KEY_WORD_WITH;
}
if (pl_bison_match_word_at(&trimmed, 0, "insert")) {
return KEY_WORD_INSERT;
}
if (pl_bison_match_word_at(&trimmed, 0, "update")) {
return KEY_WORD_UPDATE;
}
if (pl_bison_match_word_at(&trimmed, 0, "delete")) {
return KEY_WORD_DELETE;
}
if (pl_bison_match_word_at(&trimmed, 0, "merge")) {
return KEY_WORD_MERGE;
}
if (pl_bison_match_word_at(&trimmed, 0, "replace")) {
return KEY_WORD_REPLACE;
}
return default_key;
}
static status_t pl_bison_check_single_into_target(pl_into_t *into, source_location_t loc)
{
expr_node_t *node = NULL;
into->into_type = INTO_AS_VALUE;
if (into->output->count != 1) {
return OG_SUCCESS;
}
node = (expr_node_t *)cm_galist_get(into->output, 0);
if (NODE_DATATYPE(node) == OG_TYPE_RECORD) {
into->into_type = INTO_AS_REC;
}
if (NODE_DATATYPE(node) == OG_TYPE_OBJECT) {
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT,
"type mismatch found at OBJECT type between anonymous record and INTO variables");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t pl_bison_compile_into_target(sql_stmt_t *stmt, text_t *src, pl_into_t *into, source_location_t loc)
{
expr_node_t *node = NULL;
cm_trim_text(src);
if (src->len == 0) {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "identifier", "EOF");
return OG_ERROR;
}
OG_RETURN_IFERR(compile_assign_left_from_sql(stmt, src, &node));
return compile_into_var_list((pl_compiler_t *)stmt->pl_compiler, into->output, node, loc);
}
static status_t pl_bison_check_bulk_into_target(pl_into_t *into, uint8 *attr_type, source_location_t loc)
{
expr_node_t *node = NULL;
var_address_pair_t *pair = NULL;
plv_decl_t *decl = NULL;
node = (expr_node_t *)cm_galist_get(into->output, into->output->count - 1);
pair = sql_get_last_addr_pair(node);
decl = (pair == NULL || pair->type != UDT_STACK_ADDR || pair->stack == NULL) ? NULL : pair->stack->decl;
if (decl == NULL || decl->type != PLV_COLLECTION || decl->collection->attr_type == UDT_COLLECTION) {
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT,
"cannot mix between single row and multi-row (BULK) in INTO list");
return OG_ERROR;
}
if (into->output->count == 1) {
*attr_type = decl->collection->attr_type;
}
return OG_SUCCESS;
}
static status_t pl_bison_check_bulk_into_targets(pl_into_t *into, uint8 attr_type, source_location_t loc)
{
into->into_type = INTO_AS_COLL;
if (into->output->count != 1) {
return OG_SUCCESS;
}
if (attr_type == UDT_RECORD) {
into->into_type = INTO_AS_COLL_REC;
} else if (attr_type == UDT_OBJECT) {
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT,
"type mismatch found at OBJECT type between anonymous record and INTO variables");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t pl_bison_compile_into_targets(sql_stmt_t *stmt, text_t *src, pl_into_t *into, bool32 is_bulk,
source_location_t loc)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
uint32 start = 0;
int32 depth = 0;
uint8 attr_type = 0;
OG_RETURN_IFERR(plc_init_galist(compiler, &into->output));
into->is_bulk = is_bulk;
into->prefetch_rows = is_bulk ? OG_INVALID_ID32 : INTO_VALUES_PREFETCH_COUNT;
for (uint32 i = 0; i <= src->len; i++) {
if (i < src->len && (src->str[i] == '\'' || src->str[i] == '"' || src->str[i] == '`')) {
pl_bison_skip_quoted_text(src, &i);
i--;
continue;
}
if (i + 1 < src->len &&
((src->str[i] == '-' && src->str[i + 1] == '-') || (src->str[i] == '/' && src->str[i + 1] == '*'))) {
pl_bison_skip_comment(src, &i);
i--;
continue;
}
if (i < src->len && src->str[i] == '(') {
depth++;
continue;
}
if (i < src->len && src->str[i] == ')') {
if (depth > 0) {
depth--;
}
continue;
}
if (i == src->len || (depth == 0 && src->str[i] == ',')) {
text_t item = {
.str = src->str + start,
.len = i - start
};
OG_RETURN_IFERR(pl_bison_compile_into_target(stmt, &item, into, loc));
if (is_bulk) {
OG_RETURN_IFERR(pl_bison_check_bulk_into_target(into, &attr_type, loc));
}
start = i + 1;
}
}
if (is_bulk) {
return pl_bison_check_bulk_into_targets(into, attr_type, loc);
}
return pl_bison_check_single_into_target(into, loc);
}
static status_t pl_bison_sql_without_range(sql_stmt_t *stmt, text_t *src, uint32 begin, uint32 end, text_t *sql)
{
uint32 new_len;
char *buf = NULL;
if (begin >= end || end > src->len) {
*sql = *src;
cm_trim_text(sql);
return OG_SUCCESS;
}
new_len = src->len - (end - begin);
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, new_len + 1, (void **)&buf));
if (begin > 0) {
MEMS_RETURN_IFERR(memcpy_s(buf, new_len + 1, src->str, begin));
}
if (end < src->len) {
MEMS_RETURN_IFERR(memcpy_s(buf + begin, new_len + 1 - begin, src->str + end, src->len - end));
}
buf[new_len] = '\0';
sql->str = buf;
sql->len = new_len;
cm_trim_text(sql);
return OG_SUCCESS;
}
static bool32 pl_bison_find_select_into(text_t *src, uint32 *clause_start, uint32 *target_start,
uint32 *target_end, bool32 *is_bulk)
{
uint32 from_pos;
uint32 into_pos;
uint32 bulk_pos;
uint32 probe;
if (pl_bison_find_top_word(src, "from", 0, &from_pos) &&
(!pl_bison_find_top_word(src, "into", 0, &into_pos) || from_pos < into_pos)) {
return OG_FALSE;
}
if (pl_bison_find_top_word(src, "bulk", 0, &bulk_pos)) {
probe = bulk_pos + (uint32)strlen("bulk");
if (pl_bison_next_word_is(src, &probe, "collect") && pl_bison_next_word_is(src, &probe, "into") &&
pl_bison_find_top_word(src, "from", probe, &from_pos)) {
*clause_start = bulk_pos;
*target_start = probe;
*target_end = from_pos;
*is_bulk = OG_TRUE;
return OG_TRUE;
}
}
if (!pl_bison_find_top_word(src, "into", 0, &into_pos)) {
return OG_FALSE;
}
if (!pl_bison_find_top_word(src, "from", into_pos + (uint32)strlen("into"), &from_pos)) {
return OG_FALSE;
}
*clause_start = into_pos;
*target_start = into_pos + (uint32)strlen("into");
*target_end = from_pos;
*is_bulk = OG_FALSE;
return OG_TRUE;
}
static bool32 pl_bison_find_returning_into(text_t *src, uint32 *clause_start, uint32 *target_start,
uint32 *target_end, bool32 *is_bulk)
{
uint32 return_pos;
uint32 into_pos;
uint32 bulk_pos;
uint32 probe;
if (!pl_bison_find_top_word(src, "returning", 0, &return_pos) &&
!pl_bison_find_top_word(src, "return", 0, &return_pos)) {
return OG_FALSE;
}
if (pl_bison_find_top_word(src, "bulk", return_pos, &bulk_pos)) {
probe = bulk_pos + (uint32)strlen("bulk");
if (pl_bison_next_word_is(src, &probe, "collect") && pl_bison_next_word_is(src, &probe, "into")) {
*clause_start = bulk_pos;
*target_start = probe;
*target_end = src->len;
*is_bulk = OG_TRUE;
return OG_TRUE;
}
}
if (!pl_bison_find_top_word(src, "into", return_pos, &into_pos)) {
return OG_FALSE;
}
*clause_start = into_pos;
*target_start = into_pos + (uint32)strlen("into");
*target_end = src->len;
*is_bulk = OG_FALSE;
return OG_TRUE;
}
static status_t pl_bison_prepare_static_sql(sql_stmt_t *stmt, text_t *src, key_wid_t key_wid, pl_into_t *into,
source_location_t loc, text_t *sql)
{
uint32 clause_start;
uint32 target_start;
uint32 target_end;
bool32 is_bulk = OG_FALSE;
text_t target;
key_wid = pl_bison_leading_sql_keyword(src, key_wid);
if (into != NULL && (key_wid == KEY_WORD_SELECT || key_wid == KEY_WORD_WITH) &&
pl_bison_find_select_into(src, &clause_start, &target_start, &target_end, &is_bulk)) {
target.str = src->str + target_start;
target.len = target_end - target_start;
OG_RETURN_IFERR(pl_bison_compile_into_targets(stmt, &target, into, is_bulk, loc));
return pl_bison_sql_without_range(stmt, src, clause_start, target_end, sql);
}
if (into != NULL &&
(key_wid == KEY_WORD_INSERT || key_wid == KEY_WORD_UPDATE || key_wid == KEY_WORD_DELETE) &&
pl_bison_find_returning_into(src, &clause_start, &target_start, &target_end, &is_bulk)) {
target.str = src->str + target_start;
target.len = target_end - target_start;
OG_RETURN_IFERR(pl_bison_compile_into_targets(stmt, &target, into, is_bulk, loc));
return pl_bison_sql_without_range(stmt, src, clause_start, target_end, sql);
}
*sql = *src;
cm_trim_text(sql);
return OG_SUCCESS;
}
static status_t compile_static_sql_context(sql_stmt_t *stmt, text_t *src, key_wid_t key_wid, source_location_t loc,
galist_t *input, pl_into_t *into, sql_context_t **context)
{
text_t sql = { 0 };
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
pl_entity_t *entity = (pl_entity_t *)compiler->entity;
if (pl_bison_prepare_static_sql(stmt, src, key_wid, into, loc, &sql) != OG_SUCCESS) {
pl_check_and_set_loc(loc);
return OG_ERROR;
}
OGSQL_SAVE_STACK(stmt);
key_wid = pl_bison_leading_sql_keyword(&sql, key_wid);
pl_bison_static_sql_arg_t arg = { context, &sql, key_wid, &loc, &entity->sqls, input };
if (pl_bison_parse_static_sql(stmt, &arg) != OG_SUCCESS) {
OGSQL_RESTORE_STACK(stmt);
return OG_ERROR;
}
OGSQL_RESTORE_STACK(stmt);
if (!(*context)->cacheable) {
pl_entity_uncacheable(compiler->entity);
}
return OG_SUCCESS;
}
static void set_cursor_args_visible(plv_decl_t *decl, bool32 visible)
{
if (decl->cursor.ogx->args == NULL) {
return;
}
for (uint32 i = 0; i < decl->cursor.ogx->args->count; i++) {
plv_decl_t *arg = (plv_decl_t *)cm_galist_get(decl->cursor.ogx->args, i);
arg->arg_type = visible ? PLV_NORMAL_ARG : PLV_CURSOR_ARG;
}
}
static status_t compile_static_cursor_sql_context(pl_compiler_t *compiler, text_t *src, source_location_t loc,
plv_decl_t *decl)
{
text_t sql = { 0 };
sql_stmt_t *stmt = compiler->stmt;
pl_entity_t *entity = (pl_entity_t *)compiler->entity;
key_wid_t key_wid;
status_t status;
if (pl_bison_prepare_static_sql(stmt, src, KEY_WORD_SELECT, NULL, loc, &sql) != OG_SUCCESS) {
pl_check_and_set_loc(loc);
return OG_ERROR;
}
OGSQL_SAVE_STACK(stmt);
key_wid = pl_bison_leading_sql_keyword(&sql, KEY_WORD_SELECT);
/*
* Cursor parameters are hidden from the surrounding PL block, but they must be visible while compiling
* the cursor query itself.
*/
set_cursor_args_visible(decl, OG_TRUE);
pl_bison_static_sql_arg_t arg = {
&decl->cursor.ogx->context, &sql, key_wid, &loc, &entity->sqls, decl->cursor.input
};
status = pl_bison_parse_static_sql(stmt, &arg);
set_cursor_args_visible(decl, OG_FALSE);
if (status != OG_SUCCESS) {
OGSQL_RESTORE_STACK(stmt);
return OG_ERROR;
}
OGSQL_RESTORE_STACK(stmt);
if (!decl->cursor.ogx->context->cacheable) {
pl_entity_uncacheable(compiler->entity);
}
return sql_append_references(&entity->ref_list, decl->cursor.ogx->context);
}
static status_t compile_static_sql_line(sql_stmt_t *stmt, text_t *src, key_wid_t key_wid, source_location_t loc,
pl_line_sql_t *line)
{
pl_compiler_t *compiler = stmt->pl_compiler;
pl_entity_t *entity = compiler->entity;
line->is_dynamic_sql = OG_FALSE;
OG_RETURN_IFERR(compile_static_sql_context(stmt, src, key_wid, loc, line->input, &line->into, &line->context));
if (IS_DML_INTO_PL_VAR(line->context->type) && line->context->rs_columns != NULL) {
OG_RETURN_IFERR(plc_verify_into_clause(line->context, &line->into, line->ctrl.loc));
}
return sql_append_references(&entity->ref_list, line->context);
}
static status_t compile_execute_into_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler, pl_into_t *into,
int *endtoken)
{
expr_node_t *node = NULL;
int tok;
into->is_bulk = OG_FALSE;
OG_RETURN_IFERR(plc_init_galist(compiler, &into->output));
for (;;) {
tok = YYLEX;
if (tok != T_DATUM) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "identifier", pl_token_text(tok, &yylval));
return OG_ERROR;
}
node = yylval.node;
OG_RETURN_IFERR(compile_into_var_list(compiler, into->output, node, yylloc.loc));
tok = YYLEX;
if (tok != ',') {
break;
}
}
into->into_type = INTO_AS_VALUE;
if (into->output->count == 1) {
node = (expr_node_t *)cm_galist_get(into->output, 0);
if (NODE_DATATYPE(node) == OG_TYPE_RECORD) {
into->into_type = INTO_AS_REC;
} else if (NODE_DATATYPE(node) == OG_TYPE_OBJECT) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_SYNTAX_ERROR_FMT,
"type mismatch found at OBJECT type between anonymous record and INTO variables");
return OG_ERROR;
}
}
*endtoken = tok;
return OG_SUCCESS;
}
static status_t compile_execute_bulk_into_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler, pl_into_t *into,
int *endtoken)
{
expr_node_t *node = NULL;
var_address_pair_t *pair = NULL;
plv_decl_t *decl = NULL;
uint8 attr_type = 0;
int tok;
tok = YYLEX;
if (tok != K_COLLECT) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "COLLECT", pl_token_text(tok, &yylval));
return OG_ERROR;
}
tok = YYLEX;
if (tok != K_INTO) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "INTO", pl_token_text(tok, &yylval));
return OG_ERROR;
}
into->is_bulk = OG_TRUE;
into->prefetch_rows = OG_INVALID_ID32;
OG_RETURN_IFERR(plc_init_galist(compiler, &into->output));
for (;;) {
tok = YYLEX;
if (tok != T_DATUM) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "identifier", pl_token_text(tok, &yylval));
return OG_ERROR;
}
node = yylval.node;
pair = sql_get_last_addr_pair(node);
decl = (pair == NULL || pair->type != UDT_STACK_ADDR || pair->stack == NULL) ? NULL : pair->stack->decl;
if (decl == NULL || decl->type != PLV_COLLECTION) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_SYNTAX_ERROR_FMT,
"cannot mix between single row and multi-row (BULK) in INTO list");
return OG_ERROR;
}
if (decl->collection->attr_type == UDT_COLLECTION) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_SYNTAX_ERROR_FMT,
"cannot mix between single row and multi-row (BULK) in INTO list");
return OG_ERROR;
}
OG_RETURN_IFERR(compile_into_var_list(compiler, into->output, node, yylloc.loc));
if (into->output->count == 1) {
attr_type = decl->collection->attr_type;
}
tok = YYLEX;
if (tok != ',') {
break;
}
}
into->into_type = INTO_AS_COLL;
if (into->output->count == 1) {
if (attr_type == UDT_RECORD) {
into->into_type = INTO_AS_COLL_REC;
} else if (attr_type == UDT_OBJECT) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_SYNTAX_ERROR_FMT,
"type mismatch found at OBJECT type between anonymous record and INTO variables");
return OG_ERROR;
}
}
*endtoken = tok;
return OG_SUCCESS;
}
static status_t verify_execute_using_expr(pl_compiler_t *compiler, expr_tree_t *expr)
{
uint32 excl_flags = SQL_EXCL_AGGR | SQL_EXCL_STAR | SQL_EXCL_JOIN | SQL_EXCL_ROWNUM | SQL_EXCL_ROWID |
SQL_EXCL_DEFAULT | SQL_EXCL_SUBSELECT | SQL_EXCL_COLUMN | SQL_EXCL_ROWSCN | SQL_EXCL_ROWNODEID |
SQL_EXCL_METH_PROC | SQL_EXCL_PL_PROC;
return plc_verify_expr_node(compiler, expr->root, NULL, excl_flags);
}
static status_t read_execute_using_expr(core_yyscan_t yyscanner, int first_token, text_t **expr_src, int *endtoken)
{
int expr_start;
if (first_token == K_PRIOR) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
expr_start = yylloc.offset;
plsql_push_back_token(first_token, yyscanner);
*expr_src = read_sql_construct_from(expr_start, ',', ';', 0, 0, 0, 0, yyscanner, endtoken);
return OG_SUCCESS;
}
static status_t compile_execute_using_item(core_yyscan_t yyscanner, pl_compiler_t *compiler,
pl_line_execute_t *line, int *endtoken)
{
plv_direction_t dir = PLV_DIR_IN;
pl_using_expr_t *using_expr = NULL;
expr_tree_t *expr = NULL;
text_t *expr_src = NULL;
int tok = YYLEX;
if (tok == K_IN) {
tok = YYLEX;
if (tok == K_OUT) {
dir = PLV_DIR_INOUT;
tok = YYLEX;
} else {
dir = PLV_DIR_IN;
}
} else if (tok == K_OUT) {
dir = PLV_DIR_OUT;
tok = YYLEX;
}
if (tok == ';' || tok == ',' || tok == YYEOF) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "expression", pl_token_text(tok, &yylval));
return OG_ERROR;
}
OG_RETURN_IFERR(read_execute_using_expr(yyscanner, tok, &expr_src, endtoken));
OG_RETURN_IFERR(get_valid_expr_tree(compiler->stmt, expr_src, &expr));
OG_RETURN_IFERR(verify_execute_using_expr(compiler, expr));
if (dir == PLV_DIR_OUT || dir == PLV_DIR_INOUT) {
OG_RETURN_IFERR(plc_verify_out_expr(compiler, expr, NULL));
OG_RETURN_IFERR(plc_verify_using_out_cursor(compiler, expr));
}
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &expr));
OG_RETURN_IFERR(pl_alloc_mem(compiler->entity, sizeof(pl_using_expr_t), (void **)&using_expr));
using_expr->expr = expr;
using_expr->dir = dir;
return cm_galist_insert(line->using_exprs, using_expr);
}
static status_t compile_execute_using_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler,
pl_line_execute_t *line, int *endtoken)
{
OG_RETURN_IFERR(plc_init_galist(compiler, &line->using_exprs));
for (;;) {
OG_RETURN_IFERR(compile_execute_using_item(yyscanner, compiler, line, endtoken));
if (*endtoken != ',') {
return OG_SUCCESS;
}
}
}
static status_t compile_execute_immediate_stmt(core_yyscan_t yyscanner, source_location_t loc)
{
pl_line_execute_t *line = NULL;
text_t *dynamic_sql_src = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
int endtoken = 0;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_execute_t), LINE_EXECUTE, (pl_line_ctrl_t **)&line));
dynamic_sql_src = read_sql_construct(K_INTO, K_BULK, K_USING, ';', 0, 0, yyscanner, &endtoken);
OG_RETURN_IFERR(compile_dynamic_sql_expr(stmt, dynamic_sql_src, &line->dynamic_sql));
OG_RETURN_IFERR(plc_verify_expr(compiler, line->dynamic_sql));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->dynamic_sql));
if (endtoken == K_INTO) {
OG_RETURN_IFERR(compile_execute_into_clause(yyscanner, compiler, &line->into, &endtoken));
line->into.prefetch_rows = INTO_VALUES_PREFETCH_COUNT;
} else if (endtoken == K_BULK) {
OG_RETURN_IFERR(compile_execute_bulk_into_clause(yyscanner, compiler, &line->into, &endtoken));
}
if (endtoken == K_USING) {
OG_RETURN_IFERR(compile_execute_using_clause(yyscanner, compiler, line, &endtoken));
}
if (endtoken != ';') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
return OG_SUCCESS;
}
static bool32 pl_bison_type_is_sys_refcursor(type_word_t *type)
{
return (type != NULL && type->typemode == NULL && type->str != NULL &&
(cm_strcmpi(type->str, "sys_refcursor") == 0 || cm_strcmpi(type->str, "refcursor") == 0));
}
static status_t init_sys_refcursor_decl(pl_compiler_t *compiler, plv_decl_t *decl, source_location_t loc)
{
decl->loc = loc;
decl->type = PLV_CUR;
OG_RETURN_IFERR(pl_alloc_mem(compiler->entity, sizeof(plv_cursor_context_t), (void **)&decl->cursor.ogx));
decl->cursor.sql.value = CM_NULL_TEXT;
decl->cursor.sql.loc = loc;
decl->cursor.sql.implicit = OG_FALSE;
decl->cursor.ogx->is_sysref = (bool8)OG_TRUE;
decl->cursor.ogx->is_err = (bool8)OG_FALSE;
decl->cursor.ogx->args = NULL;
decl->cursor.ogx->context = NULL;
decl->cursor.input = NULL;
decl->cursor.record = NULL;
return OG_SUCCESS;
}
static status_t compile_sys_refcursor_decl(pl_compiler_t *compiler, char *name, source_location_t loc)
{
plv_decl_t *decl = NULL;
text_t name_text;
OG_RETURN_IFERR(cm_galist_new(compiler->decls, sizeof(plv_decl_t), (void **)&decl));
decl->vid.block = (int16)compiler->stack.depth;
decl->vid.id = (uint16)(compiler->decls->count - 1);
cm_str2text(name, &name_text);
OG_RETURN_IFERR(pl_copy_text(compiler->entity, &name_text, &decl->name));
return init_sys_refcursor_decl(compiler, decl, loc);
}
static status_t check_duplicate_cursor_arg(galist_t *args, const text_t *name, source_location_t loc)
{
for (uint32 i = 0; i < args->count; i++) {
plv_decl_t *arg = (plv_decl_t *)cm_galist_get(args, i);
if (cm_text_equal_ins(&arg->name, name)) {
OG_SRC_THROW_ERROR(loc, ERR_PL_DUP_ARG_FMT, T2S(name), "cursor");
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t compile_cursor_arg_decl(pl_compiler_t *compiler, plv_decl_t *cursor, galist_t *decls,
const char *name, type_word_t *type, expr_tree_t *def_expr, source_location_t loc)
{
plv_decl_t *decl = NULL;
text_t arg_name;
cm_str2text((char *)name, &arg_name);
OG_RETURN_IFERR(check_duplicate_cursor_arg(cursor->cursor.ogx->args, &arg_name, loc));
OG_RETURN_IFERR(cm_galist_new(decls, sizeof(plv_decl_t), (void **)&decl));
decl->vid.block = (int16)compiler->stack.depth;
decl->vid.id = (uint16)(decls->count - 1);
decl->loc = loc;
decl->drct = PLV_DIR_IN;
decl->type = PLV_VAR;
decl->arg_type = PLV_CURSOR_ARG;
OG_RETURN_IFERR(pl_copy_text(compiler->entity, &arg_name, &decl->name));
OG_RETURN_IFERR(plc_bison_compile_type(compiler, PLC_PMODE(decl->drct), &decl->variant.type, type));
OG_RETURN_IFERR(plc_check_decl_datatype(compiler, decl, OG_TRUE));
if (OG_IS_VARLEN_TYPE(decl->variant.type.datatype)) {
decl->variant.type.size = OG_STRING_BUFFER_SIZE;
}
OG_RETURN_IFERR(plc_bison_compile_default_def(compiler, decl, def_expr));
return cm_galist_insert(cursor->cursor.ogx->args, decl);
}
static status_t compile_cursor_decl(pl_compiler_t *compiler, char *name, galist_t *args, text_t *query,
source_location_t loc)
{
plv_decl_t *decl = NULL;
text_t name_text;
OG_RETURN_IFERR(cm_galist_new(compiler->decls, sizeof(plv_decl_t), (void **)&decl));
decl->vid.block = (int16)compiler->stack.depth;
decl->vid.id = (uint16)(compiler->decls->count - 1);
decl->loc = loc;
decl->type = PLV_CUR;
cm_str2text(name, &name_text);
OG_RETURN_IFERR(pl_copy_text(compiler->entity, &name_text, &decl->name));
OG_RETURN_IFERR(pl_alloc_mem(compiler->entity, sizeof(plv_cursor_context_t), (void **)&decl->cursor.ogx));
decl->cursor.ogx->is_sysref = (bool8)OG_FALSE;
decl->cursor.ogx->is_err = (bool8)OG_FALSE;
decl->cursor.ogx->args = NULL;
decl->cursor.ogx->context = NULL;
decl->cursor.sql.value = (query == NULL) ? CM_NULL_TEXT : *query;
OG_RETURN_IFERR(plc_init_galist(compiler, &decl->cursor.input));
if (args != NULL) {
OG_RETURN_IFERR(plc_init_galist(compiler, &decl->cursor.ogx->args));
for (uint32 i = 0; i < args->count; i++) {
pl_bison_cursor_arg_t *arg = (pl_bison_cursor_arg_t *)cm_galist_get(args, i);
OG_RETURN_IFERR(compile_cursor_arg_decl(compiler, decl, compiler->decls, arg->name, arg->type,
arg->def_expr, arg->loc));
}
}
if (query == NULL) {
decl->cursor.ogx->context = NULL;
return OG_SUCCESS;
}
return compile_static_cursor_sql_context(compiler, query, loc, decl);
}
static bool32 token_is_name(int token)
{
return token == T_WORD || (token >= K_ABSOLUTE && token <= K_WITH);
}
static char *token_name_text(int token)
{
if (token == T_WORD) {
return yylval.word.ident;
}
return pl_token_text(token, &yylval);
}
static status_t make_text_from_offsets(core_yyscan_t yyscanner, int start_offset, int end_offset, text_t **src)
{
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
OG_RETURN_IFERR(sql_stack_alloc(stmt, sizeof(text_t), (void **)src));
(*src)->str = og_yyget_extra(yyscanner)->core_yy_extra.scanbuf + start_offset;
(*src)->len = (uint32)(end_offset - start_offset);
return OG_SUCCESS;
}
static status_t compile_into_var_list(pl_compiler_t *compiler, galist_t *output, expr_node_t *node,
source_location_t loc)
{
var_address_t *addr = NULL;
var_address_pair_t *pair = NULL;
plv_decl_t *decl = NULL;
OG_RETURN_IFERR(plc_check_var_as_left(compiler, node, loc, NULL));
addr = NODE_VALUE_PTR(var_address_t, node);
if (addr == NULL || addr->pairs == NULL || addr->pairs->count == 0) {
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT, "unexpected pl-variant occurs");
return OG_ERROR;
}
pair = (var_address_pair_t *)cm_galist_get(addr->pairs, 0);
if (pair == NULL || pair->type != UDT_STACK_ADDR || pair->stack == NULL || pair->stack->decl == NULL) {
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT, "unexpected pl-variant occurs");
return OG_ERROR;
}
decl = pair->stack->decl;
if (addr->pairs->count == 1 && decl->type != PLV_PARAM) {
SET_FUNC_RETURN_TYPE(decl, node);
}
OG_RETURN_IFERR(plc_clone_expr_node(compiler, &node));
return cm_galist_insert(output, node);
}
static status_t find_cursor_decl_by_node(pl_compiler_t *compiler, expr_node_t *node, source_location_t loc,
plv_decl_t **decl)
{
var_address_pair_t *pair = sql_get_last_addr_pair(node);
if (pair == NULL || pair->type != UDT_STACK_ADDR || pair->stack->decl == NULL ||
pair->stack->decl->type != PLV_CUR) {
OG_SRC_THROW_ERROR(loc, ERR_INVALID_CURSOR);
return OG_ERROR;
}
*decl = pair->stack->decl;
return OG_SUCCESS;
}
static status_t compile_open_arg_expr(pl_compiler_t *compiler, galist_t *exprs, text_t *expr_src,
const char *arg_name)
{
expr_tree_t *expr = NULL;
text_t name_text;
OG_RETURN_IFERR(get_valid_expr_tree(compiler->stmt, expr_src, &expr));
OG_RETURN_IFERR(plc_verify_expr(compiler, expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &expr));
if (arg_name != NULL) {
cm_str2text((char *)arg_name, &name_text);
OG_RETURN_IFERR(pl_copy_text(compiler->entity, &name_text, &expr->arg_name));
}
return cm_galist_insert(exprs, expr);
}
static status_t compile_open_arg_list(core_yyscan_t yyscanner, pl_compiler_t *compiler, galist_t *exprs,
int *endtoken)
{
int tok = YYLEX;
int next_tok;
int expr_start;
int delimiter_offset;
char *arg_name = NULL;
text_t *expr_src = NULL;
if (tok == ')') {
*endtoken = tok;
return OG_SUCCESS;
}
for (;;) {
arg_name = NULL;
expr_start = yylloc.offset;
if (token_is_name(tok)) {
char *first_name = token_name_text(tok);
next_tok = YYLEX;
if (next_tok == PARA_EQUALS) {
arg_name = first_name;
tok = YYLEX;
if (tok == ',' || tok == ')' || tok == YYEOF) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "expression", pl_token_text(tok, &yylval));
return OG_ERROR;
}
expr_start = yylloc.offset;
plsql_push_back_token(tok, yyscanner);
expr_src = read_sql_construct_from(expr_start, ',', ')', 0, 0, 0, 0, yyscanner, endtoken);
} else if (next_tok == ',' || next_tok == ')') {
delimiter_offset = yylloc.offset;
*endtoken = next_tok;
OG_RETURN_IFERR(make_text_from_offsets(yyscanner, expr_start, delimiter_offset, &expr_src));
} else {
plsql_push_back_token(next_tok, yyscanner);
expr_src = read_sql_construct_from(expr_start, ',', ')', 0, 0, 0, 0, yyscanner, endtoken);
}
} else {
plsql_push_back_token(tok, yyscanner);
expr_src = read_sql_construct_from(expr_start, ',', ')', 0, 0, 0, 0, yyscanner, endtoken);
}
OG_RETURN_IFERR(compile_open_arg_expr(compiler, exprs, expr_src, arg_name));
if (*endtoken == ')') {
return OG_SUCCESS;
}
if (*endtoken != ',') {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "',' or ')'", pl_token_text(*endtoken, &yylval));
return OG_ERROR;
}
tok = YYLEX;
if (tok == ')' || tok == YYEOF) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "expression", pl_token_text(tok, &yylval));
return OG_ERROR;
}
}
}
static status_t compile_open_cursor_args_stmt(core_yyscan_t yyscanner, expr_node_t *cursor_node,
source_location_t loc)
{
pl_line_open_t *line = NULL;
plv_decl_t *decl = NULL;
int endtoken = 0;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(find_cursor_decl_by_node(compiler, cursor_node, loc, &decl));
if (decl->cursor.ogx->is_sysref || decl->cursor.ogx->context == NULL) {
OG_SRC_THROW_ERROR(loc, ERR_INVALID_CURSOR);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_open_t), LINE_OPEN, (pl_line_ctrl_t **)&line));
line->vid = decl->vid;
OG_RETURN_IFERR(plc_init_galist(compiler, &line->exprs));
OG_RETURN_IFERR(compile_open_arg_list(yyscanner, compiler, line->exprs, &endtoken));
if (endtoken != ')') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "')'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
if (line->exprs->count == 0) {
line->exprs = NULL;
return OG_SUCCESS;
}
return plc_verify_cursor_args(compiler, line->exprs, decl->cursor.ogx->args, loc);
}
static status_t compile_refcur_using_item(core_yyscan_t yyscanner, pl_compiler_t *compiler, galist_t *using_exprs,
int *endtoken)
{
expr_tree_t *expr = NULL;
text_t *expr_src = NULL;
int tok = YYLEX;
if (tok == K_IN) {
tok = YYLEX;
}
if (tok == K_OUT) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"OUT and IN/OUT modes cannot be opened in refcursor");
return OG_ERROR;
}
if (tok == ',' || tok == ';' || tok == YYEOF) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "expression", pl_token_text(tok, &yylval));
return OG_ERROR;
}
expr_src = read_sql_construct_from(yylloc.offset, ',', ';', 0, 0, 0, 0, yyscanner, endtoken);
OG_RETURN_IFERR(get_valid_expr_tree(compiler->stmt, expr_src, &expr));
OG_RETURN_IFERR(plc_verify_expr(compiler, expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &expr));
return cm_galist_insert(using_exprs, expr);
}
static status_t compile_refcur_using_clause(core_yyscan_t yyscanner, pl_compiler_t *compiler,
pl_line_open_t *line, int *endtoken)
{
OG_RETURN_IFERR(plc_init_galist(compiler, &line->using_exprs));
for (;;) {
OG_RETURN_IFERR(compile_refcur_using_item(yyscanner, compiler, line->using_exprs, endtoken));
if (*endtoken != ',') {
return OG_SUCCESS;
}
}
}
static status_t compile_open_for_stmt(core_yyscan_t yyscanner, expr_node_t *cursor_node, source_location_t loc)
{
pl_line_open_t *line = NULL;
plv_decl_t *decl = NULL;
text_t *src = NULL;
int endtoken = 0;
int tok;
source_location_t query_loc;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(find_cursor_decl_by_node(compiler, cursor_node, loc, &decl));
if (!decl->cursor.ogx->is_sysref) {
OG_SRC_THROW_ERROR(loc, ERR_INVALID_CURSOR);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_open_t), LINE_OPEN, (pl_line_ctrl_t **)&line));
line->vid = decl->vid;
tok = YYLEX;
query_loc = yylloc.loc;
if (tok == K_SELECT) {
src = read_sql_construct_from(yylloc.offset, ';', 0, 0, 0, 0, 0, yyscanner, &endtoken);
if (endtoken != ';') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
line->is_dynamic_sql = OG_FALSE;
OG_RETURN_IFERR(plc_init_galist(compiler, &line->input));
decl->cursor.input = line->input;
if (compile_static_sql_context(stmt, src, KEY_WORD_SELECT, query_loc, line->input, NULL,
&line->context) != OG_SUCCESS) {
decl->cursor.input = NULL;
return OG_ERROR;
}
OG_RETURN_IFERR(sql_append_references(&((pl_entity_t *)compiler->entity)->ref_list, line->context));
decl->cursor.input = NULL;
return OG_SUCCESS;
}
if (tok == YYEOF) {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "cursor query", "EOF");
return OG_ERROR;
}
src = read_sql_construct_from(yylloc.offset, K_USING, ';', 0, 0, 0, 0, yyscanner, &endtoken);
line->is_dynamic_sql = OG_TRUE;
OG_RETURN_IFERR(get_valid_expr_tree(stmt, src, &line->dynamic_sql));
OG_RETURN_IFERR(plc_verify_expr(compiler, line->dynamic_sql));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->dynamic_sql));
if (endtoken == K_USING) {
OG_RETURN_IFERR(compile_refcur_using_clause(yyscanner, compiler, line, &endtoken));
}
if (endtoken != ';') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t compile_fetch_bulk_stmt(core_yyscan_t yyscanner, expr_node_t *cursor_node, source_location_t loc)
{
pl_line_fetch_t *line = NULL;
plv_decl_t *decl = NULL;
int endtoken = 0;
text_t *limit_src = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(find_cursor_decl_by_node(compiler, cursor_node, loc, &decl));
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_fetch_t), LINE_FETCH, (pl_line_ctrl_t **)&line));
line->vid = decl->vid;
OG_RETURN_IFERR(compile_execute_bulk_into_clause(yyscanner, compiler, &line->into, &endtoken));
if (endtoken == K_LIMIT) {
limit_src = read_sql_expression(';', yyscanner);
OG_RETURN_IFERR(get_valid_expr_tree(stmt, limit_src, &line->into.limit));
OG_RETURN_IFERR(plc_verify_limit_expr(compiler, line->into.limit));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->into.limit));
endtoken = ';';
}
if (endtoken != ';') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
if (decl->cursor.ogx->is_sysref == OG_FALSE && decl->cursor.ogx->context != NULL) {
return plc_verify_into_clause(decl->cursor.ogx->context, &line->into, loc);
}
return OG_SUCCESS;
}
static status_t copy_context_rscols_to_record(pl_compiler_t *compiler, sql_context_t *sql_ctx, plv_record_t *record,
source_location_t loc)
{
for (uint32 col_id = 0; col_id < sql_ctx->rs_columns->count; col_id++) {
rs_column_t *col = (rs_column_t *)cm_galist_get(sql_ctx->rs_columns, col_id);
plv_record_attr_t *attr = NULL;
if (col->typmod.is_array) {
OG_SRC_THROW_ERROR(loc, ERR_PL_UNSUPPORT);
return OG_ERROR;
}
attr = udt_record_alloc_attr(compiler->entity, record);
if (attr == NULL) {
pl_check_and_set_loc(loc);
return OG_ERROR;
}
OG_RETURN_IFERR(pl_copy_name_cs(compiler->entity, &col->name, &attr->name, OG_FALSE));
attr->type = UDT_SCALAR;
OG_RETURN_IFERR(pl_alloc_mem(compiler->entity, sizeof(field_scalar_info_t), (void **)&attr->scalar_field));
attr->scalar_field->type_mode = col->typmod;
attr->default_expr = NULL;
attr->nullable = OG_FALSE;
if (attr->scalar_field->type_mode.datatype != OG_TYPE_UNKNOWN) {
OG_RETURN_IFERR(plc_check_datatype(compiler, &attr->scalar_field->type_mode, OG_FALSE));
}
}
return OG_SUCCESS;
}
static status_t init_for_line_common(pl_compiler_t *compiler, const char *index_name, pl_line_for_t **for_line,
bool32 is_cursor)
{
text_t idx_name;
text_t *label_name = current_label_name(compiler);
text_t loop_name = (label_name == NULL) ? CM_NULL_TEXT : *label_name;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_for_t), LINE_FOR, (pl_line_ctrl_t **)for_line));
OG_RETURN_IFERR(plc_init_galist(compiler, &(*for_line)->decls));
OG_RETURN_IFERR(cm_galist_new((*for_line)->decls, sizeof(plv_decl_t), (void **)&(*for_line)->id));
(*for_line)->id->vid.block = (int16)compiler->stack.depth;
(*for_line)->id->vid.id = 0;
cm_str2text((char *)index_name, &idx_name);
OG_RETURN_IFERR(pl_copy_name(compiler->entity, &idx_name, &(*for_line)->id->name));
(*for_line)->is_cur = is_cursor;
(*for_line)->is_push = OG_FALSE;
(*for_line)->name = label_name;
OG_RETURN_IFERR(plc_push(compiler, (pl_line_ctrl_t *)*for_line, &loop_name));
return plc_push_ctl(compiler, (pl_line_ctrl_t *)*for_line, &loop_name);
}
static status_t init_for_cursor_record(pl_compiler_t *compiler, pl_line_for_t *line)
{
plv_decl_t *type_record = NULL;
line->id->type = PLV_RECORD;
OG_RETURN_IFERR(cm_galist_new(line->decls, sizeof(plv_decl_t), (void **)&type_record));
type_record->vid.block = (int16)compiler->stack.depth;
type_record->vid.id = (uint16)(line->decls->count - 1);
type_record->type = PLV_TYPE;
type_record->typdef.type = PLV_RECORD;
type_record->typdef.record.root = type_record;
type_record->typdef.record.is_anonymous = OG_TRUE;
line->id->record = &type_record->typdef.record;
return OG_SUCCESS;
}
static status_t finish_numeric_for_start(core_yyscan_t yyscanner, const char *index_name, bool32 reverse,
text_t *lower_src, source_location_t loc, pl_line_for_t **for_line)
{
text_t *upper_src = read_sql_expression(K_LOOP, yyscanner);
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(init_for_line_common(compiler, index_name, for_line, OG_FALSE));
(*for_line)->id->type = PLV_VAR;
(*for_line)->id->variant.type.datatype = OG_TYPE_INTEGER;
OG_RETURN_IFERR(get_valid_expr_tree(stmt, lower_src, &(*for_line)->lower_expr));
OG_RETURN_IFERR(plc_verify_expr(compiler, (*for_line)->lower_expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &(*for_line)->lower_expr));
OG_RETURN_IFERR(get_valid_expr_tree(stmt, upper_src, &(*for_line)->upper_expr));
OG_RETURN_IFERR(plc_verify_expr(compiler, (*for_line)->upper_expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &(*for_line)->upper_expr));
(*for_line)->reverse = reverse;
return OG_SUCCESS;
}
static status_t finish_implicit_cursor_for_start(core_yyscan_t yyscanner, const char *index_name,
source_location_t loc, pl_line_for_t **for_line)
{
int tok = YYLEX;
int endtoken = 0;
text_t *query = NULL;
plv_decl_t *imp_cur = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
pl_entity_t *entity = (pl_entity_t *)compiler->entity;
if (tok != K_SELECT) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "SELECT", pl_token_text(tok, &yylval));
return OG_ERROR;
}
query = read_sql_construct_from(yylloc.offset, ')', 0, 0, 0, 0, 0, yyscanner, &endtoken);
if (endtoken != ')' || YYLEX != K_LOOP) {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "LOOP", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
OG_RETURN_IFERR(init_for_line_common(compiler, index_name, for_line, OG_TRUE));
OG_RETURN_IFERR(init_for_cursor_record(compiler, *for_line));
(*for_line)->is_impcur = OG_TRUE;
OG_RETURN_IFERR(cm_galist_new((*for_line)->decls, sizeof(plv_decl_t), (void **)&imp_cur));
OG_RETURN_IFERR(pl_alloc_mem(compiler->entity, sizeof(plv_cursor_context_t), (void **)&imp_cur->cursor.ogx));
OG_RETURN_IFERR(plc_init_galist(compiler, &imp_cur->cursor.input));
imp_cur->vid.block = (*for_line)->id->vid.block;
imp_cur->vid.id = (uint16)((*for_line)->decls->count - 1);
imp_cur->type = PLV_IMPCUR;
(*for_line)->cursor_id = imp_cur->vid;
OG_RETURN_IFERR(compile_static_sql_context(stmt, query, KEY_WORD_SELECT, loc, imp_cur->cursor.input, NULL,
&(*for_line)->context));
OG_RETURN_IFERR(sql_append_references(&entity->ref_list, (*for_line)->context));
OG_RETURN_IFERR(copy_context_rscols_to_record(compiler, (*for_line)->context, (*for_line)->id->record, loc));
OG_RETURN_IFERR(plc_init_galist(compiler, &(*for_line)->into.output));
OG_RETURN_IFERR(udt_build_list_address_single(stmt, (*for_line)->into.output, (*for_line)->id, UDT_STACK_ADDR));
(*for_line)->into.prefetch_rows = INTO_COMMON_PREFETCH_COUNT;
(*for_line)->into.into_type = (uint8)INTO_AS_REC;
(*for_line)->into.is_bulk = OG_FALSE;
imp_cur->cursor.sql.value = CM_NULL_TEXT;
imp_cur->cursor.ogx->context = (*for_line)->context;
return OG_SUCCESS;
}
static status_t finish_explicit_cursor_for_start(core_yyscan_t yyscanner, const char *index_name,
expr_node_t *cursor_node, source_location_t loc, pl_line_for_t **for_line)
{
int tok;
int endtoken = 0;
plv_decl_t *decl = NULL;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
OG_RETURN_IFERR(find_cursor_decl_by_node(compiler, cursor_node, loc, &decl));
if (decl->cursor.ogx->is_sysref || decl->cursor.ogx->context == NULL) {
OG_SRC_THROW_ERROR(loc, ERR_INVALID_CURSOR);
return OG_ERROR;
}
OG_RETURN_IFERR(init_for_line_common(compiler, index_name, for_line, OG_TRUE));
OG_RETURN_IFERR(init_for_cursor_record(compiler, *for_line));
(*for_line)->is_impcur = OG_FALSE;
(*for_line)->cursor_id = decl->vid;
tok = YYLEX;
if (tok == '(') {
OG_RETURN_IFERR(plc_init_galist(compiler, &(*for_line)->exprs));
OG_RETURN_IFERR(compile_open_arg_list(yyscanner, compiler, (*for_line)->exprs, &endtoken));
if (endtoken != ')') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "')'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
if ((*for_line)->exprs->count == 0) {
(*for_line)->exprs = NULL;
} else {
OG_RETURN_IFERR(plc_verify_cursor_args(compiler, (*for_line)->exprs, decl->cursor.ogx->args, loc));
}
tok = YYLEX;
} else {
(*for_line)->exprs = NULL;
}
if (tok != K_LOOP) {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "LOOP", pl_token_text(tok, &yylval));
return OG_ERROR;
}
OG_RETURN_IFERR(copy_context_rscols_to_record(compiler, decl->cursor.ogx->context, (*for_line)->id->record, loc));
OG_RETURN_IFERR(plc_init_galist(compiler, &(*for_line)->into.output));
OG_RETURN_IFERR(udt_build_list_address_single(stmt, (*for_line)->into.output, (*for_line)->id, UDT_STACK_ADDR));
(*for_line)->into.prefetch_rows = INTO_COMMON_PREFETCH_COUNT;
(*for_line)->into.into_type = (uint8)INTO_AS_REC;
(*for_line)->into.is_bulk = OG_FALSE;
return OG_SUCCESS;
}
static status_t compile_for_start_stmt(core_yyscan_t yyscanner, const char *index_name, source_location_t loc,
pl_line_for_t **for_line)
{
int tok = YYLEX;
int lower_start;
text_t *lower_src = NULL;
bool32 reverse = OG_FALSE;
if (tok == K_REVERSE) {
reverse = OG_TRUE;
tok = YYLEX;
}
if (tok == '(') {
return finish_implicit_cursor_for_start(yyscanner, index_name, loc, for_line);
}
lower_start = yylloc.offset;
if (!reverse && tok == T_DATUM) {
var_address_pair_t *pair = sql_get_last_addr_pair(yylval.node);
if (pair != NULL && pair->type == UDT_STACK_ADDR && pair->stack->decl != NULL &&
pair->stack->decl->type == PLV_CUR) {
return finish_explicit_cursor_for_start(yyscanner, index_name, yylval.node, loc, for_line);
}
}
lower_src = read_sql_expression(DOT_DOT, yyscanner);
lower_src->str = og_yyget_extra(yyscanner)->core_yy_extra.scanbuf + lower_start;
lower_src->len = (uint32)(yylloc.offset - lower_start);
return finish_numeric_for_start(yyscanner, index_name, reverse, lower_src, loc, for_line);
}
static key_wid_t dml_key_from_token(int token)
{
switch (token) {
case K_INSERT:
return KEY_WORD_INSERT;
case K_UPDATE:
return KEY_WORD_UPDATE;
case K_DELETE:
return KEY_WORD_DELETE;
case K_MERGE:
return KEY_WORD_MERGE;
default:
return KEY_WORD_0_UNKNOWN;
}
}
static status_t compile_forall_stmt(core_yyscan_t yyscanner, const char *index_name, text_t *lower_src,
source_location_t loc)
{
pl_line_for_t *line = NULL;
pl_line_end_loop_t *end_line = NULL;
pl_line_ctrl_t *pop_line = NULL;
pl_line_sql_t *sql_line = NULL;
expr_tree_t *lower = NULL;
expr_tree_t *upper = NULL;
text_t *upper_src = NULL;
text_t *dml_src = NULL;
text_t idx_name;
int endtoken = 0;
key_wid_t key_wid;
source_location_t dml_loc;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
upper_src = read_sql_construct(K_INSERT, K_UPDATE, K_DELETE, K_MERGE, K_SAVE, 0, yyscanner, &endtoken);
if (endtoken == K_SAVE) {
endtoken = YYLEX;
if (endtoken != K_EXCEPTIONS) {
OG_SRC_THROW_ERROR(yylloc.loc, ERR_PL_EXPECTED_FAIL_FMT, "EXCEPTIONS", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
OG_SRC_THROW_ERROR(loc, ERR_PL_UNSUPPORT);
return OG_ERROR;
}
key_wid = dml_key_from_token(endtoken);
if (key_wid == KEY_WORD_0_UNKNOWN) {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "DML statement", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_for_t), LINE_FOR, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(plc_init_galist(compiler, &line->decls));
OG_RETURN_IFERR(cm_galist_new(line->decls, sizeof(plv_decl_t), (void **)&line->id));
line->id->vid.block = (int16)compiler->stack.depth;
line->id->vid.id = 0;
line->id->type = PLV_VAR;
line->id->variant.type.datatype = OG_TYPE_INTEGER;
cm_str2text((char *)index_name, &idx_name);
OG_RETURN_IFERR(pl_copy_text(compiler->entity, &idx_name, &line->id->name));
OG_RETURN_IFERR(get_valid_expr_tree(stmt, lower_src, &lower));
OG_RETURN_IFERR(plc_verify_expr(compiler, lower));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &lower));
OG_RETURN_IFERR(get_valid_expr_tree(stmt, upper_src, &upper));
OG_RETURN_IFERR(plc_verify_expr(compiler, upper));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &upper));
line->is_cur = OG_FALSE;
line->is_push = OG_FALSE;
line->reverse = OG_FALSE;
line->lower_expr = lower;
line->upper_expr = upper;
line->name = NULL;
/*
* The legacy compiler does not have a dedicated executable FORALL line. The bison path
* preserves the supported behavior by lowering a simple FORALL range to a numeric FOR loop
* that executes the parsed DML once per index value.
*/
OG_RETURN_IFERR(plc_push(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT));
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT));
dml_loc = yylloc.loc;
dml_src = read_sql_construct_from(yylloc.offset, ';', 0, 0, 0, 0, 0, yyscanner, &endtoken);
if (endtoken != ';') {
OG_SRC_THROW_ERROR(loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", pl_token_text(endtoken, &yylval));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_sql_t), LINE_SQL, (pl_line_ctrl_t **)&sql_line));
OG_RETURN_IFERR(plc_init_galist(compiler, &sql_line->input));
OG_RETURN_IFERR(compile_static_sql_line(stmt, dml_src, key_wid, dml_loc, sql_line));
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_end_loop_t), LINE_END_LOOP,
(pl_line_ctrl_t **)&end_line));
OG_RETURN_IFERR(plc_pop(compiler, loc, PBE_END_LOOP, &pop_line));
end_line->loop = pop_line;
line->next = (pl_line_ctrl_t *)end_line;
return OG_SUCCESS;
}
static status_t find_top_loop(pl_compiler_t *compiler, source_location_t loc, pl_line_ctrl_t **line)
{
int32 i;
for (i = (int32)compiler->control_stack.depth - 1; i >= 0; i--) {
pl_line_type_t type = compiler->control_stack.items[i].entry->type;
if (type == LINE_LOOP || type == LINE_WHILE || type == LINE_FOR) {
*line = compiler->control_stack.items[i].entry;
return OG_SUCCESS;
}
}
*line = NULL;
OG_SRC_THROW_ERROR(loc, ERR_PL_SYNTAX_ERROR_FMT, "not in loop statement");
return OG_ERROR;
}
static status_t make_type_word(pl_compiler_t *compiler, type_word_t **type, char *str,
galist_t *typemode, source_location_t loc, bool32 is_name_typemode, bool32 pl_type, bool32 pl_rowtype)
{
if (sql_alloc_mem(compiler->stmt->context, sizeof(type_word_t), (void **)type) != OG_SUCCESS) {
return OG_ERROR;
}
(*type)->str = str;
(*type)->typemode = typemode;
(*type)->is_name_typemode = is_name_typemode;
(*type)->pl_type = pl_type;
(*type)->pl_rowtype = pl_rowtype;
(*type)->loc = loc;
return OG_SUCCESS;
}
static char *pl_bison_identifier_at(core_yyscan_t yyscanner, int offset)
{
core_yy_extra_type *extra = &og_yyget_extra(yyscanner)->core_yy_extra;
sql_stmt_t *stmt = extra->stmt;
uint32 start = (uint32)offset;
uint32 end = start;
char *name = NULL;
if (start >= extra->scanbuflen) {
return NULL;
}
while (end < extra->scanbuflen && pl_bison_is_ident_char(extra->scanbuf[end])) {
end++;
}
if (end == start) {
return NULL;
}
if (sql_alloc_mem(stmt->context, end - start + 1, (void **)&name) != OG_SUCCESS) {
return NULL;
}
if (memcpy_s(name, end - start + 1, extra->scanbuf + start, end - start) != EOK) {
return NULL;
}
name[end - start] = '\0';
return name;
}
static text_t *read_sql_expression(int until, core_yyscan_t yyscanner)
{
return read_sql_construct(until, 0, 0, 0, 0, 0, yyscanner, NULL);
}
static text_t *read_sql_expression2(int until, int until2, core_yyscan_t yyscanner, int *endtoken)
{
return read_sql_construct(until, until2, 0, 0, 0, 0, yyscanner, endtoken);
}
static bool32 is_sql_construct_terminator(int token, int paren_depth, int until, int until2, int until3, int until4,
int until5, int until6)
{
if (token == YYEOF) {
return OG_TRUE;
}
if (paren_depth != 0) {
return OG_FALSE;
}
return (until != 0 && token == until) || (until2 != 0 && token == until2) ||
(until3 != 0 && token == until3) || (until4 != 0 && token == until4) ||
(until5 != 0 && token == until5) || (until6 != 0 && token == until6);
}
static void update_sql_construct_depth(int token, int *paren_depth)
{
if (token == '(') {
(*paren_depth)++;
return;
}
if (token == ')' && *paren_depth > 0) {
(*paren_depth)--;
}
}
static text_t *read_sql_construct_from(int start_offset,
int until,
int until2,
int until3,
int until4,
int until5,
int until6,
core_yyscan_t yyscanner,
int *endtoken)
{
text_t *expr_src = NULL;
int tok = YYLEX;
int paren_depth = 0;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
sql_stack_alloc(stmt, sizeof(text_t), (void**)&expr_src);
for (;;) {
if (is_sql_construct_terminator(tok, paren_depth, until, until2, until3, until4, until5, until6)) {
break;
}
update_sql_construct_depth(tok, &paren_depth);
tok = YYLEX;
}
if (endtoken) {
*endtoken = tok;
}
expr_src->str = og_yyget_extra(yyscanner)->core_yy_extra.scanbuf + start_offset;
expr_src->len = yylloc.offset - start_offset;
return expr_src;
}
static text_t *read_sql_construct(int until,
int until2,
int until3,
int until4,
int until5,
int until6,
core_yyscan_t yyscanner,
int *endtoken)
{
text_t *expr_src = NULL;
int tok = YYLEX;
int begin = yylloc.offset;
int paren_depth = 0;
sql_stmt_t *stmt = og_yyget_extra(yyscanner)->core_yy_extra.stmt;
sql_stack_alloc(stmt, sizeof(text_t), (void**)&expr_src);
for (;;) {
if (is_sql_construct_terminator(tok, paren_depth, until, until2, until3, until4, until5, until6)) {
break;
}
update_sql_construct_depth(tok, &paren_depth);
tok = YYLEX;
}
if (endtoken) {
*endtoken = tok;
}
expr_src->str = og_yyget_extra(yyscanner)->core_yy_extra.scanbuf + begin;
expr_src->len = yylloc.offset - begin;
return expr_src;
}