* 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.
* -------------------------------------------------------------------------
*
* ogsql_func_verifier.c
*
*
* IDENTIFICATION
* src/ogsql/verifier/ogsql_func_verifier.c
*
* -------------------------------------------------------------------------
*/
#include "ogsql_expr_verifier.h"
#include "ogsql_func.h"
#include "ogsql_package.h"
#include "ogsql_privilege.h"
#include "srv_instance.h"
#ifdef __cplusplus
extern "C" {
#endif
static status_t inline sql_adjust_func_node(sql_stmt_t *stmt, expr_node_t *expr)
{
expr->type = EXPR_NODE_FUNC;
if (!CM_IS_EMPTY(&expr->word.column.user)) {
expr->word.func.user = expr->word.column.user;
expr->word.func.pack = expr->word.column.table;
expr->word.func.name = expr->word.column.name;
expr->word.func.count = 3;
} else {
expr->word.func.org_user = expr->word.column.table;
expr->word.func.user = expr->word.column.user_ex;
expr->word.func.pack.value = CM_NULL_TEXT;
expr->word.func.name = expr->word.column.name;
if (CM_IS_EMPTY(&expr->word.func.user)) {
expr->word.func.count = 1;
} else {
expr->word.func.count = 2;
}
}
expr->word.func.user_func_first = OG_TRUE;
expr->word.func.args.value = CM_NULL_TEXT;
if (cm_text_str_equal_ins(&expr->word.func.user.value, SYS_USER_NAME)) {
return sql_check_user_tenant(KNL_SESSION(stmt));
}
return OG_SUCCESS;
}
static status_t sql_try_verify_func3(sql_verifier_t *verif, expr_node_t *node, var_udo_t *obj, bool32 *is_found)
{
status_t status;
status = pl_try_verify_sys_pack_func3(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_pack_func3(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
return OG_SUCCESS;
}
static status_t sql_try_verify_func1(sql_verifier_t *verif, expr_node_t *node, var_udo_t *obj, bool32 *is_found)
{
bool32 user_func = node->word.func.user_func_first;
status_t status;
status = pl_try_verify_pack_func1(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_recursion_func1(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
if (user_func) {
status = pl_try_verify_func1(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_builtin_func(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_pack_std(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
} else {
status = pl_try_verify_builtin_func(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_pack_std(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_func1(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
}
status = pl_try_verify_public_func1(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
return OG_SUCCESS;
}
static status_t sql_try_verify_func2(sql_verifier_t *verif, expr_node_t *node, var_udo_t *obj, bool32 *is_found)
{
status_t status;
status = pl_try_verify_recursion_func2(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_pack_func2(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_sys_pack_func2(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_func2(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
status = pl_try_verify_public_func2(verif, node, obj, is_found);
if (*is_found) {
return status;
}
pl_revert_last_error(status);
return OG_SUCCESS;
}
static status_t pl_check_same_arg_name(expr_node_t *func)
{
expr_tree_t *arg1 = NULL;
expr_tree_t *arg2 = NULL;
for (arg1 = func->argument; arg1 != NULL; arg1 = arg1->next) {
OG_CONTINUE_IFTRUE(arg1->arg_name.len == 0);
for (arg2 = arg1->next; arg2 != NULL; arg2 = arg2->next) {
OG_CONTINUE_IFTRUE(arg2->arg_name.len == 0);
if (cm_compare_text(&arg1->arg_name, &arg2->arg_name) == 0) {
OG_SRC_THROW_ERROR(arg1->loc, ERR_PL_DUP_ARG_FMT, T2S(&arg1->arg_name),
T2S_EX(&func->word.func.name.value));
return OG_ERROR;
}
}
}
return OG_SUCCESS;
}
static status_t sql_try_verify_func(sql_verifier_t *verif, expr_node_t *node, bool32 *is_found)
{
var_udo_t obj;
char user[OG_NAME_BUFFER_SIZE];
char pack[OG_NAME_BUFFER_SIZE];
char name[OG_NAME_BUFFER_SIZE];
sql_init_udo_with_str(&obj, user, pack, name);
OG_RETURN_IFERR(pl_check_same_arg_name(node));
if (node->word.func.count == 1 || node->word.func.count == 0) {
return sql_try_verify_func1(verif, node, &obj, is_found);
} else if (node->word.func.count == 2) {
return sql_try_verify_func2(verif, node, &obj, is_found);
} else {
return sql_try_verify_func3(verif, node, &obj, is_found);
}
}
static status_t sql_verify_func_return_error(sql_verifier_t *verif, expr_node_t *node)
{
if (node->word.func.count == 1 || node->word.func.count == 0) {
return pl_try_verify_return_error1(verif, node);
} else if (node->word.func.count == 2) {
return pl_try_verify_return_error2(verif, node);
} else {
return pl_try_verify_return_error3(verif, node);
}
}
status_t sql_verify_func(sql_verifier_t *verif, expr_node_t *node)
{
bool32 is_found = OG_FALSE;
verif->excl_flags |= SQL_EXCL_METH_PROC;
uint32 save_excl_flags = verif->excl_flags;
OG_BIT_RESET(verif->excl_flags, SQL_EXCL_ARRAY);
OG_BIT_SET(verif->excl_flags, SQL_EXCL_STAR);
status_t status = sql_try_verify_func(verif, node, &is_found);
verif->excl_flags = save_excl_flags;
OG_RETURN_IFERR(status);
if (!is_found) {
return sql_verify_func_return_error(verif, node);
}
if ((node->type == EXPR_NODE_PROC || node->type == EXPR_NODE_USER_PROC) && (verif->excl_flags & SQL_EXCL_PL_PROC)) {
OG_SRC_THROW_ERROR(node->loc, ERR_SQL_SYNTAX_ERROR, "procedure is not allowed here.");
return OG_ERROR;
}
if (verif->create_table_define &&
(node->type == EXPR_NODE_PROC || node->type == EXPR_NODE_USER_PROC || node->type == EXPR_NODE_USER_FUNC)) {
OG_SRC_THROW_ERROR(node->loc, ERR_UNSUPPORT_FUNC, "procedure, function, package, or type", "here");
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t sql_try_verify_noarg_func(sql_verifier_t *verif, expr_node_t *node, bool32 *is_found)
{
if (node->type == EXPR_NODE_RESERVED) {
return OG_FALSE;
}
expr_node_t node_bak = *node;
if (sql_adjust_func_node(verif->stmt, node) != OG_SUCCESS) {
*node = node_bak;
return OG_ERROR;
}
status_t status = sql_try_verify_func(verif, node, is_found);
OG_RETURN_IFERR(status);
if (!(*is_found)) {
*node = node_bak;
}
return OG_SUCCESS;
}
static inline void sql_merge_first_exec_node(sql_verifier_t *verif, expr_node_t *node, uint32 new_idx)
{
SQL_SET_OPTMZ_MODE(node, OPTMZ_FIRST_EXEC_NODE);
if (new_idx != NODE_OPTMZ_IDX(node)) {
--(verif->context->fexec_vars_cnt);
node->optmz_info.idx = (uint16)new_idx;
}
if (OG_IS_VARLEN_TYPE(node->datatype)) {
verif->context->fexec_vars_bytes -= node->size;
}
}
static inline status_t sql_scan_func_args_optmz_mode(expr_node_t *func, expr_tree_t *expr,
expr_optmz_info_t *func_optmz_info)
{
while (expr != NULL) {
if (func_optmz_info->mode > NODE_OPTIMIZE_MODE(expr->root)) {
func_optmz_info->mode = NODE_OPTIMIZE_MODE(expr->root);
switch (func_optmz_info->mode) {
case OPTIMIZE_NONE:
SQL_SET_OPTMZ_MODE(func, OPTIMIZE_NONE);
return OG_ERROR;
case OPTMZ_FIRST_EXEC_ROOT:
func_optmz_info->idx = MIN(func_optmz_info->idx, NODE_OPTMZ_IDX(expr->root));
break;
case OPTIMIZE_AS_CONST:
case OPTIMIZE_AS_PARAM:
break;
default:
CM_NEVER;
break;
}
}
expr = expr->next;
}
return OG_SUCCESS;
}
void sql_infer_func_optmz_mode(sql_verifier_t *verif, expr_node_t *func)
{
expr_tree_t *expr = func->argument;
if (expr == NULL) {
return;
}
expr_optmz_info_t func_optmz_info = {
.mode = OPTMZ_INVAILD,
.idx = OG_INVALID_ID16
};
OG_RETVOID_IFERR(sql_scan_func_args_optmz_mode(func, expr, &func_optmz_info));
if (func_optmz_info.mode == OPTIMIZE_AS_CONST) {
SQL_SET_OPTMZ_MODE(func, OPTIMIZE_AS_CONST);
return;
}
if (func_optmz_info.mode == OPTIMIZE_AS_PARAM) {
sql_add_first_exec_node(verif, func);
return;
}
if (func_optmz_info.idx == OG_INVALID_ID16) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "idx(%u) != OG_INVALID_ID16(%u)", (uint32)func_optmz_info.idx,
(uint32)OG_INVALID_ID16);
}
if (func_optmz_info.mode != OPTMZ_FIRST_EXEC_ROOT) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "mode(%u) == OPTMZ_FIRST_EXEC_ROOT(%u)", (uint32)func_optmz_info.mode,
(uint32)OPTMZ_FIRST_EXEC_ROOT);
}
func->optmz_info.mode = OPTMZ_FIRST_EXEC_ROOT;
func->optmz_info.idx = func_optmz_info.idx;
expr = func->argument;
while (expr != NULL) {
if (NODE_IS_FIRST_EXECUTABLE(expr->root)) {
sql_merge_first_exec_node(verif, expr->root, func_optmz_info.idx);
}
expr = expr->next;
}
if (OG_IS_VARLEN_TYPE(func->datatype)) {
verif->context->fexec_vars_bytes += func->size;
}
}
void sql_infer_oper_optmz_mode(sql_verifier_t *verif, expr_node_t *node)
{
optmz_mode_t mode = MIN(NODE_OPTIMIZE_MODE(node->left), NODE_OPTIMIZE_MODE(node->right));
uint32 index = OG_INVALID_ID16;
if (mode == OPTIMIZE_NONE || mode == OPTIMIZE_AS_CONST || mode == OPTIMIZE_AS_PARAM) {
SQL_SET_OPTMZ_MODE(node, mode);
return;
}
if (mode != OPTMZ_FIRST_EXEC_ROOT) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "mode(%u) == OPTMZ_FIRST_EXEC_ROOT(%u)", (uint32)mode,
(uint32)OPTMZ_FIRST_EXEC_ROOT);
}
if (NODE_IS_FIRST_EXECUTABLE(node->left)) {
index = NODE_OPTMZ_IDX(node->left);
}
if (NODE_IS_FIRST_EXECUTABLE(node->right)) {
index = MIN(index, NODE_OPTMZ_IDX(node->right));
}
node->optmz_info.mode = OPTMZ_FIRST_EXEC_ROOT;
node->optmz_info.idx = (uint16)index;
if (NODE_IS_FIRST_EXECUTABLE(node->left)) {
sql_merge_first_exec_node(verif, node->left, index);
}
if (NODE_IS_FIRST_EXECUTABLE(node->right)) {
sql_merge_first_exec_node(verif, node->right, index);
}
}
void sql_infer_unary_oper_optmz_mode(sql_verifier_t *verif, expr_node_t *node)
{
optmz_mode_t mode = NODE_OPTIMIZE_MODE(node->right);
uint32 index = OG_INVALID_ID16;
if (mode == OPTIMIZE_NONE || mode == OPTIMIZE_AS_CONST || mode == OPTIMIZE_AS_PARAM) {
SQL_SET_OPTMZ_MODE(node, mode);
return;
}
if (mode != OPTMZ_FIRST_EXEC_ROOT) {
OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "mode(%u) == OPTMZ_FIRST_EXEC_ROOT(%u)", (uint32)mode,
(uint32)OPTMZ_FIRST_EXEC_ROOT);
}
if (NODE_IS_FIRST_EXECUTABLE(node->right)) {
index = MIN(index, NODE_OPTMZ_IDX(node->right));
}
node->optmz_info.mode = OPTMZ_FIRST_EXEC_ROOT;
node->optmz_info.idx = (uint16)index;
if (NODE_IS_FIRST_EXECUTABLE(node->right)) {
sql_merge_first_exec_node(verif, node->right, index);
}
}
void sql_add_first_exec_node(sql_verifier_t *verif, expr_node_t *node)
{
while (verif->do_expr_optmz) {
if (verif->context->fexec_vars_cnt >= SQL_MAX_FEXEC_VARS) {
break;
}
if (OG_IS_LOB_TYPE(node->datatype)) {
break;
}
if (OG_IS_VARLEN_TYPE(node->datatype)) {
if (verif->context->fexec_vars_bytes + node->size >= SQL_MAX_FEXEC_VAR_BYTES) {
break;
}
verif->context->fexec_vars_bytes += node->size;
}
node->optmz_info.idx = verif->context->fexec_vars_cnt++;
node->optmz_info.mode = OPTMZ_FIRST_EXEC_ROOT;
return;
}
SQL_SET_OPTMZ_MODE(node, OPTIMIZE_NONE);
}
#ifdef __cplusplus
}
#endif