/* -------------------------------------------------------------------------
 *  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.
 * -------------------------------------------------------------------------
 *
 * decl_cl.c
 *
 *
 * IDENTIFICATION
 * src/ogsql/pl/parser/decl_cl.c
 *
 * -------------------------------------------------------------------------
 */
#include "decl_cl.h"
#include "base_compiler.h"
#include "srv_instance.h"
#include "pl_memory.h"
#include "trigger_decl_cl.h"
#include "pragma_cl.h"
#include "cursor_cl.h"
#include "typedef_cl.h"

plv_decl_t *plc_find_param_by_id(pl_compiler_t *compiler, plv_id_t plv_id)
{
    return (plv_decl_t *)cm_galist_get(compiler->params, plv_id.id);
}

plv_decl_t *plc_find_decl_by_id(pl_compiler_t *compiler, plv_id_t plv_id)
{
    if (plv_id.block >= (int16)compiler->stack.depth) {
        if (plv_id.block != 0 || compiler->decls == NULL || plv_id.id >= compiler->decls->count) {
            return NULL;
        }

        return (plv_decl_t *)cm_galist_get(compiler->decls, plv_id.id);
    }

    uint16 depth = (uint16)plv_id.block;
    plc_block_t *block = &compiler->stack.items[depth];
    pl_line_ctrl_t *line = block->entry;
    galist_t *decls = NULL;

    if (line->type == LINE_BEGIN) {
        decls = ((pl_line_begin_t *)line)->decls;
    } else if (line->type == LINE_FOR) {
        decls = ((pl_line_for_t *)line)->decls;
    } else {
        return NULL;
    }

    if (decls == NULL || plv_id.id >= decls->count) {
        return NULL;
    }

    return (plv_decl_t *)cm_galist_get(decls, plv_id.id);
}

/*
 * @brief   seek label if exists, and avoid duplicate label
 */
void plc_find_label(pl_compiler_t *compiler, text_t *label, pl_line_ctrl_t **line, bool32 *result)
{
    uint32 i;
    pl_line_label_t *cmp_line = NULL;
    uint32 label_num = 0;

    for (i = 0; i < compiler->labels.count; i++) {
        cmp_line = (pl_line_label_t *)compiler->labels.lines[i];
        if (cm_text_equal(&cmp_line->name, label)) {
            *line = compiler->labels.lines[i];
            label_num++;
        }
    }

    *result = (label_num == 1);
}

/*
 * @brief    search variant decl by block name and variant own name in BEGIN line
 */
void plc_find_in_begin_block(pl_compiler_t *compiler, uint32 stack_id, plc_variant_name_t *var, uint32 types,
    plv_decl_t **decl)
{
    plc_block_t *block = &compiler->stack.items[stack_id];
    pl_line_begin_t *begin_line = (pl_line_begin_t *)block->entry;
    plv_decl_t *select = NULL;

    if ((var->block_name.len != 0) && !cm_text_equal_ins(&var->block_name, &block->name)) {
        return;
    }

    if (begin_line->decls == NULL) {
        return;
    }

    plc_find_in_decls(begin_line->decls, &var->name, var->case_sensitive, &select);
    if (select && (select->type & types) == 0) {
        return;
    }
    *decl = select;
}

/*
 * @brief    search variant decl by block name and variant own name in FOR line
 */
static void plc_find_in_for_block(pl_compiler_t *compiler, uint32 stack_id, plc_variant_name_t *var, uint32 types,
    plv_decl_t **decl)
{
    plc_block_t *block = &compiler->stack.items[stack_id];
    pl_line_for_t *line = (pl_line_for_t *)block->entry;
    bool32 result = OG_FALSE;

    if ((var->block_name.len != 0) && !cm_text_equal(&var->block_name, &block->name)) {
        return;
    }

    plc_cmp_name(&line->id->name, &var->name, var->case_sensitive, &result);
    if (!result || (line->id->type & types) == 0) {
        return;
    }

    *decl = line->id;
}

/*
 * @brief   search variant decl by block name and variant own name in compiler's block
 */
void plc_find_block_decl(pl_compiler_t *compiler, plc_variant_name_t *variant_name, plv_decl_t **decl)
{
    plc_block_t *block = NULL;
    plv_decl_t *item = NULL;

    /* Name is occured at begin block. */
    for (int32 i = (int32)compiler->stack.depth - 1; i >= 0; i--) {
        block = &compiler->stack.items[i];
        switch (block->entry->type) {
            case LINE_FOR:
                plc_find_in_for_block(compiler, (uint32)i, variant_name, variant_name->types, &item);
                break;
            case LINE_BEGIN:
                plc_find_in_begin_block(compiler, (uint32)i, variant_name, variant_name->types, &item);
                break;
            default:
                break;
        }

        if (item != NULL && item->arg_type != PLV_CURSOR_ARG) {
            // has found decl but we not return cursor_arg decl
            *decl = item;
            return;
        }
    }
    item = NULL;

    /* Name is occured at declare block. */
    if (compiler->decls != NULL) {
        plc_find_in_decls(compiler->decls, &variant_name->name, variant_name->case_sensitive, &item);
        if (item == NULL || (item->type & variant_name->types) == 0 || item->arg_type == PLV_CURSOR_ARG) {
            return;
        }

        *decl = item;
        return;
    }
}

/*
 * @brief    change the variant name to block name and variant own name
 */
static void plc_translate_variant(pl_compiler_t *compiler, word_t *word, plc_variant_name_t *variant_name,
                           plc_var_type_t *var_type)
{
    pl_line_ctrl_t *line = NULL;
    bool32 is_label = OG_FALSE;
    bool32 is_upper_case = OG_TRUE;

    variant_name->case_sensitive = IS_DQ_STRING(word->type);
    if (word->ex_count == 0) {
        *var_type = PLC_NORMAL_VAR;
        variant_name->block_name.len = 0;
        plc_concat_text_upper_by_type(&variant_name->name, OG_MAX_NAME_LEN, &word->text.value, word->type);
        return;
    }

    // 触发器变量,例如:new.a, :old.b
    if (IS_TRIGGER_WORD_TYPE(word)) {
        *var_type = PLC_TRIGGER_VAR;
        variant_name->block_name.len = 0;
        plc_get_trig_decl_name(compiler, &variant_name->name, word, &is_upper_case);
        variant_name->case_sensitive = OG_TRUE;
        return;
    }

    // 带多个.的,例如a.b
    if (word->ex_count >= 1) {
        plc_concat_text_upper_by_type(&variant_name->block_name, OG_MAX_NAME_LEN, &word->text.value, word->type);
        // 先找a是不是label,可能是兄弟label,也可能是父子label
        plc_find_label(compiler, &variant_name->block_name, &line, &is_label);
        if (is_label) {
            *var_type = PLC_BLOCK_VAR;
            plc_concat_text_upper_by_type(&variant_name->name, OG_MAX_NAME_LEN, &word->ex_words[0].text.value,
                word->ex_words[0].type);
            return;
        } else {
            variant_name->block_name.len = 0;
            plc_concat_text_upper_by_type(&variant_name->name, OG_MAX_NAME_LEN, &word->text.value, word->type);
        }
    }
    *var_type = PLC_MULTIEX_VAR;
}

/*
 * @brief    search decl by name and type, before using plc_find_decl_ex, check len of word
 */
void plc_find_decl_ex(pl_compiler_t *compiler, word_t *word, uint32 types, plc_var_type_t *var_type, plv_decl_t **decl)
{
    plc_var_type_t type;
    char block_name_buf[OG_NAME_BUFFER_SIZE];
    char name_buf[OG_NAME_BUFFER_SIZE];
    plc_variant_name_t variant_name;
    *decl = NULL;
    PLC_INIT_VARIANT_NAME(&variant_name, block_name_buf, name_buf, OG_FALSE, types); // 初始化plc_variant_name_t对象
    plc_translate_variant(compiler, word, &variant_name, &type);

    if (var_type != NULL) {
        *var_type = type;
    }
    if (type == PLC_NORMAL_VAR || type == PLC_TRIGGER_VAR) {
        plc_find_block_decl(compiler, &variant_name, decl);
        return;
    }
    if (type == PLC_BLOCK_VAR) {
        // 往上找父块是否有匹配的
        plc_find_block_decl(compiler, &variant_name, decl);
        if (*decl == NULL) {
            type = PLC_MULTIEX_VAR;
            variant_name.block_name.len = 0;
            variant_name.name.len = 0;
            plc_concat_text_upper_by_type(&variant_name.name, OG_MAX_NAME_LEN, &word->text.value, word->type);
        } else {
            if (word->ex_count == 1) {
                return;
            }
            if (var_type != NULL) {
                *var_type = PLC_BLOCK_MULTIEX_VAR;
            }
            return;
        }
    }

    if (var_type != NULL) {
        *var_type = type;
    }
    plc_find_block_decl(compiler, &variant_name, decl);
}

status_t plc_find_decl(pl_compiler_t *compiler, word_t *word, uint32 types, plc_var_type_t *var_type, plv_decl_t **decl)
{
    size_t witer_pos;
    OG_RETURN_IFERR(plc_verify_word_as_var(compiler, word));
    plc_find_decl_ex(compiler, word, types, var_type, decl);

    if (*decl != NULL) {
        return OG_SUCCESS;
    }

    witer_pos = strlen(g_tls_error.message);
    if (IS_TRIGGER_WORD_TYPE(word)) {
        OG_RETURN_IFERR(plc_add_trigger_decl(compiler, 0, word, PLV_VAR, decl));
        if (*decl != NULL) {
            return OG_SUCCESS;
        }
    }

    if (strlen(g_tls_error.message) == witer_pos) {
        OG_SRC_THROW_ERROR(word->loc, ERR_UNDEFINED_SYMBOL_FMT, W2S(word));
    }

    return OG_ERROR;
}

/*
 * @brief    pl's check datatype, it's important to support blob/clob/image in pl
 */
status_t plc_check_datatype(pl_compiler_t *compiler, typmode_t *type, bool32 is_arg)
{
    lex_t *lex = compiler->stmt->session->lex;
    switch (type->datatype) {
        case OG_TYPE_CHAR:
            if (is_arg) {
                type->size = (uint16)OG_MAX_COLUMN_SIZE;
                return OG_SUCCESS;
            }
            if (type->size > OG_MAX_COLUMN_SIZE) {
                OG_SRC_THROW_ERROR_EX(lex->loc, ERR_PL_SYNTAX_ERROR_FMT, "size of CHAR must less than %d",
                    OG_MAX_COLUMN_SIZE);
                return OG_ERROR;
            }
            return OG_SUCCESS;

        case OG_TYPE_STRING:
        case OG_TYPE_VARCHAR:
            if (is_arg) {
                type->size = (uint16)OG_MAX_STRING_LEN;
                return OG_SUCCESS;
            }
            if (type->size > OG_MAX_STRING_LEN) {
                OG_SRC_THROW_ERROR_EX(lex->loc, ERR_PL_SYNTAX_ERROR_FMT, "size of VARCHAR must less than %d",
                    OG_MAX_STRING_LEN);
                return OG_ERROR;
            }
            return OG_SUCCESS;

        case OG_TYPE_BINARY:
        case OG_TYPE_VARBINARY:
        case OG_TYPE_RAW:
        case OG_TYPE_INTEGER:
        case OG_TYPE_BOOLEAN:
        case OG_TYPE_BIGINT:
        case OG_TYPE_REAL:
        case OG_TYPE_NUMBER:
        case OG_TYPE_NUMBER2:
        case OG_TYPE_DATE:
        case OG_TYPE_TIMESTAMP:
        case OG_TYPE_TIMESTAMP_TZ_FAKE:
        case OG_TYPE_TIMESTAMP_TZ:
        case OG_TYPE_TIMESTAMP_LTZ:
        case OG_TYPE_INTERVAL_DS:
        case OG_TYPE_INTERVAL_YM:
        case OG_TYPE_CURSOR:
        case OG_TYPE_DECIMAL:
        case OG_TYPE_UINT32:
        case OG_TYPE_UINT64:
        case OG_TYPE_SMALLINT:
        case OG_TYPE_USMALLINT:
        case OG_TYPE_TINYINT:
        case OG_TYPE_UTINYINT:
            return OG_SUCCESS;

        case OG_TYPE_BLOB:
            type->datatype = OG_TYPE_RAW;
            type->size = OG_MAX_COLUMN_SIZE;
            return OG_SUCCESS;

        case OG_TYPE_CLOB:
        case OG_TYPE_IMAGE:
            type->datatype = OG_TYPE_STRING;
            type->size = OG_MAX_STRING_LEN;
            return OG_SUCCESS;

        default:
            OG_SRC_THROW_ERROR(lex->loc, ERR_INVALID_DATA_TYPE, "unknown");
            return OG_ERROR;
    }
}

status_t plc_check_record_datatype(pl_compiler_t *compiler, plv_decl_t *decl, bool32 is_arg)
{
    uint32 i;
    for (i = 0; i < decl->record->count; i++) {
        plv_record_attr_t *attr = udt_seek_field_by_id(decl->record, i);
        if (attr->type != UDT_SCALAR) {
            continue;
        }

        OG_RETURN_IFERR(plc_check_datatype(compiler, &attr->scalar_field->type_mode, is_arg));
    }
    return OG_SUCCESS;
}

/*
 * @brief    compile variant's default expr define
 */
status_t plc_compile_default_def(pl_compiler_t *compiler, word_t *word, plv_decl_t *decl, bool32 is_arg)
{
    bool32 result = OG_FALSE;
    lex_t *lex = compiler->stmt->session->lex;
    OG_RETURN_IFERR(lex_try_fetch(lex, ":=", &result));
    if (!result) {
        OG_RETURN_IFERR(lex_try_fetch(lex, "DEFAULT", &result));
    }

    if (!result) {
        if (is_arg) {
            OG_RETURN_IFERR(lex_fetch(lex, word));
        } else {
            OG_RETURN_IFERR(lex_expected_fetch_word(lex, ";"));
        }

        return OG_SUCCESS;
    }

    if (decl->drct == PLV_DIR_OUT || decl->drct == PLV_DIR_INOUT) {
        OG_SRC_THROW_ERROR(word->loc, ERR_PL_OUT_PARAM_WITH_DFT);
        return OG_ERROR;
    }

    lex->flags = LEX_WITH_OWNER | LEX_WITH_ARG;

    PLC_RESET_WORD_LOC(lex, word);
    OG_RETURN_IFERR(lex_try_fetch(lex, "PRIOR", &result));
    if (result) {
        OG_SRC_THROW_ERROR(word->loc, ERR_PL_ENCOUNT_PRIOR);
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_create_expr_until(compiler->stmt, &decl->default_expr, word));
    OG_RETURN_IFERR(plc_verify_expr(compiler, decl->default_expr));
    OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &decl->default_expr));

    if (is_arg) {
        return OG_SUCCESS;
    }
    if (!IS_SPEC_CHAR(word, ';')) {
        OG_SRC_THROW_ERROR(word->loc, ERR_PL_EXPECTED_FAIL_FMT, "';'", "OTHERS");
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

status_t plc_extract_table_column(pl_compiler_t *compiler, word_t *word, var_udo_t *obj, text_t *column)
{
    session_t *cmpl_session = compiler->stmt->session;
    if ((word->ex_count == 0) || (word->ex_count >= MAX_EXTRA_TEXTS)) {
        OG_SRC_THROW_ERROR(word->loc, ERR_PL_ATTR_TYPE_FMT, cmpl_session->curr_schema, W2S(word));
        return OG_ERROR;
    }

    if (word->ex_count == 1) {
        OG_RETURN_IFERR(cm_text_copy_from_str(&obj->user, cmpl_session->curr_schema, OG_NAME_BUFFER_SIZE));
        OG_RETURN_IFERR(cm_text_copy(&obj->name, OG_NAME_BUFFER_SIZE, &word->text.value));
        obj->user_explicit = OG_FALSE;
        *column = word->ex_words[0].text.value;
    } else {
        text_t user;
        OG_RETURN_IFERR(sql_copy_prefix_tenant(compiler->stmt, &word->text.value, &user, sql_copy_text));
        cm_text_copy_upper(&obj->user, &user);
        OG_RETURN_IFERR(cm_text_copy(&obj->name, OG_NAME_BUFFER_SIZE, &word->ex_words[0].text.value));
        *column = word->ex_words[1].text.value;
        obj->user_explicit = OG_TRUE;
    }

    if (IS_CASE_INSENSITIVE) {
        cm_text_upper(&obj->name);
        cm_text_upper(column);
    }
    return OG_SUCCESS;
}

expr_node_t *plc_get_param_vid(pl_compiler_t *compiler, uint32 p_nid)
{
    galist_t *input = NULL;
    plv_decl_t *decl = NULL;
    pl_line_ctrl_t *line_ctrl = compiler->last_line;

    if (line_ctrl == NULL) {
        return NULL;
    }

    switch (line_ctrl->type) {
        case LINE_SQL:
            input = ((pl_line_sql_t *)line_ctrl)->input;
            break;
        case LINE_OPEN:
            decl = plc_find_decl_by_id(compiler, ((pl_line_open_t *)line_ctrl)->vid);
            if (decl == NULL) {
                return NULL;
            }
            input = (decl->cursor.ogx->is_sysref) ? ((pl_line_open_t *)line_ctrl)->input : decl->cursor.input;
            break;
        case LINE_FOR:
            if (!((pl_line_for_t *)line_ctrl)->is_cur) {
                return NULL;
            }
            decl = plc_find_decl_by_id(compiler, ((pl_line_for_t *)line_ctrl)->cursor_id);
            if (decl == NULL) {
                return NULL;
            }
            input = decl->cursor.input;
            break;
        default:
            return NULL;
    }

    if (input == NULL || p_nid >= input->count) {
        return NULL;
    }

    return (expr_node_t *)cm_galist_get(input, p_nid);
}

status_t plc_compile_decl(pl_compiler_t *compiler, galist_t *decls, word_t *word)
{
    uint32 matched_id;
    plv_decl_t *decl = NULL;
    bool32 result = OG_FALSE;
    lex_t *lex = compiler->stmt->session->lex;
    plc_check_duplicate(decls, (text_t *)&word->text, IS_DQ_STRING(word->type), &result);

    if (result) {
        OG_SRC_THROW_ERROR(word->loc, ERR_DUPLICATE_NAME, "declaration", T2S((text_t *)&word->text));
        return OG_ERROR;
    }
    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); // not overflow
    decl->loc = word->loc;

    OG_RETURN_IFERR(lex_try_fetch_1of2(lex, "EXCEPTION", "SYS_REFCURSOR", &matched_id));

    if (matched_id == 0) {
        decl->type = PLV_EXCPT;
        OG_RETURN_IFERR(plc_compile_excpt_def(compiler, word, decl));
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, ";"));
    } else if (matched_id == 1) {
        decl->type = PLV_CUR;
        OG_RETURN_IFERR(plc_compile_syscursor_def(compiler, word, decl));
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, ";"));
    } else {
        OG_RETURN_IFERR(plc_compile_variant_def(compiler, word, decl, OG_FALSE, decls, OG_TRUE));
        OG_RETURN_IFERR(plc_compile_default_def(compiler, word, decl, OG_FALSE));
    }
    return OG_SUCCESS;
}

/*
 * @brief    complex type compile, only support record and cursor
 */
status_t plc_compile_complex_type(pl_compiler_t *compiler, plv_decl_t *decl, plv_decl_t *type_recur)
{
    plv_typdef_t *type = &type_recur->typdef;
    pl_entity_t *pl_entity = compiler->entity;
    switch (decl->type) {
        case PLV_RECORD:
            decl->record = &type->record;
            return plc_check_record_datatype(compiler, decl, OG_FALSE);
        case PLV_OBJECT:
            decl->object = &type->object;
            return plc_check_object_datatype(compiler, decl, OG_FALSE);
        case PLV_CUR:
            OG_RETURN_IFERR(pl_alloc_mem(pl_entity, sizeof(plv_cursor_context_t), (void **)&decl->cursor.ogx));
            decl->cursor.ogx->is_sysref = (bool8)OG_TRUE;
            break;
        case PLV_COLLECTION:
            decl->collection = &type->collection;
            break;
        default:
            break;
    }
    return OG_SUCCESS;
}

status_t plc_bison_compile_default_def(pl_compiler_t *compiler, plv_decl_t *decl, expr_tree_t *expr)
{
    if (expr == NULL) {
        return OG_SUCCESS;
    }

    if (decl->drct == PLV_DIR_OUT || decl->drct == PLV_DIR_INOUT) {
        OG_THROW_ERROR(ERR_PL_OUT_PARAM_WITH_DFT);
        return OG_ERROR;
    }

    if (expr->root->type == EXPR_NODE_PRIOR) {
        OG_THROW_ERROR(ERR_PL_ENCOUNT_PRIOR);
        return OG_ERROR;
    }

    decl->default_expr = expr;
    OG_RETURN_IFERR(plc_verify_expr(compiler, decl->default_expr));
    OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &decl->default_expr));

    return OG_SUCCESS;
}

static void plc_bison_translate_variant(pl_compiler_t *compiler, plc_variant_name_t *variant_name,
    plc_var_type_t *var_type, text_t *name, galist_t *sub_names)
{
    pl_line_ctrl_t *line = NULL;
    bool32 is_label = OG_FALSE;

    if (sub_names == NULL) {
        *var_type = PLC_NORMAL_VAR;
        variant_name->block_name.len = 0;
        plc_concat_text_upper_by_type(&variant_name->name, OG_MAX_NAME_LEN, name, WORD_TYPE_VARIANT);
        return;
    }

    if (sub_names != NULL) {
        plc_concat_text_upper_by_type(&variant_name->block_name, OG_MAX_NAME_LEN, name, WORD_TYPE_VARIANT);
        plc_find_label(compiler, &variant_name->block_name, &line, &is_label);
        if (is_label) {
            *var_type = PLC_BLOCK_VAR;
            plc_concat_text_upper_by_type(&variant_name->name, OG_MAX_NAME_LEN, (text_t*)cm_galist_get(sub_names, 0),
                WORD_TYPE_VARIANT);
            return;
        } else {
            variant_name->block_name.len = 0;
            plc_concat_text_upper_by_type(&variant_name->name, OG_MAX_NAME_LEN, name, WORD_TYPE_VARIANT);
        }
    }
    *var_type = PLC_MULTIEX_VAR;
}

void plc_bison_find_decl_ex(pl_compiler_t *compiler, uint32 types, plc_var_type_t *var_type, plv_decl_t **decl,
    char *first_name, galist_t *sub_names)
{
    text_t name;
    cm_str2text(first_name, &name);
    plc_var_type_t type;
    char block_name_buf[OG_NAME_BUFFER_SIZE];
    char name_buf[OG_NAME_BUFFER_SIZE];
    plc_variant_name_t variant_name;
    *decl = NULL;
    PLC_INIT_VARIANT_NAME(&variant_name, block_name_buf, name_buf, OG_FALSE, types);
    plc_bison_translate_variant(compiler, &variant_name, &type, &name, sub_names);

    if (var_type != NULL) {
        *var_type = type;
    }
    if (type == PLC_NORMAL_VAR || type == PLC_TRIGGER_VAR) {
        plc_find_block_decl(compiler, &variant_name, decl);
        return;
    }
    if (type == PLC_BLOCK_VAR) {
        plc_find_block_decl(compiler, &variant_name, decl);
        if (*decl == NULL) {
            type = PLC_MULTIEX_VAR;
            variant_name.block_name.len = 0;
            variant_name.name.len = 0;
            plc_concat_text_upper_by_type(&variant_name.name, OG_MAX_NAME_LEN, &name, WORD_TYPE_VARIANT);
        } else {
            if (sub_names->count == 1) {
                return;
            }
            if (var_type != NULL) {
                *var_type = PLC_BLOCK_MULTIEX_VAR;
            }
            return;
        }
    }

    if (var_type != NULL) {
        *var_type = type;
    }
    plc_find_block_decl(compiler, &variant_name, decl);
}

status_t plc_bison_extract_table_column(pl_compiler_t *compiler, var_udo_t *obj, text_t *column, char *column_buf,
    type_word_t *type)
{
    session_t *cmpl_session = compiler->stmt->session;
    galist_t *name_parts = type->is_name_typemode ? type->typemode : NULL;
    if (name_parts == NULL || name_parts->count == 0 || (name_parts->count >= MAX_EXTRA_TEXTS)) {
        OG_SRC_THROW_ERROR(type->loc, ERR_PL_ATTR_TYPE_FMT, cmpl_session->curr_schema, type->str);
        return OG_ERROR;
    }

    column->str = column_buf;
    column->len = 0;
    if (name_parts->count == 1) {
        text_t raw_name;
        text_t *raw_column = (text_t *)cm_galist_get(name_parts, 0);

        OG_RETURN_IFERR(cm_text_copy_from_str(&obj->user, cmpl_session->curr_schema, OG_NAME_BUFFER_SIZE));
        cm_str2text(type->str, &raw_name);
        OG_RETURN_IFERR(cm_text_copy(&obj->name, OG_NAME_BUFFER_SIZE, &raw_name));
        obj->user_explicit = OG_FALSE;
        OG_RETURN_IFERR(cm_text_copy(column, OG_NAME_BUFFER_SIZE, raw_column));
    } else {
        text_t user;
        text_t raw_user;
        text_t *raw_name = (text_t *)cm_galist_get(name_parts, 0);
        text_t *raw_column = (text_t *)cm_galist_get(name_parts, 1);

        cm_str2text(type->str, &raw_user);
        OG_RETURN_IFERR(sql_copy_prefix_tenant(compiler->stmt, &raw_user, &user, sql_copy_text));
        cm_text_copy_upper(&obj->user, &user);
        OG_RETURN_IFERR(cm_text_copy(&obj->name, OG_NAME_BUFFER_SIZE, raw_name));
        OG_RETURN_IFERR(cm_text_copy(column, OG_NAME_BUFFER_SIZE, raw_column));
        obj->user_explicit = OG_TRUE;
    }

    if (IS_CASE_INSENSITIVE) {
        cm_text_upper(&obj->name);
        cm_text_upper(column);
    }
    return OG_SUCCESS;
}