* This file is part of the oGRAC project.
* Copyright (c) 2024 Huawei Technologies Co.,Ltd.
*
* oGRAC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* lines_cl.c
*
*
* IDENTIFICATION
* src/ogsql/pl/parser/lines_cl.c
*
* -------------------------------------------------------------------------
*/
#include "lines_cl.h"
#include "ast_cl.h"
#include "dml_cl.h"
#include "pl_memory.h"
#include "base_compiler.h"
#include "decl_cl.h"
#include "pragma_cl.h"
#include "cond_parser.h"
#include "func_parser.h"
#include "pl_common.h"
#include "cursor_cl.h"
#include "call_cl.h"
#include "pl_executor.h"
#include "param_decl_cl.h"
#include "pl_udt.h"
typedef status_t (*plc_compile_lines_t)(pl_compiler_t *compiler, word_t *word);
struct st_plc_compile_lines_map {
key_wid_t type;
plc_compile_lines_t func;
};
static status_t plc_compile_declare_ln(pl_compiler_t *compiler, word_t *word)
{
galist_t *decls = NULL;
galist_t *type_decls = NULL;
OG_RETURN_IFERR(plc_init_galist(compiler, &decls));
OG_RETURN_IFERR(plc_init_galist(compiler, &type_decls));
compiler->type_decls = type_decls;
return plc_compile_block(compiler, decls, NULL, word);
}
static status_t plc_compile_begin_ln(pl_compiler_t *compiler, word_t *unused_word)
{
galist_t *decls = NULL;
pl_line_begin_t *line = NULL;
word_t word;
bool32 result = OG_FALSE;
text_t block_name;
pl_line_label_t *label = NULL;
bool32 body_empty = OG_TRUE;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
label = (pl_line_label_t *)compiler->last_line;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_begin_t), LINE_BEGIN, (pl_line_ctrl_t **)&line));
line->name = ((label != NULL) && (label->ctrl.type == LINE_LABEL)) ? &label->name : NULL;
block_name = (line->name != NULL) ? *line->name : CM_NULL_TEXT;
line->decls = decls;
OG_RETURN_IFERR(plc_push(compiler, (pl_line_ctrl_t *)line, &block_name));
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, &word));
plc_check_end_symbol(&word);
OG_RETURN_IFERR(plc_check_word_eof(word.type, word.loc));
OG_RETURN_IFERR(plc_try_compile_end_ln(compiler, &result, NULL, &word));
if (result) {
if (body_empty) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_EXPECTED_FAIL_FMT, "lines", "END");
return OG_ERROR;
}
break;
}
if (plc_compile_lines(compiler, &word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, &word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word.type == WORD_TYPE_EOF) {
break;
}
}
body_empty = OG_FALSE;
}
return status;
}
static status_t plc_compile_end_ln(pl_compiler_t *compiler, word_t *word)
{
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "OTHERS", "END");
return OG_ERROR;
}
static status_t plc_compile_exception_line(pl_compiler_t *cmpl, word_t *word, lex_t *lex, status_t *status, bool32 *result)
{
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_expected_fetch(lex, word));
if (word->type == WORD_TYPE_BRACKET) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_KEYWORD_ERROR);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_try_compile_end_when(cmpl, result, word));
OG_BREAK_IF_TRUE(*result);
if (plc_compile_lines(cmpl, word) != OG_SUCCESS) {
*status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(cmpl, word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word->type == WORD_TYPE_EOF) {
break;
}
}
}
return OG_SUCCESS;
}
static status_t plc_compile_exception(pl_compiler_t *compiler, word_t *word)
{
pl_line_begin_t *begin_line = NULL;
pl_line_except_t *except_line = NULL;
pl_line_ctrl_t *except_end = NULL;
plv_decl_t *decl = NULL;
var_udo_t *block_obj = NULL;
bool32 result = OG_FALSE;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
pl_entity_t *pl_entity = compiler->entity;
char block_buf[OG_MAX_NAME_LEN] = { 0 };
text_t block_name = {
.str = block_buf,
.len = 0
};
word_t save_word;
begin_line = (pl_line_begin_t *)plc_get_current_beginln(compiler);
if (begin_line == NULL) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_UNEXPECTED_FMT, "symbol exception");
return OG_ERROR;
}
if (begin_line->except != NULL) {
OG_SRC_THROW_ERROR(word->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;
OG_RETURN_IFERR(plc_push(compiler, (pl_line_ctrl_t *)except_line, &block_name));
OG_RETURN_IFERR(lex_expected_fetch(lex, word));
if (word->id != KEY_WORD_WHEN) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "WHEN", W2S(word));
return OG_ERROR;
}
do {
pl_line_when_t *line = NULL;
pl_line_ctrl_t *line_end = NULL;
compiler->line_loc = word->loc;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_when_t), LINE_WHEN, (pl_line_ctrl_t **)&line));
cm_galist_init(&line->excepts, pl_entity, pl_alloc_mem);
do {
pl_exception_t *except = NULL;
OG_RETURN_IFERR(lex_expected_fetch(lex, word));
OG_RETURN_IFERR(pl_alloc_mem(pl_entity, sizeof(pl_exception_t), (void **)&except));
OG_RETURN_IFERR(plc_verify_word_as_var(compiler, word));
plc_find_decl_ex(compiler, word, PLV_EXCPT, NULL, &decl);
OG_RETURN_IFERR(plc_compile_exception_set_except(decl, word, except));
if (plc_find_line_except(compiler, line, except, &word->text) == OG_SUCCESS) {
return OG_ERROR;
}
if (plc_check_except_exists(compiler, ((pl_line_except_t *)begin_line->except)->excpts, except,
&word->text) == OG_SUCCESS) {
return OG_ERROR;
}
if (except->is_userdef == OG_FALSE && except->error_code == OTHERS && line->excepts.count > 0) {
OG_SRC_THROW_ERROR(word->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(&line->excepts, (pointer_t)except));
OG_RETURN_IFERR(lex_expected_fetch(lex, word));
} while (word->id == KEY_WORD_OR);
if (word->id != KEY_WORD_THEN) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "THEN", W2S(word));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_compile_exception_line(compiler, word, lex, &status, &result));
OG_RETURN_IFERR(cm_galist_insert(((pl_line_except_t *)begin_line->except)->excpts, line));
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END_WHEN, (pl_line_ctrl_t **)&line_end));
} while (word->id == KEY_WORD_WHEN);
OG_RETURN_IFERR(
plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END_EXCEPTION, (pl_line_ctrl_t **)&except_end));
OG_RETURN_IFERR(plc_pop(compiler, compiler->line_loc, PBE_END_EXCEPTION, &except_end));
if (result) {
except_line->end = compiler->last_line;
}
block_obj = (compiler->stack.depth == 1) ? compiler->obj : NULL;
save_word = *word;
OG_RETURN_IFERR(plc_expected_end_ln(compiler, &result, block_obj, word));
if (!result) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_SYNTAX_ERROR_FMT, "expected END of BEGIN...END");
return OG_ERROR;
}
*word = save_word;
lex_back(lex, word);
return status;
}
static status_t plc_try_compile_elsif_cond(pl_compiler_t *compiler, pl_line_elsif_t *line, word_t *word)
{
if (sql_create_cond_until(compiler->stmt, &line->cond, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, &line->cond));
return OG_SUCCESS;
}
static status_t plc_try_compile_end_if(pl_compiler_t *compiler, bool32 *result, word_t *word)
{
pl_line_ctrl_t *line = NULL;
pl_line_ctrl_t *pop_line = NULL;
pl_line_if_t *if_line = NULL;
lex_t *lex = compiler->stmt->session->lex;
if (word->id != KEY_WORD_END) {
*result = OG_FALSE;
return OG_SUCCESS;
}
compiler->line_loc = word->loc;
OG_RETURN_IFERR(lex_try_fetch(lex, "IF", result));
if (!(*result)) {
return OG_SUCCESS;
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_END_IF, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(plc_pop(compiler, compiler->line_loc, PBE_END_IF, &pop_line));
OG_RETURN_IFERR(lex_expected_fetch_word(lex, ";"));
if ((pop_line->type == LINE_IF) || (pop_line->type == LINE_ELIF)) {
if_line = (pl_line_if_t *)pop_line;
if_line->f_line = line;
}
return OG_SUCCESS;
}
static status_t plc_try_compile_elsif(pl_compiler_t *compiler, bool32 *result, word_t *word)
{
pl_line_elsif_t *line = NULL;
pl_line_ctrl_t *brother_line = NULL;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
if (word->id != KEY_WORD_ELSIF) {
*result = OG_FALSE;
return OG_SUCCESS;
}
lex->flags = LEX_WITH_OWNER | LEX_WITH_ARG;
*result = OG_TRUE;
compiler->line_loc = word->loc;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_elsif_t), LINE_ELIF, (pl_line_ctrl_t **)&line));
PLC_RESET_WORD_LOC(lex, word);
OG_RETURN_IFERR(plc_try_compile_elsif_cond(compiler, line, word));
if (word->id != KEY_WORD_THEN) {
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_EXPECTED_FAIL_FMT, "THEN", W2S(word));
return OG_ERROR;
}
OG_RETURN_IFERR(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;
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT));
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, word));
plc_check_end_symbol(word);
OG_RETURN_IFERR(plc_check_word_eof(word->type, word->loc));
OG_RETURN_IFERR(plc_try_compile_end_if(compiler, result, word));
OG_BREAK_IF_TRUE(*result);
OG_RETURN_IFERR(plc_try_compile_elsif(compiler, result, word));
OG_BREAK_IF_TRUE(*result);
if (plc_compile_lines(compiler, word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word->type == WORD_TYPE_EOF) {
break;
}
}
}
if (*result) {
line->next = compiler->last_line;
}
return status;
}
static status_t plc_compile_if(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_if_t *line = NULL;
bool32 result = OG_FALSE;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_if_t), LINE_IF, (pl_line_ctrl_t **)&line));
word.text.loc = lex->loc;
if (sql_create_cond_until(compiler->stmt, &line->cond, &word) != OG_SUCCESS) {
pl_check_and_set_loc(line->ctrl.loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, &line->cond));
if (word.id != KEY_WORD_THEN) {
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_EXPECTED_FAIL_FMT, "THEN", W2S(&word));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT));
line->t_line = NULL;
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, &word));
plc_check_end_symbol(&word);
OG_RETURN_IFERR(plc_check_word_eof(word.type, word.loc));
OG_RETURN_IFERR(plc_try_compile_end_if(compiler, &result, &word));
OG_BREAK_IF_TRUE(result);
OG_RETURN_IFERR(plc_try_compile_elsif(compiler, &result, &word));
OG_BREAK_IF_TRUE(result);
if (plc_compile_lines(compiler, &word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, &word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
OG_BREAK_IF_TRUE(word.type == WORD_TYPE_EOF);
}
}
if (result) {
line->next = compiler->last_line;
}
return status;
}
static status_t plc_compile_elsif(pl_compiler_t *compiler, word_t *word)
{
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_EXPECTED_FAIL_FMT, "OTHERS", "ELSIF");
return OG_ERROR;
}
static status_t plc_compile_else(pl_compiler_t *compiler, word_t *unused_word)
{
pl_line_ctrl_t *brother_line = NULL;
pl_line_else_t *else_line = NULL;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_else_t), LINE_ELSE, (pl_line_ctrl_t **)&else_line));
OG_RETURN_IFERR(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;
return plc_push_ctl(compiler, (pl_line_ctrl_t *)else_line, &CM_NULL_TEXT);
}
static status_t plc_try_compile_end_case(pl_compiler_t *compiler, bool32 *result, word_t *word, pl_line_case_t *case_ln)
{
pl_line_when_case_t *end_case = NULL;
pl_line_ctrl_t *brother_line = NULL;
lex_t *lex = compiler->stmt->session->lex;
if (word->id != KEY_WORD_END) {
*result = OG_FALSE;
return OG_SUCCESS;
}
compiler->line_loc = word->loc;
OG_RETURN_IFERR(lex_try_fetch(lex, "CASE", result));
if (!(*result)) {
return OG_SUCCESS;
}
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, word->loc, PBE_END_CASE, (pl_line_ctrl_t **)&brother_line));
OG_RETURN_IFERR(lex_expected_fetch_word(lex, ";"));
if (brother_line->type == LINE_WHEN_CASE) {
pl_line_when_case_t *when_case = (pl_line_when_case_t *)brother_line;
when_case->f_line = &end_case->ctrl;
}
end_case->selector = case_ln->selector;
return OG_SUCCESS;
}
static status_t plc_try_compile_when_case(pl_compiler_t *compiler, bool32 *result, word_t *word,
pl_line_case_t *case_ln)
{
pl_line_when_case_t *line = NULL;
pl_line_ctrl_t *brother_line = NULL;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_stack_safe(compiler));
if (word->id != KEY_WORD_WHEN) {
*result = OG_FALSE;
return OG_SUCCESS;
}
*result = OG_TRUE;
lex->flags = LEX_WITH_OWNER | LEX_WITH_ARG;
compiler->line_loc = word->loc;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_when_case_t), LINE_WHEN_CASE, (pl_line_ctrl_t **)&line));
LEX_SAVE(lex);
OG_RETURN_IFERR(lex_expected_fetch(lex, word));
int32 except_id = pl_get_exception_id(word);
if (except_id != INVALID_EXCEPTION) {
OG_SRC_THROW_ERROR_EX(lex->loc, ERR_PLSQL_ILLEGAL_LINE_FMT, "indentifier %s is reserved in the exception body",
W2S(word));
return OG_ERROR;
}
LEX_RESTORE(lex);
PLC_RESET_WORD_LOC(lex, word);
if (case_ln->selector == NULL) {
if (sql_create_cond_until(compiler->stmt, (cond_tree_t **)&line->cond, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)&line->cond));
} else {
bool32 word_flag = OG_FALSE;
OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &word_flag));
if (word_flag) {
OG_SRC_THROW_ERROR(word->loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
if (sql_create_expr_until(compiler->stmt, (expr_tree_t **)&line->cond, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_expr(compiler, (expr_tree_t *)line->cond));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, (expr_tree_t **)&line->cond));
}
if (word->id != KEY_WORD_THEN) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "THEN", W2S(word));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_pop(compiler, word->loc, PBE_WHEN_CASE, (pl_line_ctrl_t **)&brother_line));
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT));
line->if_line = (brother_line->type == LINE_CASE) ? NULL : (pl_line_if_t *)brother_line;
line->t_line = NULL;
line->selector = case_ln->selector;
if (brother_line->type == LINE_WHEN_CASE) {
pl_line_when_case_t *when_case = (pl_line_when_case_t *)brother_line;
when_case->f_line = (pl_line_ctrl_t *)line;
}
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, word));
plc_check_end_symbol(word);
OG_RETURN_IFERR(plc_check_word_eof(word->type, word->loc));
OG_RETURN_IFERR(plc_try_compile_end_case(compiler, result, word, case_ln));
OG_BREAK_IF_TRUE(*result);
OG_RETURN_IFERR(plc_try_compile_when_case(compiler, result, word, case_ln));
OG_BREAK_IF_TRUE(*result);
if (plc_compile_lines(compiler, word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word->type == WORD_TYPE_EOF) {
break;
}
}
}
if (*result) {
line->next = compiler->last_line;
}
return status;
}
static status_t plc_compile_case(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_case_t *line = NULL;
bool32 result = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_case_t), LINE_CASE, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &CM_NULL_TEXT));
OG_RETURN_IFERR(lex_try_fetch(lex, "WHEN", &result));
if (result) {
line->selector = NULL;
word.id = KEY_WORD_WHEN;
word.loc = compiler->line_loc;
OG_RETURN_IFERR(plc_try_compile_when_case(compiler, &result, &word, line));
} else {
OG_RETURN_IFERR(sql_create_expr_until(compiler->stmt, &line->selector, &word));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->selector));
OG_RETURN_IFERR(plc_try_compile_when_case(compiler, &result, &word, line));
if (!result) {
OG_SRC_THROW_ERROR(word.loc, ERR_PL_SYNTAX_ERROR_FMT, "case statement must has when statement.");
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t plc_compile_goto(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_goto_t *line = NULL;
lex_t *lex = compiler->stmt->session->lex;
pl_entity_t *pl_entity = compiler->entity;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_goto_t), LINE_GOTO, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_expected_fetch_variant(lex, &word));
OG_RETURN_IFERR(pl_copy_object_name_ci(pl_entity, word.type, (text_t *)&word.text, &line->label));
return lex_expected_fetch_word(lex, ";");
}
static status_t plc_try_compile_end_loop(pl_compiler_t *compiler, bool32 *result, word_t *word)
{
pl_line_end_loop_t *line = NULL;
pl_line_ctrl_t *pop_line = NULL;
lex_t *lex = compiler->stmt->session->lex;
if (word->id != KEY_WORD_END) {
*result = OG_FALSE;
return OG_SUCCESS;
}
OG_RETURN_IFERR(lex_try_fetch(lex, "LOOP", result));
if (!(*result)) {
return OG_SUCCESS;
}
compiler->line_loc = word->loc;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_end_loop_t), LINE_END_LOOP, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(plc_pop(compiler, compiler->line_loc, PBE_END_LOOP, &pop_line));
OG_RETURN_IFERR(lex_fetch(lex, word));
if (IS_VARIANT(word)) {
OG_RETURN_IFERR(lex_fetch(lex, word));
}
if (word->type != WORD_TYPE_PL_TERM) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", W2S(word));
return OG_ERROR;
}
line->loop = pop_line;
return OG_SUCCESS;
}
static status_t plc_compile_loop(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_loop_t *line = NULL;
pl_line_label_t *label = NULL;
text_t loop_name;
bool32 result = OG_FALSE;
bool32 body_empty = OG_TRUE;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
label = (pl_line_label_t *)compiler->last_line;
loop_name = ((label != NULL) && (label->ctrl.type == LINE_LABEL)) ? label->name : CM_NULL_TEXT;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_loop_t), LINE_LOOP, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &loop_name));
line->stack_line = CURR_BLOCK_BEGIN(compiler);
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, &word));
plc_check_end_symbol(&word);
OG_RETURN_IFERR(plc_check_word_eof(word.type, word.loc));
OG_RETURN_IFERR(plc_try_compile_end_loop(compiler, &result, &word));
if (result) {
if (body_empty) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_EXPECTED_FAIL_FMT, "lines", "END LOOP");
return OG_ERROR;
}
break;
}
if (plc_compile_lines(compiler, &word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, &word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word.type == WORD_TYPE_EOF) {
break;
}
}
body_empty = OG_FALSE;
}
if (result) {
line->next = compiler->last_line;
}
return status;
}
static status_t plc_expected_fetch_range(pl_compiler_t *compiler, pl_line_for_t *line)
{
word_t word;
bool32 err_flag = OG_FALSE;
bool32 result = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
PLC_RESET_WORD_LOC(lex, &word);
OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &result));
if (result) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
if (sql_create_expr_until(compiler->stmt, &line->lower_expr, &word) != OG_SUCCESS) {
OG_SRC_THROW_ERROR(word.loc, ERR_PL_EXPECTED_FAIL_FMT, "lower_bound expr", W2S(&word));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_expr(compiler, line->lower_expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->lower_expr));
if (word.type != WORD_TYPE_PL_RANGE) {
OG_SRC_THROW_ERROR(word.loc, ERR_PL_EXPECTED_FAIL_FMT, "..", W2S(&word));
return OG_ERROR;
}
OG_RETURN_IFERR(lex_try_fetch_char(lex, '.', &err_flag));
if (err_flag) {
OG_SRC_THROW_ERROR(word.loc, ERR_PL_EXPECTED_FAIL_FMT, "upper_bound", "'.'");
return OG_ERROR;
}
PLC_RESET_WORD_LOC(lex, &word);
OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &result));
if (result) {
OG_SRC_THROW_ERROR(word.loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
if (sql_create_expr_until(compiler->stmt, &line->upper_expr, &word) != OG_SUCCESS) {
cm_reset_error();
OG_SRC_THROW_ERROR(word.loc, ERR_PL_EXPECTED_FAIL_FMT, "upper_bound expr", W2S(&word));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_expr(compiler, line->upper_expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->upper_expr));
if (word.id != KEY_WORD_LOOP) {
OG_SRC_THROW_ERROR(word.loc, ERR_PL_EXPECTED_FAIL_FMT, "LOOP", W2S(&word));
return OG_ERROR;
}
lex_back(lex, &word);
return OG_SUCCESS;
}
static status_t plc_compile_for(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_for_t *line = NULL;
pl_line_label_t *label = NULL;
text_t loop_name;
bool32 result = OG_FALSE;
plv_decl_t *id = NULL;
bool32 body_empty = OG_TRUE;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_stack_safe(compiler));
label = (pl_line_label_t *)compiler->last_line;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_for_t), LINE_FOR, (pl_line_ctrl_t **)&line));
line->name = ((label != NULL) && (label->ctrl.type == LINE_LABEL)) ? &label->name : NULL;
loop_name = (line->name != NULL) ? *line->name : CM_NULL_TEXT;
OG_RETURN_IFERR(plc_init_galist(compiler, &line->decls));
OG_RETURN_IFERR(cm_galist_new(line->decls, sizeof(plv_decl_t), (void **)&line->id));
id = line->id;
id->vid.block = (int16)compiler->stack.depth;
id->vid.id = 0;
OG_RETURN_IFERR(plc_push(compiler, (pl_line_ctrl_t *)line, &loop_name));
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &loop_name));
OG_RETURN_IFERR(lex_expected_fetch(lex, &word));
OG_RETURN_IFERR(plc_diagnose_for_is_cursor(compiler, &line->is_cur));
if (line->is_cur) {
OG_RETURN_IFERR(plc_compile_for_cursor(compiler, line, &word));
} else {
* FOR LOOP Statement, The statement has this structure:
* [label] FOR index IN [REVERSE] lower_bound..upper_bound LOOP
* statements
* END LOOP[label];
* NOTE:
* 1.If lower_bound is greater than upper_bound, then the statements never run.
* 2.If the lower_bound or upper_bound is number type, the the i should use the round of the number.
*/
if (!IS_VARIANT(&word)) {
OG_SRC_THROW_ERROR(word.loc, ERR_PL_EXPECTED_FAIL_FMT, "variant", W2S(&word));
return OG_ERROR;
}
id->type = PLV_VAR;
OG_RETURN_IFERR(pl_copy_object_name_ci(compiler->entity, word.type, (text_t *)&word.text, &id->name));
id->variant.type.datatype = OG_TYPE_INTEGER;
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "in"));
OG_RETURN_IFERR(lex_try_fetch(lex, "reverse", &line->reverse));
OG_RETURN_IFERR(plc_expected_fetch_range(compiler, line));
}
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "LOOP"));
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, &word));
plc_check_end_symbol(&word);
OG_RETURN_IFERR(plc_check_word_eof(word.type, word.loc));
OG_RETURN_IFERR(plc_try_compile_end_loop(compiler, &result, &word));
if (result) {
if (body_empty) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_EXPECTED_FAIL_FMT, "lines", "END LOOP");
return OG_ERROR;
}
break;
}
if (plc_compile_lines(compiler, &word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, &word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word.type == WORD_TYPE_EOF) {
break;
}
}
body_empty = OG_FALSE;
}
if (result) {
line->next = compiler->last_line;
}
return status;
}
static status_t plc_compile_forall(pl_compiler_t *compiler, word_t *unused_word)
{
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_UNSUPPORT);
return OG_ERROR;
}
static status_t plc_compile_while(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_while_t *line = NULL;
pl_line_label_t *label = NULL;
text_t loop_name;
bool32 result = OG_FALSE;
bool32 body_empty = OG_TRUE;
status_t status = OG_SUCCESS;
lex_t *lex = compiler->stmt->session->lex;
label = (pl_line_label_t *)compiler->last_line;
loop_name = ((label != NULL) && (label->ctrl.type == LINE_LABEL)) ? label->name : CM_NULL_TEXT;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_while_t), LINE_WHILE, (pl_line_ctrl_t **)&line));
PLC_RESET_WORD_LOC(lex, &word);
if (sql_create_cond_until(compiler->stmt, &line->cond, &word) != OG_SUCCESS) {
pl_check_and_set_loc(word.loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, &line->cond));
OG_RETURN_IFERR(plc_push_ctl(compiler, (pl_line_ctrl_t *)line, &loop_name));
line->stack_line = CURR_BLOCK_BEGIN(compiler);
while (OG_TRUE) {
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, &word));
plc_check_end_symbol(&word);
OG_RETURN_IFERR(plc_check_word_eof(word.type, word.loc));
OG_RETURN_IFERR(plc_try_compile_end_loop(compiler, &result, &word));
if (result) {
if (body_empty) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_EXPECTED_FAIL_FMT, "lines", "END LOOP");
return OG_ERROR;
}
break;
}
if (plc_compile_lines(compiler, &word) != OG_SUCCESS) {
status = OG_ERROR;
OG_RETURN_IFERR(plc_skip_error_line(compiler, &word));
if (g_tls_error.is_full) {
return OG_ERROR;
}
if (word.type == WORD_TYPE_EOF) {
break;
}
}
body_empty = OG_FALSE;
}
if (result) {
line->next = compiler->last_line;
}
return status;
}
static status_t plc_compile_when(pl_compiler_t *compiler, word_t *word)
{
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_UNSUPPORT);
return OG_ERROR;
}
static status_t plc_compile_null(pl_compiler_t *compiler, word_t *unused_word)
{
pl_line_ctrl_t *line = NULL;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_NULL, (pl_line_ctrl_t **)&line));
return lex_expected_fetch_word(lex, ";");
}
static status_t plc_compile_open_decl(pl_compiler_t *compiler, lex_t *lex, word_t *word, plv_decl_t **decl)
{
OG_RETURN_IFERR(lex_expected_fetch(lex, word));
if (word->type != WORD_TYPE_PARAM) {
OG_RETURN_IFERR(plc_find_decl(compiler, word, PLV_CUR, NULL, decl));
} else {
OG_SRC_THROW_ERROR(word->loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"the declaration of the cursor of this expression is incomplete or malformed");
return OG_ERROR;
}
if ((*decl)->cursor.ogx->is_err) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_INCOMPLETE_DECL_FMT, T2S(&(*decl)->name));
return OG_ERROR;
}
if ((*decl)->drct == PLV_DIR_IN) {
OG_SRC_THROW_ERROR(word->loc, ERR_PLE_CURSOR_IN_OPEN, T2S(&(*decl)->name));
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_compile_open(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_open_t *line = NULL;
plv_decl_t *decl = NULL;
bool32 result = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_stack_safe(compiler));
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_open_t), LINE_OPEN, (pl_line_ctrl_t **)&line));
uint32 save_flags = lex->flags;
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(plc_compile_open_decl(compiler, lex, &word, &decl));
line->vid = decl->vid;
OG_RETURN_IFERR(lex_try_fetch_bracket(lex, &word, &result));
if (result) {
lex_trim(&word.text);
if (word.text.len == 0) {
result = OG_FALSE;
}
}
lex->flags = save_flags;
if (result) {
OG_RETURN_IFERR(plc_init_galist(compiler, &line->exprs));
OG_RETURN_IFERR(plc_build_open_cursor_args(compiler, &word, line->exprs));
OG_RETURN_IFERR(plc_verify_cursor_args(compiler, line->exprs, decl->cursor.ogx->args, line->ctrl.loc));
} else {
line->exprs = NULL;
}
if (!decl->cursor.ogx->is_sysref) {
if (decl->cursor.ogx->context == NULL) {
OG_SRC_THROW_ERROR(decl->loc, ERR_PL_INCOMPLETE_DECL_FMT, T2S(&decl->name));
return OG_ERROR;
}
OG_RETURN_IFERR(lex_fetch(lex, &word));
if (IS_SPEC_CHAR(&word, ';')) {
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", W2S(&word));
return OG_ERROR;
} else {
if (decl->cursor.ogx->context != NULL) {
OG_SRC_THROW_ERROR(decl->loc, ERR_PL_INCOMPLETE_DECL_FMT, T2S(&decl->name));
return OG_ERROR;
}
}
return plc_compile_refcur(compiler, &word, decl, line);
}
static status_t plc_compile_bulk_limit_clause(pl_compiler_t *compiler, pl_into_t *into, word_t *word)
{
if (word->id != KEY_WORD_LIMIT) {
return OG_SUCCESS;
}
variant_t var;
if (sql_create_expr_until(compiler->stmt, &into->limit, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_limit_expr(compiler, into->limit));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &into->limit));
if (TREE_EXPR_TYPE(into->limit) != EXPR_NODE_CONST) {
return OG_SUCCESS;
}
OG_RETURN_IFERR(sql_exec_expr(compiler->stmt, into->limit, &var));
if (OG_IS_NUMERIC_TYPE(var.type)) {
OG_RETURN_IFERR(sql_convert_variant(compiler->stmt, &var, OG_TYPE_INTEGER));
}
if (var.is_null || var.type != OG_TYPE_INTEGER || var.v_int <= 0) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_SYNTAX_ERROR_FMT, "numberic or value error");
return OG_ERROR;
}
into->limit = NULL;
into->prefetch_rows = var.v_int;
return OG_SUCCESS;
}
static status_t plc_compile_into_record_clause(pl_into_t *into, word_t *word)
{
expr_node_t *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(word->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 plc_check_param_into(pl_compiler_t *compiler, source_location_t loc)
{
sql_stmt_t *stmt = (sql_stmt_t *)compiler->stmt;
if (stmt == NULL || stmt->context->type != OGSQL_TYPE_ANONYMOUS_BLOCK) {
OG_SRC_THROW_ERROR(loc, ERR_PL_PARAM_USE);
return OG_ERROR;
}
pl_executor_t *exec = (pl_executor_t *)stmt->pl_exec;
if (exec == NULL || exec->dynamic_parent == NULL) {
OG_SRC_THROW_ERROR(loc, ERR_PLSQL_ILLEGAL_LINE_FMT, "into clause cannot use param decl until in dynamic sql");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_into_clause_check_decl(plv_decl_t *decl, word_t *word)
{
if (decl == NULL) {
OG_SRC_THROW_ERROR_EX(word->loc, ERR_PL_SYNTAX_ERROR_FMT, "identifier \'%s\' must be declared", W2S(word));
return OG_ERROR;
}
if (decl->type == PLV_CUR) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPR_AS_INTO_FMT, T2S(&decl->name));
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t plc_compile_into_clause(pl_compiler_t *compiler, pl_into_t *into, word_t *word)
{
plv_decl_t *decl = NULL;
plc_var_type_t var_type;
expr_node_t *node = NULL;
into->is_bulk = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_init_galist(compiler, &into->output));
while (OG_TRUE) {
OG_RETURN_IFERR(lex_fetch(lex, word));
OG_RETURN_IFERR(cm_galist_new(into->output, sizeof(expr_node_t), (void **)&node));
if (word->type == WORD_TYPE_PARAM) {
OG_RETURN_IFERR(plc_check_param_into(compiler, word->loc));
OG_RETURN_IFERR(plc_find_param_as_expr_left(compiler, word, &decl));
OG_RETURN_IFERR(plc_build_var_address(compiler->stmt, decl, node, UDT_STACK_ADDR));
} else {
OG_RETURN_IFERR(plc_find_decl(compiler, word, PLV_VARIANT_AND_CUR, &var_type, &decl));
OG_RETURN_IFERR(plc_into_clause_check_decl(decl, word));
if (PLC_IS_MULTIEX_VARIANT(var_type)) {
if (plc_try_obj_access_bracket(compiler->stmt, word, node) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_address_expr(compiler, node));
} else {
OG_RETURN_IFERR(plc_build_var_address(compiler->stmt, decl, node, UDT_STACK_ADDR));
SET_FUNC_RETURN_TYPE(decl, node);
}
}
OG_RETURN_IFERR(plc_check_var_as_left(compiler, node, word->loc, NULL));
OG_RETURN_IFERR(lex_fetch(lex, word));
if (!IS_SPEC_CHAR(word, ',')) {
break;
}
}
into->into_type = INTO_AS_VALUE;
if (into->output->count == 1) {
OG_RETURN_IFERR(plc_compile_into_record_clause(into, word));
}
return OG_SUCCESS;
}
status_t plc_compile_bulk_into_clause(pl_compiler_t *compiler, pl_into_t *into, word_t *word)
{
plc_var_type_t var_type;
plv_decl_t *decl = NULL;
lex_t *lex = compiler->stmt->session->lex;
expr_node_t *node = NULL;
uint8 attr_type = 0;
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "COLLECT"));
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "INTO"));
OG_RETURN_IFERR(plc_init_galist(compiler, &into->output));
into->is_bulk = OG_TRUE;
into->prefetch_rows = OG_INVALID_ID32;
for (;;) {
OG_RETURN_IFERR(lex_fetch(lex, word));
OG_RETURN_IFERR(plc_find_decl(compiler, word, PLV_COLLECTION, &var_type, &decl));
if (decl->collection->attr_type == UDT_COLLECTION || PLC_IS_MULTIEX_VARIANT(var_type)) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_SYNTAX_ERROR_FMT,
"cannot mix between single row and multi-row (BULK) in INTO list");
return OG_ERROR;
}
OG_RETURN_IFERR(cm_galist_new(into->output, sizeof(expr_node_t), (void **)&node));
OG_RETURN_IFERR(plc_build_var_address(compiler->stmt, decl, node, UDT_STACK_ADDR));
SET_FUNC_RETURN_TYPE(decl, node);
if (into->output->count == 1) {
attr_type = decl->collection->attr_type;
}
OG_RETURN_IFERR(lex_fetch(lex, word));
if (!IS_SPEC_CHAR(word, ',')) {
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(word->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 plc_compile_fetch(pl_compiler_t *compiler, word_t *unused_word)
{
uint32 matched_id;
word_t word;
pl_line_fetch_t *line = NULL;
plv_decl_t *decl = NULL;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_fetch_t), LINE_FETCH, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_expected_fetch_variant(lex, &word));
if (word.type != WORD_TYPE_PARAM) {
OG_RETURN_IFERR(plc_find_decl(compiler, &word, PLV_CUR, NULL, &decl));
} else {
OG_SRC_THROW_ERROR(word.loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"the declaration of the cursor of this expression is incomplete or malformed");
return OG_ERROR;
}
line->vid = decl->vid;
OG_RETURN_IFERR(lex_expected_fetch_1of2(lex, "INTO", "BULK", &matched_id));
if (matched_id == 0) {
OG_RETURN_IFERR(plc_compile_into_clause(compiler, &line->into, &word));
line->into.prefetch_rows = INTO_COMMON_PREFETCH_COUNT;
} else {
OG_RETURN_IFERR(plc_compile_bulk_into_clause(compiler, &line->into, &word));
OG_RETURN_IFERR(plc_compile_bulk_limit_clause(compiler, &line->into, &word));
}
if (decl->cursor.ogx->is_sysref == OG_FALSE) {
if (decl->cursor.ogx->context == NULL) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_UNDEFINED_SYMBOL_FMT, T2S(&decl->name));
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_into_clause(decl->cursor.ogx->context, &line->into, line->ctrl.loc));
} else {
if (decl->cursor.ogx->context != NULL) {
OG_SRC_THROW_ERROR(decl->loc, ERR_PL_INCOMPLETE_DECL_FMT, T2S(&decl->name));
return OG_ERROR;
}
}
if (word.type == WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_UNEXPECTED_FMT, "EOF");
return OG_ERROR;
}
if (word.type != WORD_TYPE_PL_TERM) {
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", W2S(&word));
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_compile_close(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_close_t *line = NULL;
plv_decl_t *decl = NULL;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_close_t), LINE_CLOSE, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_expected_fetch_variant(lex, &word));
if (word.type != WORD_TYPE_PARAM) {
OG_RETURN_IFERR(plc_find_decl(compiler, &word, PLV_CUR, NULL, &decl));
} else {
OG_SRC_THROW_ERROR(word.loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"the declaration of the cursor of this expression is incomplete or malformed");
return OG_ERROR;
}
line->vid = decl->vid;
return lex_expected_fetch_word(lex, ";");
}
static status_t plc_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, "no in loop statement.");
return OG_ERROR;
}
static status_t plc_verify_loop(pl_compiler_t *compiler, source_location_t loc, text_t *name, pl_line_ctrl_t **line)
{
text_t *cmp = NULL;
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)) {
cmp = &compiler->control_stack.items[i].name;
if (cm_text_equal_ins(cmp, 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.", T2S(name));
return OG_ERROR;
}
static status_t plc_compile_exit(pl_compiler_t *compiler, word_t *unused_word)
{
pl_line_exit_t *line = NULL;
word_t word;
bool32 result = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_exit_t), LINE_EXIT, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_try_fetch(lex, ";", &result));
if (result) {
line->cond = NULL;
return plc_top_loop(compiler, compiler->line_loc, &line->next);
}
OG_RETURN_IFERR(lex_try_fetch(lex, "when", &result));
if (result) {
PLC_RESET_WORD_LOC(lex, &word);
if (sql_create_cond_until(compiler->stmt, (cond_tree_t **)&line->cond, &word) != OG_SUCCESS) {
pl_check_and_set_loc(line->ctrl.loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)&line->cond));
return plc_top_loop(compiler, compiler->line_loc, &line->next);
}
OG_RETURN_IFERR(lex_expected_fetch(lex, &word));
OG_RETURN_IFERR(plc_verify_loop(compiler, compiler->line_loc, (text_t *)&word.text, &line->next));
OG_RETURN_IFERR(lex_try_fetch(lex, ";", &result));
if (result) {
line->cond = NULL;
return OG_SUCCESS;
}
OG_RETURN_IFERR(lex_try_fetch(lex, "when", &result));
if (result) {
PLC_RESET_WORD_LOC(lex, &word);
if (sql_create_cond_until(compiler->stmt, (cond_tree_t **)&line->cond, &word) != OG_SUCCESS) {
pl_check_and_set_loc(line->ctrl.loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)&line->cond));
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_SYNTAX_ERROR_FMT, "mismatch EXIT [LABEL] [WHEN COND].");
return OG_ERROR;
}
static status_t plc_compile_continue(pl_compiler_t *compiler, word_t *unused_word)
{
pl_line_continue_t *line = NULL;
word_t word;
bool32 result = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_continue_t), LINE_CONTINUE, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_try_fetch(lex, ";", &result));
if (result) {
line->cond = NULL;
return plc_top_loop(compiler, line->ctrl.loc, &line->next);
}
OG_RETURN_IFERR(lex_try_fetch(lex, "when", &result));
if (result) {
PLC_RESET_WORD_LOC(lex, &word);
if (sql_create_cond_until(compiler->stmt, (cond_tree_t **)&line->cond, &word) != OG_SUCCESS) {
pl_check_and_set_loc(line->ctrl.loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)&line->cond));
return plc_top_loop(compiler, line->ctrl.loc, &line->next);
}
OG_RETURN_IFERR(lex_expected_fetch(lex, &word));
OG_RETURN_IFERR(plc_verify_loop(compiler, line->ctrl.loc, (text_t *)&word.text, &line->next));
OG_RETURN_IFERR(lex_try_fetch(lex, ";", &result));
if (result) {
line->cond = NULL;
return OG_SUCCESS;
}
OG_RETURN_IFERR(lex_try_fetch(lex, "when", &result));
if (result) {
PLC_RESET_WORD_LOC(lex, &word);
if (sql_create_cond_until(compiler->stmt, (cond_tree_t **)&line->cond, &word) != OG_SUCCESS) {
pl_check_and_set_loc(line->ctrl.loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_cond(compiler, (cond_tree_t *)line->cond));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, (cond_tree_t **)&line->cond));
return OG_SUCCESS;
}
OG_SRC_THROW_ERROR(line->ctrl.loc, ERR_PL_SYNTAX_ERROR_FMT, "mismatched CONTINUE [LABEL] [WHEN COND].");
return OG_ERROR;
}
static status_t plc_compile_commit(pl_compiler_t *compiler, word_t *unused_word)
{
pl_line_ctrl_t *line = NULL;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_ctrl_t), LINE_COMMIT, (pl_line_ctrl_t **)&line));
return lex_expected_fetch_word(lex, ";");
}
static status_t plc_compile_rollback(pl_compiler_t *compiler, word_t *word)
{
pl_entity_t *entity = compiler->entity;
lex_t *lex = compiler->stmt->session->lex;
pl_line_rollback_t *line = NULL;
bool32 result = OG_FALSE;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_rollback_t), LINE_ROLLBACK, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_try_fetch(lex, "TO", &result));
if (result) {
OG_RETURN_IFERR(lex_try_fetch(lex, "SAVEPOINT", &result));
OG_RETURN_IFERR(lex_expected_fetch_variant(lex, word));
OG_RETURN_IFERR(pl_copy_name(entity, (text_t *)&word->text, &line->savepoint));
}
return lex_expected_fetch_word(lex, ";");
}
static status_t plc_compile_savepoint(pl_compiler_t *compiler, word_t *word)
{
pl_line_savepoint_t *line = NULL;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_savepoint_t), LINE_SAVEPOINT, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(lex_expected_fetch_variant(lex, word));
OG_RETURN_IFERR(pl_copy_name(compiler->entity, (text_t *)&word->text, &line->savepoint));
return lex_expected_fetch_word(lex, ";");
}
static status_t plc_compile_with(pl_compiler_t *compiler, word_t *word)
{
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_UNSUPPORT);
return OG_ERROR;
}
static status_t plc_compile_raise_decl(pl_compiler_t *compiler, plv_decl_t *decl, word_t *word,
pl_line_raise_t *raise_line)
{
int32 except_id;
pl_entity_t *entity = compiler->entity;
if (decl != NULL) {
OG_RETURN_IFERR(pl_copy_name(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;
} else {
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(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 plc_compile_raise(pl_compiler_t *compiler, word_t *unused_word)
{
word_t word;
pl_line_raise_t *raise_line = NULL;
pl_line_ctrl_t *ctrl_line = NULL;
bool32 result = OG_FALSE;
plv_decl_t *decl = NULL;
lex_t *lex = compiler->stmt->session->lex;
int i;
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_raise_t), LINE_RAISE, (pl_line_ctrl_t **)&raise_line));
OG_RETURN_IFERR(lex_expected_fetch(lex, &word));
if (IS_VARIANT(&word)) {
OG_RETURN_IFERR(plc_verify_word_as_var(compiler, &word));
plc_find_decl_ex(compiler, &word, PLV_EXCPT, NULL, &decl);
OG_RETURN_IFERR(plc_compile_raise_decl(compiler, decl, &word, raise_line));
OG_RETURN_IFERR(lex_try_fetch(lex, ";", &result));
if (!result) {
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", "OTHERS");
return OG_ERROR;
}
} else if (word.type == WORD_TYPE_PL_TERM) {
for (i = (int)compiler->stack.depth; i > 0; i--) {
ctrl_line = compiler->stack.items[i - 1].entry;
if (ctrl_line->type == LINE_EXCEPTION) {
break;
}
}
if (i == 0) {
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_SYNTAX_ERROR_FMT,
"a RAISE statement with no exception name must be inside an exception handler");
return OG_ERROR;
}
raise_line->excpt_name = CM_NULL_TEXT;
raise_line->excpt_info.is_userdef = OG_FALSE;
raise_line->excpt_info.error_code = INVALID_EXCEPTION;
} else {
OG_SRC_THROW_ERROR(compiler->line_loc, ERR_PL_INVALID_EXCEPTION_FMT, W2S(&word));
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_verify_variant_assign(pl_compiler_t *compiler, plv_decl_t *decl, expr_tree_t *right,
source_location_t loc)
{
if (decl->variant.type.datatype == OG_TYPE_CURSOR) {
return plc_verify_cursor_setval(compiler, right);
}
if (!var_datatype_matched(decl->variant.type.datatype, TREE_DATATYPE(right))) {
OG_SRC_THROW_ERROR(TREE_LOC(right), ERR_TYPE_MISMATCH,
get_datatype_name_str((int32)(decl->variant.type.datatype)),
get_datatype_name_str((int32)(right->root->datatype)));
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_verify_array_assign(plv_decl_t *decl, expr_tree_t *right)
{
if (!TREE_TYPMODE(right).is_array) {
OG_SRC_THROW_ERROR(TREE_LOC(right), ERR_TYPE_MISMATCH, "ARRAY",
get_datatype_name_str((int32)(right->root->datatype)));
return OG_ERROR;
}
if (!var_datatype_matched(decl->array.type.datatype, TREE_DATATYPE(right))) {
OG_SRC_THROW_ERROR(TREE_LOC(right), ERR_TYPE_MISMATCH,
get_datatype_name_str((int32)(decl->variant.type.datatype)),
get_datatype_name_str((int32)(right->root->datatype)));
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t plc_verify_stack_var_assign(pl_compiler_t *compiler, plv_decl_t *left_decl, expr_tree_t *right)
{
switch (left_decl->type) {
case PLV_PARAM:
break;
case PLV_CUR:
OG_RETURN_IFERR(plc_verify_cursor_setval(compiler, right));
break;
case PLV_RECORD:
if (udt_verify_record_assign(right->root, left_decl->record) != OG_SUCCESS) {
cm_reset_error();
OG_SRC_THROW_ERROR(right->loc, ERR_PL_EXPR_WRONG_TYPE);
return OG_ERROR;
}
break;
case PLV_OBJECT:
if (udt_verify_object_assign(right->root, left_decl->object) != OG_SUCCESS) {
cm_reset_error();
OG_SRC_THROW_ERROR(right->loc, ERR_PL_EXPR_WRONG_TYPE);
return OG_ERROR;
}
break;
case PLV_COLLECTION:
if (!UDT_VERIFY_COLL_ASSIGN(right->root, left_decl->collection)) {
OG_SRC_THROW_ERROR(right->loc, ERR_PL_EXPR_WRONG_TYPE);
return OG_ERROR;
}
break;
case PLV_VAR:
OG_RETURN_IFERR(plc_verify_variant_assign(compiler, left_decl, right, left_decl->loc));
break;
case PLV_ARRAY:
OG_RETURN_IFERR(plc_verify_array_assign(left_decl, right));
break;
case PLV_EXCPT:
case PLV_TYPE:
default:
OG_SRC_THROW_ERROR(right->loc, ERR_PL_EXPR_WRONG_TYPE);
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_compile_return(pl_compiler_t *compiler, word_t *word)
{
lex_t *lex = compiler->stmt->session->lex;
pl_line_return_t *line = NULL;
bool32 result = OG_FALSE;
plv_decl_t *decl = NULL;
plv_id_t ret_vid = {
.block = 0,
.id = 0,
.input_id = 0
};
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_return_t), LINE_RETURN, (pl_line_ctrl_t **)&line));
if (compiler->type == PL_FUNCTION) {
PLC_RESET_WORD_LOC(lex, word);
OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &result));
if (result) {
OG_SRC_THROW_ERROR(word->loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
if (sql_create_expr_until(compiler->stmt, &line->expr, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
if (word->type != WORD_TYPE_PL_TERM) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", W2S(word));
return OG_ERROR;
}
decl = plc_find_param_by_id(compiler, ret_vid);
if (decl == NULL) {
OG_THROW_ERROR(ERR_PL_SYNTAX_ERROR_FMT, "unexpected pl-variant occurs");
return OG_ERROR;
}
OG_RETURN_IFERR(plc_check_decl_as_left(compiler, decl, compiler->last_line->loc, NULL));
OG_RETURN_IFERR(plc_verify_expr(compiler, line->expr));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->expr));
if (sql_is_skipped_expr(line->expr)) {
return OG_SUCCESS;
}
return plc_verify_stack_var_assign(compiler, decl, line->expr);
}
return lex_expected_fetch_word(lex, ";");
}
* @brief pl verify expr after using. ATTENTION: bind param can not be used after using.
*/
static inline status_t plc_verify_using_expr(pl_compiler_t *compiler, expr_tree_t *expr)
{
uint32 excl_flags;
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 plc_compile_using_clause(pl_compiler_t *compiler, pl_line_execute_t *line, word_t *word)
{
plv_direction_t dir = PLV_DIR_NONE;
expr_tree_t *expr = NULL;
bool32 result = OG_FALSE;
pl_using_expr_t *using_expr = NULL;
text_t decl_name;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(plc_init_galist(compiler, &line->using_exprs));
while (OG_TRUE) {
OG_RETURN_IFERR(plc_using_clause_get_dir(&dir, lex, &decl_name));
PLC_RESET_WORD_LOC(lex, word);
OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &result));
if (result) {
OG_SRC_THROW_ERROR(word->loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
if (sql_create_expr_until(compiler->stmt, &expr, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
return OG_ERROR;
}
OG_RETURN_IFERR(plc_verify_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));
}
decl_name.len = (uint32)(word->text.str - decl_name.str);
cm_trim_text(&decl_name);
if (decl_name.len > OG_MAX_NAME_LEN) {
decl_name.len = OG_MAX_NAME_LEN;
}
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;
OG_RETURN_IFERR(cm_galist_insert(line->using_exprs, using_expr));
if (word->text.len != 1 || word->text.str[0] != ',') {
break;
}
}
return OG_SUCCESS;
}
static status_t plc_compile_exec_immediate(pl_compiler_t *compiler, word_t *word)
{
pl_line_execute_t *line = NULL;
lex_t *lex = compiler->stmt->session->lex;
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "IMMEDIATE"));
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_execute_t), LINE_EXECUTE, (pl_line_ctrl_t **)&line));
PLC_RESET_WORD_LOC(lex, word);
OG_RETURN_IFERR(sql_create_expr_until(compiler->stmt, &line->dynamic_sql, word));
OG_RETURN_IFERR(plc_verify_expr(compiler, line->dynamic_sql));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->dynamic_sql));
if (word->id == KEY_WORD_INTO) {
OG_RETURN_IFERR(plc_compile_into_clause(compiler, &line->into, word));
line->into.prefetch_rows = INTO_VALUES_PREFETCH_COUNT;
} else if (word->id == KEY_WORD_BULK) {
OG_RETURN_IFERR(plc_compile_bulk_into_clause(compiler, &line->into, word));
}
if (word->id == KEY_WORD_USING) {
OG_RETURN_IFERR(plc_compile_using_clause(compiler, line, word));
}
if (word->type != WORD_TYPE_PL_TERM) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", W2S(word));
return OG_ERROR;
}
return OG_SUCCESS;
}
static plc_compile_lines_map_t g_plc_compile_lines_map[] = {
{ KEY_WORD_DECLARE, plc_compile_declare_ln },
{ KEY_WORD_BEGIN, plc_compile_begin_ln },
{ KEY_WORD_END, plc_compile_end_ln },
{ KEY_WORD_EXCEPTION, plc_compile_exception },
{ KEY_WORD_IF, plc_compile_if },
{ KEY_WORD_ELSIF, plc_compile_elsif },
{ KEY_WORD_ELSE, plc_compile_else },
{ KEY_WORD_CASE, plc_compile_case },
{ KEY_WORD_GOTO, plc_compile_goto },
{ KEY_WORD_LOOP, plc_compile_loop },
{ KEY_WORD_FOR, plc_compile_for },
{ KEY_WORD_FORALL, plc_compile_forall },
{ KEY_WORD_WHILE, plc_compile_while },
{ KEY_WORD_WHEN, plc_compile_when },
{ RES_WORD_NULL, plc_compile_null },
{ KEY_WORD_NULL, plc_compile_null },
{ KEY_WORD_OPEN, plc_compile_open },
{ KEY_WORD_FETCH, plc_compile_fetch },
{ KEY_WORD_CLOSE, plc_compile_close },
{ KEY_WORD_EXIT, plc_compile_exit },
{ KEY_WORD_CONTINUE, plc_compile_continue },
{ KEY_WORD_SELECT, plc_compile_sql },
{ KEY_WORD_INSERT, plc_compile_sql },
{ KEY_WORD_UPDATE, plc_compile_sql },
{ KEY_WORD_DELETE, plc_compile_sql },
{ KEY_WORD_MERGE, plc_compile_sql },
{ KEY_WORD_REPLACE, plc_compile_sql },
{ KEY_WORD_COMMIT, plc_compile_commit },
{ KEY_WORD_ROLLBACK, plc_compile_rollback },
{ KEY_WORD_SAVEPOINT, plc_compile_savepoint },
{ KEY_WORD_WITH, plc_compile_with },
{ KEY_WORD_RAISE, plc_compile_raise },
{ KEY_WORD_RETURN, plc_compile_return },
{ KEY_WORD_EXECUTE, plc_compile_exec_immediate },
};
static status_t plc_add_label(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;
compiler->labels.count++;
return OG_SUCCESS;
}
status_t plc_compile_label(pl_compiler_t *compiler, word_t *word, text_t *label_name)
{
pl_line_label_t *line = NULL;
lex_t *lex = compiler->stmt->session->lex;
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));
OG_RETURN_IFERR(plc_add_label(compiler, (pl_line_ctrl_t *)line));
OG_RETURN_IFERR(lex_expected_fetch_word(lex, ">>"));
*label_name = line->name;
line->stack_line = CURR_BLOCK_BEGIN(compiler);
return OG_SUCCESS;
}
static status_t plc_verify_var_assign(pl_compiler_t *compiler, expr_node_t *left_node, expr_tree_t *right)
{
status_t status = OG_SUCCESS;
OG_RETURN_IFERR(plc_verify_address_expr(compiler, left_node));
switch (left_node->datatype) {
case OG_TYPE_RECORD:
status = udt_verify_record_assign(right->root, (plv_record_t *)left_node->udt_type);
break;
case OG_TYPE_OBJECT:
status = udt_verify_object_assign(right->root, (plv_object_t *)left_node->udt_type);
break;
case OG_TYPE_COLLECTION:
status =
UDT_VERIFY_COLL_ASSIGN(right->root, (plv_collection_t *)left_node->udt_type) ? OG_SUCCESS : OG_ERROR;
break;
case OG_TYPE_CURSOR:
status = plc_verify_cursor_setval(compiler, right);
break;
default:
if (CM_IS_SCALAR_DATATYPE(left_node->datatype) &&
!var_datatype_matched(left_node->datatype, TREE_DATATYPE(right))) {
OG_SRC_THROW_ERROR(TREE_LOC(right), ERR_TYPE_MISMATCH,
get_datatype_name_str((int32)left_node->datatype),
get_datatype_name_str((int32)TREE_DATATYPE(right)));
return OG_ERROR;
}
break;
}
if (status != OG_SUCCESS) {
cm_reset_error();
OG_SRC_THROW_ERROR(right->loc, ERR_PL_EXPR_WRONG_TYPE);
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t plc_verify_setval(pl_compiler_t *compiler, expr_node_t *left, expr_tree_t *right)
{
OG_RETURN_IFERR(plc_verify_expr(compiler, right));
if (sql_is_skipped_expr(right)) {
return OG_SUCCESS;
}
return plc_verify_var_assign(compiler, left, right);
}
static status_t plc_compile_setval(pl_compiler_t *compiler, word_t *word, expr_node_t *expr, pl_line_normal_t *line)
{
bool32 result = OG_FALSE;
lex_t *lex = compiler->stmt->session->lex;
line->left = expr;
OG_RETURN_IFERR(plc_check_var_as_left(compiler, line->left, compiler->last_line->loc, NULL));
LEX_SAVE(lex);
PLC_RESET_WORD_LOC(lex, word);
OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &result));
if (result) {
OG_SRC_THROW_ERROR(word->loc, ERR_PLSQL_ILLEGAL_LINE_FMT,
"function or pseudo-column 'PRIOR' may be used inside a SQL statement");
return OG_ERROR;
}
if (sql_create_expr_until(compiler->stmt, &line->expr, word) != OG_SUCCESS) {
LEX_RESTORE(lex);
line->expr = NULL;
PLC_RESET_WORD_LOC(lex, word);
if (sql_create_cond_until(compiler->stmt, &line->cond, word) != OG_SUCCESS) {
pl_check_and_set_loc(word->loc);
cm_reset_error();
return OG_ERROR;
}
cm_reset_error();
OG_RETURN_IFERR(plc_verify_cond(compiler, line->cond));
} else {
OG_RETURN_IFERR(plc_verify_setval(compiler, line->left, line->expr));
}
if (word->type != WORD_TYPE_PL_TERM) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", W2S(word));
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t plc_label_name_verify(pl_compiler_t *compiler, const word_t *word)
{
if (word->type != WORD_TYPE_VARIANT && word->type != WORD_TYPE_DQ_STRING) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_LABEL_INVALID_TYPE);
return OG_ERROR;
}
if (word->ex_count > 0) {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, ">>", ".");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t plc_compile_normal_ln(pl_compiler_t *compiler, word_t *word)
{
text_t label_name = CM_NULL_TEXT;
lex_t *lex = compiler->stmt->session->lex;
expr_node_t *expr = NULL;
pl_line_normal_t *line = NULL;
lex_back(lex, word);
OG_RETURN_IFERR(lex_fetch(lex, word));
if (IS_PL_LABEL(word)) {
OG_RETURN_IFERR(lex_fetch_pl_label(lex, word));
OG_RETURN_IFERR(plc_label_name_verify(compiler, word));
return plc_compile_label(compiler, word, &label_name);
}
OG_RETURN_IFERR(plc_alloc_line(compiler, sizeof(pl_line_normal_t), LINE_SETVAL, (pl_line_ctrl_t **)&line));
OG_RETURN_IFERR(sql_alloc_mem(compiler->stmt->context, sizeof(expr_node_t), (void **)&expr));
expr->owner = NULL;
expr->type = EXPR_NODE_PROC;
expr->unary = UNARY_OPER_NONE;
expr->loc = word->text.loc;
OG_RETURN_IFERR(sql_build_func_node(compiler->stmt, word, expr));
if (word->type == WORD_TYPE_PL_OLD_COL) {
OG_SRC_THROW_ERROR_EX(word->loc, ERR_PL_SYNTAX_ERROR_FMT,
"Assigning value to ':old.' is not supported. word = %s", W2S(word));
return OG_ERROR;
}
OG_RETURN_IFERR(lex_fetch(lex, word));
if (word->type == WORD_TYPE_PL_TERM) {
OG_RETURN_IFERR(plc_compile_call(compiler, expr, line));
OG_RETURN_IFERR(plc_clone_expr_node(compiler, &line->proc));
} else if (word->type == WORD_TYPE_PL_SETVAL) {
OG_RETURN_IFERR(plc_compile_setval(compiler, word, expr, line));
OG_RETURN_IFERR(plc_clone_expr_node(compiler, &line->left));
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &line->expr));
OG_RETURN_IFERR(plc_clone_cond_tree(compiler, &line->cond));
} else {
OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "';' or ':='", W2S(word));
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t plc_compile_lines(pl_compiler_t *compiler, word_t *word)
{
OG_RETURN_IFERR(plc_stack_safe(compiler));
compiler->line_loc = word->loc;
lex_t *lex = compiler->stmt->session->lex;
lex->flags = LEX_WITH_OWNER | LEX_WITH_ARG;
uint32 func_index;
uint32 func_cnt = sizeof(g_plc_compile_lines_map) / sizeof(plc_compile_lines_map_t);
for (func_index = 0; func_index < func_cnt; func_index++) {
if (word->id == g_plc_compile_lines_map[func_index].type) {
OG_RETURN_IFERR(g_plc_compile_lines_map[func_index].func(compiler, word));
break;
}
}
if (func_index == func_cnt) {
OG_RETURN_IFERR(plc_compile_normal_ln(compiler, word));
}
return OG_SUCCESS;
}