/* -------------------------------------------------------------------------
 *  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_cond_rewrite.c
 *
 *
 * IDENTIFICATION
 * src/ogsql/optimizer/ogsql_cond_rewrite.c
 *
 * -------------------------------------------------------------------------
 */
#include "ogsql_table_func.h"
#include "ogsql_func.h"
#include "ogsql_cond_rewrite.h"
#include "srv_instance.h"
#include "dml_parser.h"
#include "plan_rbo.h"
#include "plan_range.h"
#include "ogsql_optim_common.h"

#ifdef __cplusplus
extern "C" {
#endif

static inline status_t replace_group_node(visit_assist_t *visit_ass, expr_node_t **node)
{
    if ((*node)->type != EXPR_NODE_GROUP || NODE_VM_ANCESTOR(*node) > 0) {
        return OG_SUCCESS;
    }

    expr_node_t *origin_ref = sql_get_origin_ref(*node);
    OG_RETURN_IFERR(sql_clone_expr_node(visit_ass->stmt->context, origin_ref, node, sql_alloc_mem));
    return visit_expr_node(visit_ass, node, replace_group_node);
}

status_t replace_group_expr_node(sql_stmt_t *stmt, expr_node_t **node)
{
    visit_assist_t visit_ass;
    sql_init_visit_assist(&visit_ass, stmt, NULL);
    return visit_expr_node(&visit_ass, node, replace_group_node);
}

static inline bool32 sql_cols_is_same_tab(uint32 tab, cols_used_t *cols_used)
{
    if (cols_used->flags == 0) {
        return OG_TRUE;
    }

    if ((cols_used->flags & (FLAG_HAS_PARENT_COLS | FLAG_HAS_ANCESTOR_COLS)) != 0) {
        return OG_FALSE;
    }

    if ((cols_used->level_flags[SELF_IDX] & LEVEL_HAS_DIFF_TABS) != 0) {
        return OG_FALSE;
    }

    expr_node_t *first_node = OBJECT_OF(expr_node_t, biqueue_first(&cols_used->cols_que[SELF_IDX]));
    return (tab == TAB_OF_NODE(first_node));
}

static status_t sql_try_simplify_cond(sql_stmt_t *stmt, cond_node_t *cond, uint32 *rnum_upper, bool8 *rnum_pending)
{
    OG_RETURN_IFERR(sql_stack_safe(stmt));
    switch (cond->type) {
        case COND_NODE_AND:
            OG_RETURN_IFERR(sql_try_simplify_cond(stmt, cond->left, rnum_upper, rnum_pending));
            OG_RETURN_IFERR(sql_try_simplify_cond(stmt, cond->right, rnum_upper, rnum_pending));
            try_eval_logic_and(cond);
            return OG_SUCCESS;
        case COND_NODE_OR:
            OG_RETURN_IFERR(sql_try_simplify_cond(stmt, cond->left, rnum_upper, rnum_pending));
            OG_RETURN_IFERR(sql_try_simplify_cond(stmt, cond->right, rnum_upper, rnum_pending));
            try_eval_logic_or(cond);
            return OG_SUCCESS;
        case COND_NODE_COMPARE:
            return try_eval_compare_node(stmt, cond, rnum_upper, rnum_pending);
        default:
            return OG_SUCCESS;
    }
}

status_t sql_try_simplify_new_cond(sql_stmt_t *stmt, cond_node_t *cond)
{
    if (IS_COORDINATOR || stmt->context->has_dblink) {
        return OG_SUCCESS;
    }
    uint32 rnum_upper = OG_INVALID_ID32;
    bool8 rnum_pending = OG_FALSE;
    return sql_try_simplify_cond(stmt, cond, &rnum_upper, &rnum_pending);
}

static status_t update_select_node_object(visit_assist_t *visit_ass, expr_node_t **node)
{
    if ((*node)->type != EXPR_NODE_SELECT) {
        return OG_SUCCESS;
    }
    sql_select_t *select = (sql_select_t *)VALUE_PTR(var_object_t, &(*node)->value)->ptr;
    sql_select_t *ssa = NULL;
    for (uint32 i = 0; i < visit_ass->query->ssa.count; i++) {
        ssa = (sql_select_t *)sql_array_get(&visit_ass->query->ssa, i);
        if (ssa == select) {
            (*node)->value.v_obj.id = i;
            break;
        }
    }
    return OG_SUCCESS;
}

static status_t og_update_multi_set_pair(column_value_pair_t *col_val_pair, visit_assist_t *v_ast,
    uint32 pair_expr_idx)
{
    expr_tree_t *exprtr = (expr_tree_t *)cm_galist_get(col_val_pair->exprs, pair_expr_idx);
    return visit_expr_node(v_ast, &exprtr->root, update_select_node_object);
}

static status_t og_handle_update_col_val_pair_ssa(sql_update_t *upd)
{
    OG_RETVALUE_IFTRUE(upd->pairs == NULL, OG_ERROR);
    visit_assist_t v_ast = { 0 };
    sql_init_visit_assist(&v_ast, NULL, upd->query);
    column_value_pair_t *col_val_pair = (column_value_pair_t *)cm_galist_get(upd->pairs, 0);
    if (col_val_pair->rs_no > 0) {
        return og_update_multi_set_pair(col_val_pair, &v_ast, 0);
    }
    uint32 pair_idx = 0;
    while (pair_idx < upd->pairs->count) {
        col_val_pair = (column_value_pair_t *)cm_galist_get(upd->pairs, pair_idx);
        pair_idx++;
        uint32 expr_idx = 0;
        while (expr_idx < col_val_pair->exprs->count) {
            OG_RETURN_IFERR(og_update_multi_set_pair(col_val_pair, &v_ast, expr_idx));
            expr_idx++;
        }
    }
    return OG_SUCCESS;
}

status_t sql_update_query_ssa(sql_stmt_t *statement, sql_query_t *query)
{
    visit_assist_t visit_ass;
    sql_init_visit_assist(&visit_ass, statement, query);
    if (query->cond != NULL) {
        OG_RETURN_IFERR(visit_cond_node(&visit_ass, query->cond->root, update_select_node_object));
    }
    if (query->having_cond != NULL) {
        OG_RETURN_IFERR(visit_cond_node(&visit_ass, query->having_cond->root, update_select_node_object));
    }
    if (query->start_with_cond != NULL) {
        OG_RETURN_IFERR(visit_cond_node(&visit_ass, query->start_with_cond->root, update_select_node_object));
    }
    if (query->connect_by_cond != NULL) {
        OG_RETURN_IFERR(visit_cond_node(&visit_ass, query->connect_by_cond->root, update_select_node_object));
    }
    if (query->filter_cond != NULL) {
        OG_RETURN_IFERR(visit_cond_node(&visit_ass, query->filter_cond->root, update_select_node_object));
    }
    if (query->join_assist.join_node != NULL) {
        OG_RETURN_IFERR(visit_join_node_cond(&visit_ass, query->join_assist.join_node, update_select_node_object));
    }
    for (uint32 i = 0; i < query->sort_items->count; i++) {
        sort_item_t *sort_item = (sort_item_t *)cm_galist_get(query->sort_items, i);
        OG_RETURN_IFERR(visit_expr_tree(&visit_ass, sort_item->expr, update_select_node_object));
    }
    for (uint32 i = 0; i < query->aggrs->count; i++) {
        expr_node_t *node = (expr_node_t *)cm_galist_get(query->aggrs, i);
        OG_RETURN_IFERR(visit_expr_node(&visit_ass, &node, update_select_node_object));
    }

    if (statement->context->type == OGSQL_TYPE_UPDATE) {
        sql_update_t *upd = (sql_update_t *)statement->context->entry;
        OG_RETURN_IFERR(og_handle_update_col_val_pair_ssa(upd));
    }
    return OG_SUCCESS;
}

uint32 sql_get_func_table_column_count(sql_stmt_t *stmt, sql_table_t *table)
{
    plv_collection_t *plv_coll = NULL;
    if (cm_text_str_equal(&table->func.name, "CAST")) {
        plv_coll = (plv_collection_t *)table->func.args->next->root->udt_type;
        return plv_coll->attr_type == UDT_OBJECT ? UDT_GET_TYPE_DEF_OBJECT(plv_coll->elmt_type)->count :
                                                     table->func.desc->column_count;
    } else {
        return table->func.desc->column_count;
    }
}

static uint32 sql_get_table_column_count(sql_stmt_t *stmt, sql_table_t *table)
{
    switch (table->type) {
        case VIEW_AS_TABLE:
        case SUBSELECT_AS_TABLE:
        case WITH_AS_TABLE:
            return table->select_ctx->first_query->rs_columns->count;
        case FUNC_AS_TABLE:
            return sql_get_func_table_column_count(stmt, table);
        case JSON_TABLE:
            return table->json_table_info->columns.count;
        default:
            return knl_get_column_count(table->entry->dc.handle);
    }
}
/* *******************predicate deliver************************ */
static inline status_t sql_init_dlvr_pair(sql_stmt_t *stmt, sql_query_t *query, dlvr_pair_t **dlvr_pair,
                                          galist_t *pairs)
{
    uint32 mem_size;
    sql_table_t *table = NULL;

    OG_RETURN_IFERR(cm_galist_new(pairs, sizeof(dlvr_pair_t), (void **)dlvr_pair));
    cm_galist_init(&(*dlvr_pair)->cols, stmt->session->stack, cm_stack_alloc);
    cm_galist_init(&(*dlvr_pair)->values, stmt->session->stack, cm_stack_alloc);

    for (uint32 i = 0; i < query->tables.count; i++) {
        table = (sql_table_t *)sql_array_get(&query->tables, i);
        mem_size = sql_get_table_column_count(stmt, table) * sizeof(uint32);
        OG_RETURN_IFERR(cm_stack_alloc(stmt->session->stack, mem_size, (void **)&(*dlvr_pair)->col_map[i]));
        MEMS_RETURN_IFERR(memset_s((*dlvr_pair)->col_map[i], mem_size, 0, mem_size));
    }
    return OG_SUCCESS;
}

bool32 if_dlvr_border_equal(sql_stmt_t *stmt, plan_border_t *border1, plan_border_t *border2)
{
    if (border1->type != border2->type || border1->closed != border2->closed) {
        return OG_FALSE;
    }
    return sql_expr_tree_equal(stmt, border1->expr, border2->expr, NULL);
}

bool32 if_dlvr_range_equal(sql_stmt_t *stmt, plan_range_t *range1, plan_range_t *range2)
{
    if (range1->type != range2->type) {
        return OG_FALSE;
    }

    switch (range1->type) {
        case RANGE_LIST:
        case RANGE_POINT:
        case RANGE_LIKE:
            // left border is the same as the right, so we just need to compare the left border
            return if_dlvr_border_equal(stmt, &range1->left, &range2->left);
        case RANGE_SECTION:
            if (!if_dlvr_border_equal(stmt, &range1->left, &range2->left)) {
                return OG_FALSE;
            }
            return if_dlvr_border_equal(stmt, &range1->right, &range2->right);
        default:
            break;
    }
    return OG_FALSE;
}

static inline bool32 sql_dlvr_pair_exists_col(expr_tree_t *col, dlvr_pair_t *dlvr_pair)
{
    return dlvr_pair->col_map[EXPR_TAB(col)][EXPR_COL(col)];
}

static inline status_t sql_dlvr_pair_add_col(expr_tree_t *column, dlvr_pair_t *dlvr_pair)
{
    uint16 tab = EXPR_TAB(column);
    uint16 col = EXPR_COL(column);
    if (dlvr_pair->col_map[tab][col]) {
        return OG_SUCCESS;
    }
    dlvr_pair->col_map[tab][col] = OG_TRUE;
    return cm_galist_insert(&dlvr_pair->cols, column);
}

static status_t sql_dlvr_pair_try_add_ff(expr_tree_t *left, expr_tree_t *right, dlvr_pair_t *dlvr_pair, bool32
    *is_found)
{
    if (sql_dlvr_pair_exists_col(left, dlvr_pair)) {
        *is_found = OG_TRUE;
        return sql_dlvr_pair_add_col(right, dlvr_pair);
    }

    if (sql_dlvr_pair_exists_col(right, dlvr_pair)) {
        *is_found = OG_TRUE;
        return sql_dlvr_pair_add_col(left, dlvr_pair);
    }
    return OG_SUCCESS;
}

static status_t sql_dlvr_pairs_add_ff_pair(sql_stmt_t *stmt, sql_query_t *query, expr_tree_t *left, expr_tree_t *right,
    galist_t *pairs)
{
    dlvr_pair_t *dlvr_pair = NULL;
    OG_RETURN_IFERR(sql_init_dlvr_pair(stmt, query, &dlvr_pair, pairs));
    OG_RETURN_IFERR(sql_dlvr_pair_add_col(left, dlvr_pair));
    return sql_dlvr_pair_add_col(right, dlvr_pair);
}

bool32 get_specified_level_query(sql_query_t *curr_query, uint32 level, sql_query_t **query, sql_select_t **subslct)
{
    uint32 depth = 0;
    sql_select_t *first_level_subslct = NULL;

    while (depth < level) {
        if (curr_query->owner == NULL || curr_query->owner->parent == NULL) {
            return OG_FALSE;
        }
        first_level_subslct = curr_query->owner;
        curr_query = curr_query->owner->parent;
        depth++;
    }
    *query = curr_query;
    if (subslct != NULL) {
        *subslct = first_level_subslct;
    }
    return OG_TRUE;
}

static inline bool32 if_cond_can_be_pulled(expr_node_t *node)
{
    switch (node->type) {
        case EXPR_NODE_CONST:
        case EXPR_NODE_PARAM:
        case EXPR_NODE_CSR_PARAM:
        case EXPR_NODE_PL_ATTR:
            return OG_TRUE;
        case EXPR_NODE_V_ADDR:
            return sql_pair_type_is_plvar(node);
        default:
            // If want to support parent or ancestor columns, cannot include pushed-down ones
            return OG_FALSE;
    }
}

static inline bool32 if_range_need_merge(plan_range_t *range)
{
    if (range->type != RANGE_SECTION && range->type != RANGE_POINT) {
        return OG_FALSE;
    }

    if ((range->left.type != BORDER_CONST && range->left.type != BORDER_INFINITE_LEFT) ||
        (range->right.type != BORDER_CONST && range->right.type != BORDER_INFINITE_RIGHT)) {
        return OG_FALSE;
    }
    return OG_TRUE;
}

static inline bool32 sql_dlvr_inter_border(sql_stmt_t *stmt, plan_border_t *border1, plan_border_t *border2,
    uint32 ref_val, plan_border_t *result, bool32 is_left)
{
    if (border1->type == ref_val) {
        *result = *border2;
        return OG_TRUE;
    }

    if (border2->type == ref_val) {
        *result = *border1;
        return OG_TRUE;
    }
    return sql_inter_const_range(stmt, border1, border2, is_left, result);
}

bool32 sql_dlvr_inter_range(sql_stmt_t *stmt, plan_range_t *range1, plan_range_t *range2, plan_range_t *result)
{
    if (!sql_dlvr_inter_border(stmt, &range1->left, &range2->left, BORDER_INFINITE_LEFT, &result->left, OG_TRUE)) {
        return OG_FALSE;
    }

    if (!sql_dlvr_inter_border(stmt, &range1->right, &range2->right, BORDER_INFINITE_RIGHT, &result->right, OG_FALSE)) {
        return OG_FALSE;
    }

    result->type = RANGE_SECTION;
    result->datatype = range1->datatype;
    if (result->left.type == BORDER_CONST && result->right.type == BORDER_CONST &&
        sql_verify_const_range(stmt, result) != OG_SUCCESS) {
        cm_reset_error();
        return OG_FALSE;
    }
    return OG_TRUE;
}

static inline status_t sql_dlvr_pair_add_range(sql_stmt_t *stmt, dlvr_pair_t *pair, plan_range_t *new_range,
    bool32 *is_false)
{
    plan_range_t result;
    plan_range_t *range = NULL;

    for (uint32 i = 0; i < pair->values.count; i++) {
        range = (plan_range_t *)cm_galist_get(&pair->values, i);
        if (if_range_need_merge(range) && if_range_need_merge(new_range) &&
            sql_dlvr_inter_range(stmt, range, new_range, &result)) {
            if (result.type == RANGE_EMPTY) {
                *is_false = OG_TRUE;
            } else {
                *range = result;
            }
            return OG_SUCCESS;
        }
        if (if_dlvr_range_equal(stmt, range, new_range)) {
            return OG_SUCCESS;
        }
    }
    return cm_galist_insert(&pair->values, new_range);
}

static inline bool32 sql_dlvr_pair_exists_value(sql_stmt_t *stmt, plan_range_t *new_range, dlvr_pair_t *pair)
{
    for (uint32 i = 0; i < pair->values.count; i++) {
        plan_range_t *range = (plan_range_t *)cm_galist_get(&pair->values, i);
        if (range->type == RANGE_POINT && if_dlvr_range_equal(stmt, range, new_range)) {
            return OG_TRUE;
        }
    }
    return OG_FALSE;
}

static inline status_t sql_dlvr_make_range(sql_stmt_t *stmt, og_type_t col_datatype, cmp_type_t cmp_type,
    expr_tree_t *val, plan_range_t **range)
{
    if (cm_stack_alloc(stmt->session->stack, sizeof(plan_range_t), (void **)range) != OG_SUCCESS) {
        return OG_ERROR;
    }

    (*range)->datatype = col_datatype;
    sql_make_range(cmp_type, val, *range);
    return OG_SUCCESS;
}

static inline bool32 if_cond_num_exceed_max(sql_query_t *query, galist_t *pairs)
{
    dlvr_pair_t *dlvr_pair = NULL;

    for (uint32 i = 0; i < pairs->count; i++) {
        dlvr_pair = (dlvr_pair_t *)cm_galist_get(pairs, i);
        if (dlvr_pair->cols.count > OG_MAX_DLVR_COLS_COUNT) {
            return OG_TRUE;
        }
    }

    return OG_FALSE;
}

static inline bool32 if_cond_dlvr_support(cond_node_t *cond)
{
    cols_used_t cols_used;

    init_cols_used(&cols_used);
    sql_collect_cols_in_cond(cond, &cols_used);
    if (HAS_SUBSLCT(&cols_used)) {
        return OG_FALSE;
    }
    return (cond->cmp->type == CMP_TYPE_EQUAL);
}

static status_t dlvr_pull_range_with_cmp(sql_stmt_t *stmt, cond_node_t *cond, dlvr_pair_t *dlvr_pair,
    expr_tree_t *ancestor_col, bool32 *is_false)
{
    expr_tree_t *val = NULL;
    expr_tree_t *col = NULL;
    cmp_node_t *cmp = cond->cmp;
    plan_range_t *new_range = NULL;
    cmp_type_t cmp_type = cmp->type;

    if (!if_cond_dlvr_support(cond)) {
        return OG_SUCCESS;
    }

    if (IS_LOCAL_COLUMN(cmp->left) && EXPR_TAB(cmp->left) == EXPR_TAB(ancestor_col) &&
        EXPR_COL(cmp->left) == EXPR_COL(ancestor_col)) {
        col = cmp->left;
        val = cmp->right;
    } else if (IS_LOCAL_COLUMN(cmp->right) && EXPR_TAB(cmp->right) == EXPR_TAB(ancestor_col) &&
        EXPR_COL(cmp->right) == EXPR_COL(ancestor_col)) {
        col = cmp->right;
        val = cmp->left;
        // for 'in', 'like', 'between', column must be the left operand of comparison
        // so cmp_node->type is reversible
        cmp_type = sql_reverse_cmp(cmp_type);
    } else {
        return OG_SUCCESS;
    }

    if (!if_cond_can_be_pulled(val->root)) {
        return OG_SUCCESS;
    }

    OG_RETURN_IFERR(sql_dlvr_make_range(stmt, TREE_DATATYPE(col), cmp_type, val, &new_range));
    return sql_dlvr_pair_add_range(stmt, dlvr_pair, new_range, is_false);
}

static status_t dlvr_pull_ancestor_range(sql_stmt_t *stmt, cond_node_t *cond, dlvr_pair_t *dlvr_pair,
    expr_tree_t *ancestor_col, bool32 *is_false)
{
    OG_RETURN_IFERR(sql_stack_safe(stmt));
    switch (cond->type) {
        case COND_NODE_AND:
            OG_RETURN_IFERR(dlvr_pull_ancestor_range(stmt, cond->left, dlvr_pair, ancestor_col, is_false));
            if (*is_false) {
                return OG_SUCCESS;
            }
            return dlvr_pull_ancestor_range(stmt, cond->right, dlvr_pair, ancestor_col, is_false);

        case COND_NODE_COMPARE:
            return dlvr_pull_range_with_cmp(stmt, cond, dlvr_pair, ancestor_col, is_false);

        default:
            return OG_SUCCESS;
    }
}

static inline status_t sql_dlvr_pull_ancestor_cond(sql_stmt_t *stmt, sql_query_t *query, dlvr_pair_t *dlvr_pair,
    plan_range_t *range, bool32 *is_false)
{
    if (range->type != RANGE_POINT) {
        return OG_SUCCESS;
    }

    expr_tree_t *val = range->left.expr;
    uint32 ancestor = EXPR_ANCESTOR(val);
    sql_query_t *ancestor_query = NULL;

    if (IS_COORDINATOR || stmt->context->has_dblink || !IS_NORMAL_COLUMN(val) || ancestor == 0 ||
        !get_specified_level_query(query, ancestor, &ancestor_query, NULL)) {
        return OG_SUCCESS;
    }

    if (ancestor_query->cond == NULL || ancestor_query->cond->root == NULL) {
        return OG_SUCCESS;
    }

    return dlvr_pull_ancestor_range(stmt, ancestor_query->cond->root, dlvr_pair, val, is_false);
}

static inline status_t sql_dlvr_pair_add_values(sql_stmt_t *stmt, sql_query_t *query, dlvr_pair_t *dlvr_pair,
    plan_range_t *new_range, bool32 *is_false)
{
    OG_RETURN_IFERR(sql_dlvr_pair_add_range(stmt, dlvr_pair, new_range, is_false));
    if (*is_false) {
        return OG_SUCCESS;
    }
    return sql_dlvr_pull_ancestor_cond(stmt, query, dlvr_pair, new_range, is_false);
}

static inline status_t sql_dlvr_merge_pair_values(sql_stmt_t *stmt, dlvr_pair_t *src, dlvr_pair_t *dst,
    bool32 *is_false)
{
    for (uint32 i = 0; i < src->values.count; i++) {
        plan_range_t *range = (plan_range_t *)cm_galist_get(&src->values, i);
        OG_RETURN_IFERR(sql_dlvr_pair_add_range(stmt, dst, range, is_false));
        if (*is_false) {
            break;
        }
    }
    return OG_SUCCESS;
}

static inline status_t sql_dlvr_merge_pair_columns(dlvr_pair_t *src, dlvr_pair_t *dst)
{
    for (uint32 i = 0; i < src->cols.count; i++) {
        expr_tree_t *col = (expr_tree_t *)cm_galist_get(&src->cols, i);
        OG_RETURN_IFERR(sql_dlvr_pair_add_col(col, dst));
    }
    return OG_SUCCESS;
}

static inline status_t sql_dlvr_merge_pair(sql_stmt_t *stmt, dlvr_pair_t *src, dlvr_pair_t *dst, bool32 *is_false)
{
    OG_RETURN_IFERR(sql_dlvr_merge_pair_values(stmt, src, dst, is_false));
    if (*is_false) {
        return OG_SUCCESS;
    }
    return sql_dlvr_merge_pair_columns(src, dst);
}

static inline status_t sql_dlvr_try_merge_pairs(sql_stmt_t *stmt, uint32 start_pos, expr_tree_t *left,
    expr_tree_t *right, dlvr_pair_t *merge_pair, galist_t *pairs, bool32 *is_false)
{
    dlvr_pair_t *dlvr_pair = NULL;

    for (uint32 i = start_pos; i < pairs->count;) {
        dlvr_pair = (dlvr_pair_t *)cm_galist_get(pairs, i);
        if (sql_dlvr_pair_exists_col(left, dlvr_pair) || (right != NULL &&
                                                          sql_dlvr_pair_exists_col(right, dlvr_pair))) {
            OG_RETURN_IFERR(sql_dlvr_merge_pair(stmt, dlvr_pair, merge_pair, is_false));
            if (*is_false) {
                break;
            }
            cm_galist_delete(pairs, i);
            continue;
        }
        i++;
    }
    return OG_SUCCESS;
}

static inline status_t sql_dlvr_pairs_add_ff(sql_stmt_t *stmt, sql_query_t *query, expr_tree_t *left,
    expr_tree_t *right, galist_t *pairs, bool32 *is_false)
{
    bool32 is_found = OG_FALSE;
    dlvr_pair_t *pair = NULL;

    for (uint32 i = 0; i < pairs->count; i++) {
        pair = (dlvr_pair_t *)cm_galist_get(pairs, i);
        OG_RETURN_IFERR(sql_dlvr_pair_try_add_ff(left, right, pair, &is_found));
        if (is_found) {
            return sql_dlvr_try_merge_pairs(stmt, i + 1, left, right, pair, pairs, is_false);
        }
    }
    return sql_dlvr_pairs_add_ff_pair(stmt, query, left, right, pairs);
}

static inline status_t sql_dlvr_pairs_add_fv_pair(sql_stmt_t *stmt, sql_query_t *query, expr_tree_t *col,
    plan_range_t *new_range, galist_t *pairs)
{
    bool32 is_false = OG_FALSE;
    dlvr_pair_t *pair = NULL;

    OG_RETURN_IFERR(sql_init_dlvr_pair(stmt, query, &pair, pairs));
    OG_RETURN_IFERR(sql_dlvr_pair_add_col(col, pair));
    OG_RETURN_IFERR(cm_galist_insert(&pair->values, new_range));
    return sql_dlvr_pull_ancestor_cond(stmt, query, pair, new_range, &is_false);
}

static inline bool32 has_semi_in_expr(sql_query_t *query, expr_tree_t *expr)
{
    if (!IS_LOCAL_COLUMN(expr)) {
        return OG_FALSE;
    }
    sql_table_t *table = (sql_table_t *)sql_array_get(&query->tables, expr->root->value.v_col.tab);
    if (table->subslct_tab_usage == SUBSELECT_4_NORMAL_JOIN) {
        return OG_FALSE;
    }
    return OG_TRUE;
}

bool32 has_semi_in_cmp_node(sql_query_t *query, cmp_node_t *cmp)
{
    return (bool32)(has_semi_in_expr(query, cmp->left) || has_semi_in_expr(query, cmp->right));
}

status_t expr_node_is_dlvr_value(visit_assist_t *visit_ass, expr_node_t **node)
{
    switch ((*node)->type) {
        case EXPR_NODE_PARAM:
        case EXPR_NODE_CSR_PARAM:
        case EXPR_NODE_CONST:
            return OG_SUCCESS;

        case EXPR_NODE_RESERVED:
            if ((*node)->value.v_int == RES_WORD_SYSDATE || (*node)->value.v_int == RES_WORD_SYSTIMESTAMP ||
                ((*node)->value.v_int == RES_WORD_ROWID && (*node)->value.v_rid.ancestor > 0)) {
                return OG_SUCCESS;
            }
            break;

        case EXPR_NODE_COLUMN:
            if (NODE_ANCESTOR(*node) > 0) {
                return OG_SUCCESS;
            }
            break;
        case EXPR_NODE_V_ADDR:
            if (sql_pair_type_is_plvar(*node)) {
                return OG_SUCCESS;
            }
            break;
        default:
            break;
    }
    visit_ass->result0 = OG_FALSE;
    return OG_SUCCESS;
}

status_t expr_tree_is_dlvr_value(sql_stmt_t *stmt, expr_tree_t *expr_tree, bool32 *is_dlvr)
{
    visit_assist_t visit_ass;
    sql_init_visit_assist(&visit_ass, stmt, NULL);
    visit_ass.result0 = OG_TRUE;
    OG_RETURN_IFERR(visit_expr_tree(&visit_ass, expr_tree, expr_node_is_dlvr_value));
    *is_dlvr = (bool32)visit_ass.result0;
    return OG_SUCCESS;
}

status_t pre_generate_dlvr_cond(sql_stmt_t *stmt, expr_tree_t *column, cond_node_t **node)
{
    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(cond_node_t), (void **)node));
    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(cmp_node_t), (void **)&(*node)->cmp));
    (*node)->type = COND_NODE_COMPARE;
    return sql_clone_expr_tree(stmt->context, column, &(*node)->cmp->left, sql_alloc_mem);
}

static inline status_t sql_generate_ff_cond(sql_stmt_t *stmt, expr_tree_t *left, expr_tree_t *right, cond_tree_t *cond,
    bool32 has_filter_cond)
{
    cond_node_t *node = NULL;
    OG_RETURN_IFERR(pre_generate_dlvr_cond(stmt, left, &node));
    node->cmp->type = CMP_TYPE_EQUAL;
    node->cmp->has_conflict_chain = has_filter_cond;
    OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, right, &node->cmp->right, sql_alloc_mem));
    return sql_add_cond_node_left(cond, node);
}

static inline status_t generate_range_list_cond(sql_stmt_t *stmt, expr_tree_t *left, plan_range_t *range,
    cond_tree_t *cond)
{
    cond_node_t *node = NULL;
    expr_tree_t **next = NULL;
    expr_tree_t *arg = range->left.expr;

    OG_RETURN_IFERR(pre_generate_dlvr_cond(stmt, left, &node));
    node->cmp->type = CMP_TYPE_IN;
    next = &node->cmp->right;
    while (arg) {
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, arg, next, sql_alloc_mem));
        arg = arg->next;
        next = &(*next)->next;
    }
    return sql_add_cond_node_left(cond, node);
}

static inline status_t generate_range_like_cond(sql_stmt_t *stmt, expr_tree_t *left, plan_range_t *range,
    cond_tree_t *cond)
{
    cond_node_t *node = NULL;

    OG_RETURN_IFERR(pre_generate_dlvr_cond(stmt, left, &node));
    node->cmp->type = CMP_TYPE_LIKE;
    OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->left.expr, &node->cmp->right, sql_alloc_mem));
    return sql_add_cond_node_left(cond, node);
}

static inline status_t generate_range_point_cond(sql_stmt_t *stmt, expr_tree_t *left, plan_range_t *range,
    cond_tree_t *cond)
{
    cond_node_t *node = NULL;

    OG_RETURN_IFERR(pre_generate_dlvr_cond(stmt, left, &node));
    node->cmp->type = CMP_TYPE_EQUAL;
    OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->left.expr, &node->cmp->right, sql_alloc_mem));
    return sql_add_cond_node_left(cond, node);
}

static inline status_t generate_range_section_cond(sql_stmt_t *stmt, expr_tree_t *left, plan_range_t *range,
    cond_tree_t *cond)
{
    cond_node_t *node = NULL;
    OG_RETURN_IFERR(pre_generate_dlvr_cond(stmt, left, &node));

    if (range->left.type == BORDER_INFINITE_LEFT) {
        node->cmp->type = range->right.closed ? CMP_TYPE_LESS_EQUAL : CMP_TYPE_LESS;
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->right.expr, &node->cmp->right, sql_alloc_mem));
    } else if (range->right.type == BORDER_INFINITE_RIGHT) {
        node->cmp->type = range->left.closed ? CMP_TYPE_GREAT_EQUAL : CMP_TYPE_GREAT;
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->left.expr, &node->cmp->right, sql_alloc_mem));
    } else if (range->left.closed && range->right.closed) {
        node->cmp->type = CMP_TYPE_BETWEEN;
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->left.expr, &node->cmp->right, sql_alloc_mem));
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->right.expr, &node->cmp->right->next, sql_alloc_mem));
    } else {
        node->cmp->type = range->right.closed ? CMP_TYPE_LESS_EQUAL : CMP_TYPE_LESS;
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->right.expr, &node->cmp->right, sql_alloc_mem));
        OG_RETURN_IFERR(sql_add_cond_node_left(cond, node));

        OG_RETURN_IFERR(pre_generate_dlvr_cond(stmt, left, &node));
        node->cmp->type = range->left.closed ? CMP_TYPE_GREAT_EQUAL : CMP_TYPE_GREAT;
        OG_RETURN_IFERR(sql_clone_expr_tree(stmt->context, range->left.expr, &node->cmp->right, sql_alloc_mem));
    }
    return sql_add_cond_node_left(cond, node);
}

status_t sql_generate_fv_cond(sql_stmt_t *stmt, expr_tree_t *left, plan_range_t *range, cond_tree_t *cond)
{
    switch (range->type) {
        case RANGE_LIST:
            return generate_range_list_cond(stmt, left, range, cond);

        case RANGE_LIKE:
            return generate_range_like_cond(stmt, left, range, cond);

        case RANGE_POINT:
            return generate_range_point_cond(stmt, left, range, cond);

        case RANGE_SECTION:
            return generate_range_section_cond(stmt, left, range, cond);

        default:
            OG_THROW_ERROR(ERR_NOT_SUPPORT_TYPE, (int32)range->type);
            return OG_ERROR;
    }
}

static inline status_t sql_generate_fv_dlvr_conds(sql_stmt_t *stmt, cond_tree_t *cond, dlvr_pair_t *dlvr_pair)
{
    expr_tree_t *col = NULL;
    plan_range_t *range = NULL;

    for (uint32 i = 0; i < dlvr_pair->cols.count; i++) {
        col = (expr_tree_t *)cm_galist_get(&dlvr_pair->cols, i);
        for (uint32 j = 0; j < dlvr_pair->values.count; j++) {
            range = (plan_range_t *)cm_galist_get(&dlvr_pair->values, j);
            OG_RETURN_IFERR(sql_generate_fv_cond(stmt, col, range, cond));
        }
    }
    return OG_SUCCESS;
}

static status_t sql_generate_ff_dlvr_conds(sql_stmt_t *stmt, cond_tree_t *cond, dlvr_pair_t *dlvr_pair)
{
    expr_tree_t *left = NULL;
    expr_tree_t *right = NULL;
    plan_range_t *range = NULL;
    bool32 has_filter_cond = OG_FALSE;
    for (uint32 j = 0; j < dlvr_pair->values.count; j++) {
        range = (plan_range_t *)cm_galist_get(&dlvr_pair->values, j);
        if (range->type == RANGE_POINT) {
            has_filter_cond = OG_TRUE;
            break;
        }
    }
    for (uint32 i = 0; i < dlvr_pair->cols.count - 1; i++) {
        left = (expr_tree_t *)cm_galist_get(&dlvr_pair->cols, i);
        for (uint32 j = i + 1; j < dlvr_pair->cols.count; j++) {
            right = (expr_tree_t *)cm_galist_get(&dlvr_pair->cols, j);
            OG_RETURN_IFERR(sql_generate_ff_cond(stmt, left, right, cond, has_filter_cond));
        }
    }
    return OG_SUCCESS;
}

static inline status_t sql_generate_dlvr_cond(sql_stmt_t *stmt, cond_tree_t *cond, dlvr_pair_t *dlvr_pair)
{
    // generate join condition
    OG_RETURN_IFERR(sql_generate_ff_dlvr_conds(stmt, cond, dlvr_pair));
    // generate filter condition
    return sql_generate_fv_dlvr_conds(stmt, cond, dlvr_pair);
}

static inline status_t sql_generate_dlvr_conds(sql_stmt_t *stmt, cond_tree_t *cond, galist_t *pairs)
{
    dlvr_pair_t *dlvr_pair = NULL;

    for (uint32 i = 0; i < pairs->count; i++) {
        dlvr_pair = (dlvr_pair_t *)cm_galist_get(pairs, i);
        OG_RETURN_IFERR(sql_generate_dlvr_cond(stmt, cond, dlvr_pair));
    }
    return OG_SUCCESS;
}

static status_t sql_generate_dlvr_pairs(sql_stmt_t *stmt, sql_query_t *query, expr_tree_t *l_col, galist_t *values,
    galist_t *ff_pairs, galist_t *dlvr_pairs)
{
    bool32 is_found = OG_FALSE;
    dlvr_pair_t *ff_pair = NULL;
    dlvr_pair_t *dlvr_pair = NULL;

    for (uint32 i = 0; i < ff_pairs->count; i++) {
        ff_pair = (dlvr_pair_t *)cm_galist_get(ff_pairs, i);
        if (sql_dlvr_pair_exists_col(l_col, ff_pair)) {
            is_found = OG_TRUE;
            break;
        }
    }

    if (!is_found) {
        return OG_SUCCESS;
    }

    OG_RETURN_IFERR(sql_init_dlvr_pair(stmt, query, &dlvr_pair, dlvr_pairs));

    for (uint32 i = 0; i < ff_pair->cols.count; i++) {
        expr_tree_t *r_col = (expr_tree_t *)cm_galist_get(&ff_pair->cols, i);
        if (EXPR_TAB(r_col) == EXPR_TAB(l_col) && EXPR_COL(r_col) == EXPR_COL(l_col)) {
            continue;
        }
        OG_RETURN_IFERR(sql_dlvr_pair_add_col(r_col, dlvr_pair));
    }

    for (uint32 i = 0; i < values->count; i++) {
        plan_range_t *range = (plan_range_t *)cm_galist_get(values, i);
        OG_RETURN_IFERR(cm_galist_insert(&dlvr_pair->values, range));
    }
    return OG_SUCCESS;
}

static inline status_t sql_try_generate_dlvr_pairs(sql_stmt_t *stmt, sql_query_t *query, galist_t *ff_pairs,
    galist_t *fv_pairs, galist_t *dlvr_pairs)
{
    for (uint32 i = 0; i < fv_pairs->count; i++) {
        dlvr_pair_t *pair = (dlvr_pair_t *)cm_galist_get(fv_pairs, i);
        if (pair->values.count == 0) {
            continue;
        }

        for (uint32 j = 0; j < pair->cols.count; j++) {
            expr_tree_t *col = (expr_tree_t *)cm_galist_get(&pair->cols, j);
            OG_RETURN_IFERR(sql_generate_dlvr_pairs(stmt, query, col, &pair->values, ff_pairs, dlvr_pairs));
        }
    }
    return OG_SUCCESS;
}

#ifdef __cplusplus
}
#endif