/* -------------------------------------------------------------------------
 *  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.
 * -------------------------------------------------------------------------
 *
 * ddl_partition_parser.c
 *
 *
 * IDENTIFICATION
 * src/ogsql/parser_ddl/ddl_partition_parser.c
 *
 * -------------------------------------------------------------------------
 */

#include "ddl_partition_parser.h"
#include "ddl_table_attr_parser.h"
#include "ddl_parser_common.h"
#include "ogsql_expr.h"
#include "expr_parser.h"
#include "ogsql_package.h"
#include "ogsql_verifier.h"
#include "cm_license.h"
#include "knl_heap.h"
#include "knl_dc.h"

static status_t sql_check_part_range_values(sql_stmt_t *stmt, word_t *word)
{
    word_t word_temp;
    status_t stat;
    lex_t *lex = stmt->session->lex;

    OG_RETURN_IFERR(lex_push(lex, &word->text));
    stat = lex_fetch(lex, &word_temp);
    if (stat != OG_SUCCESS) {
        lex_pop(lex);
        return stat;
    }

    if (word_temp.type == WORD_TYPE_OPERATOR && word_temp.id == OPER_TYPE_SUB) {
        stat = lex_fetch(lex, &word_temp);
        if (stat != OG_SUCCESS) {
            lex_pop(lex);
            return stat;
        }
    }

    while (word_temp.type != WORD_TYPE_EOF) {
        if (word_temp.type == WORD_TYPE_OPERATOR) {
            lex_pop(lex);
            OG_SRC_THROW_ERROR_EX(word_temp.text.loc, ERR_SQL_SYNTAX_ERROR,
                "can not specify an expression for a range partition boundval");
            return OG_ERROR;
        }
        stat = lex_fetch(lex, &word_temp);
        if (stat != OG_SUCCESS) {
            lex_pop(lex);
            return stat;
        }
    }

    lex_pop(lex);
    return OG_SUCCESS;
}

static status_t sql_range_verify_keys(sql_stmt_t *stmt, knl_part_obj_def_t *obj_def, knl_part_def_t *part_def,
    knl_part_def_t *parent_def)
{
    int32 cmp_result = 0;
    knl_part_def_t *prev_part = NULL;
    galist_t *tmp_parts = &obj_def->parts;
    galist_t *tmp_part_keys = &obj_def->part_keys;

    if (parent_def != NULL) {
        tmp_parts = &parent_def->subparts;
        tmp_part_keys = &obj_def->subpart_keys;
    }

    if (tmp_parts->count >= 2) {
        prev_part = cm_galist_get(tmp_parts, tmp_parts->count - 2);
        cmp_result = knl_compare_defined_key(tmp_part_keys, prev_part->partkey, part_def->partkey);
        if (cmp_result >= 0) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "partition %s boundary invalid", T2S(&part_def->name));
            return OG_ERROR;
        }
    }

    return OG_SUCCESS;
}

static void sql_init_verifier(sql_stmt_t *stmt, sql_verifier_t *verf)
{
    verf->context = stmt->context;
    verf->stmt = stmt;
    verf->excl_flags = SQL_EXCL_AGGR | SQL_EXCL_COLUMN | SQL_EXCL_STAR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
        SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_DEFAULT | SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;
}

static status_t sql_parse_range_values(sql_stmt_t *stmt, word_t *word, knl_part_def_t *part_def,
    knl_part_obj_def_t *obj_def, knl_part_def_t *parent_def)
{
    bool32 result = OG_FALSE;
    uint32 count = 0;
    variant_t value;
    expr_tree_t *value_expr = NULL;
    lex_t *lex = stmt->session->lex;
    sql_verifier_t verf = { 0 };
    knl_part_column_def_t *key = NULL;
    galist_t *tmp_part_keys = &obj_def->part_keys;

    sql_init_verifier(stmt, &verf);

    OG_RETURN_IFERR(sql_check_part_range_values(stmt, word));

    if (parent_def != NULL) {
        tmp_part_keys = &obj_def->subpart_keys;
    }

    part_key_init(part_def->partkey, tmp_part_keys->count);

    for (;;) {
        if (count >= tmp_part_keys->count) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "value count must equal to partition keys");
            return OG_ERROR;
        }

        OG_RETURN_IFERR(lex_try_fetch(lex, PART_VALUE_MAX, &result));
        if ((obj_def->is_interval) && (result) && parent_def == NULL) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
                "Maxvalue partition cannot be specified for interval partitioned");
            return OG_ERROR;
        }

        if (result) {
            OG_RETURN_IFERR(lex_fetch(lex, word));
            part_put_max(part_def->partkey);
        } else {
            key = cm_galist_get(tmp_part_keys, count);

            OG_RETURN_IFERR(sql_create_expr_until(stmt, &value_expr, word));

            OG_RETURN_IFERR(sql_verify_expr(&verf, value_expr));

            OG_RETURN_IFERR(sql_exec_expr(stmt, value_expr, &value));

            OG_RETURN_IFERR(sql_part_put_key(stmt, &value, key->datatype, key->size, key->is_char, key->precision,
                key->scale, part_def->partkey));
        }

        count++;

        OG_BREAK_IF_TRUE(word->type == WORD_TYPE_EOF);

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }

    if (count != tmp_part_keys->count) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "value count must equal to partition keys");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_put_list_key(sql_stmt_t *stmt, knl_part_def_t *part_def, knl_part_obj_def_t *obj_def,
    knl_part_def_t *parent_part_def)
{
    variant_t value;
    uint32 group_count;
    uint32 remainder;
    galist_t *value_list = &part_def->value_list;
    galist_t *tmp_part_keys = &obj_def->part_keys;

    part_key_init(part_def->partkey, value_list->count);

    if (parent_part_def != NULL) {
        tmp_part_keys = &obj_def->subpart_keys;
    }

    remainder = value_list->count % tmp_part_keys->count;
    group_count = value_list->count / tmp_part_keys->count;

    if (remainder != 0) {
        OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "remainder(%u) == 0", remainder);
        return OG_ERROR;
    }
    if (group_count > PART_MAX_LIST_COUNT) {
        OG_THROW_ERROR(ERR_PART_LIST_COUNT);
        return OG_ERROR;
    }

    for (uint32 i = 0; i < group_count; i++) {
        for (uint32 j = 0; j < tmp_part_keys->count; j++) {
            knl_part_column_def_t *key = cm_galist_get(tmp_part_keys, j);
            expr_tree_t *value_expr = cm_galist_get(value_list, i * tmp_part_keys->count + j);

            OG_RETURN_IFERR(sql_exec_expr(stmt, value_expr, &value));

            OG_RETURN_IFERR(sql_part_put_key(stmt, &value, key->datatype, key->size, key->is_char, key->precision,
                key->scale, part_def->partkey));
        }
    }
    return OG_SUCCESS;
}

static status_t sql_parse_list_default(lex_t *lex, knl_part_def_t *parent_part_def, knl_part_def_t *part_def,
    knl_part_obj_def_t *obj_def, bool32 *end)
{
    status_t status;
    bool32 result = OG_FALSE;
    bool32 *has_default = &obj_def->has_default;

    if (parent_part_def != NULL) {
        has_default = &obj_def->sub_has_default;
    }

    if (*has_default) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "default must be in last partition");
        return OG_ERROR;
    }

    status = lex_try_fetch(lex, PART_VALUE_DEFAULT, &result);
    OG_RETURN_IFERR(status);

    if (result) {
        status = lex_expected_end(lex);
        OG_RETURN_IFERR(status);

        *has_default = OG_TRUE;
        part_key_init(part_def->partkey, 1);
        part_put_default(part_def->partkey);
        *end = OG_TRUE;
        return OG_SUCCESS;
    }

    *end = OG_FALSE;
    return OG_SUCCESS;
}


static status_t sql_part_parse_type(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *part_def)
{
    lex_t *lex = stmt->session->lex;

    if (lex_expected_fetch_word(lex, "BY") != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (lex_expected_fetch(lex, word) != OG_SUCCESS) {
        return OG_ERROR;
    }

    switch (word->id) {
        case KEY_WORD_RANGE:
            if (part_def->is_composite) {
                part_def->subpart_type = PART_TYPE_RANGE;
            } else {
                part_def->part_type = PART_TYPE_RANGE;
            }
            break;

        case KEY_WORD_LIST:
            if (part_def->is_composite) {
                part_def->subpart_type = PART_TYPE_LIST;
            } else {
                part_def->part_type = PART_TYPE_LIST;
            }
            break;

        case KEY_WORD_HASH:
            if (part_def->is_composite) {
                part_def->subpart_type = PART_TYPE_HASH;
            } else {
                part_def->part_type = PART_TYPE_HASH;
            }
            break;

        default:
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "unexpected text %s", W2S(word));
            return OG_ERROR;
    }

    return OG_SUCCESS;
}

status_t sql_part_verify_key_type(typmode_t *typmod)
{
    if (typmod->is_array) {
        OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "invalid partition key type - got ARRAY");
        return OG_ERROR;
    }

    switch (typmod->datatype) {
        case OG_TYPE_UINT32:
        case OG_TYPE_UINT64:
        case OG_TYPE_INTEGER:
        case OG_TYPE_BIGINT:
        case OG_TYPE_REAL:
        case OG_TYPE_NUMBER:
        case OG_TYPE_NUMBER2:
        case OG_TYPE_NUMBER3:
        case OG_TYPE_DECIMAL:
        case OG_TYPE_DATE:
        case OG_TYPE_TIMESTAMP:
        case OG_TYPE_INTERVAL_DS:
        case OG_TYPE_INTERVAL_YM:
        case OG_TYPE_CHAR:
        case OG_TYPE_VARCHAR:
        case OG_TYPE_STRING:
        case OG_TYPE_BINARY:
        case OG_TYPE_RAW:
            return OG_SUCCESS;
        default:
            break;
    }

    if (OG_IS_LOB_TYPE(typmod->datatype)) {
        OG_THROW_ERROR(ERR_LOB_PART_COLUMN);
    } else {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid partition key type - got %s",
            get_datatype_name_str((int32)(typmod->datatype)));
    }

    return OG_ERROR;
}

static status_t sql_check_part_keys(word_t *word, knl_table_def_t *table_def, knl_part_column_def_t *part_column)
{
    knl_part_column_def_t *column_def = NULL;
    knl_part_obj_def_t *def = table_def->part_def;

    if (def->part_keys.count > OG_MAX_PARTKEY_COLUMNS) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "part key count %u more than max value %u",
            def->part_keys.count, OG_MAX_PARTKEY_COLUMNS);
        return OG_ERROR;
    }

    if (def->subpart_keys.count > OG_MAX_PARTKEY_COLUMNS) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", subpart key count %u more than max value %u",
            def->subpart_keys.count, OG_MAX_PARTKEY_COLUMNS);
        return OG_ERROR;
    }

    if (def->is_composite) {
        for (uint32 i = 0; i < def->subpart_keys.count - 1; i++) {
            column_def = (knl_part_column_def_t *)cm_galist_get(&def->subpart_keys, i);
            if (part_column->column_id == column_def->column_id) {
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "duplicate column name %s", W2S(word));
                return OG_ERROR;
            }
        }
    } else {
        for (uint32 i = 0; i < def->part_keys.count - 1; i++) {
            column_def = (knl_part_column_def_t *)cm_galist_get(&def->part_keys, i);
            if (part_column->column_id == column_def->column_id) {
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "duplicate column name %s", W2S(word));
                return OG_ERROR;
            }
        }
    }

    return OG_SUCCESS;
}

static status_t sql_part_parse_keys(sql_stmt_t *stmt, word_t *word, bool32 *expect_as, knl_table_def_t *table_def)
{
    status_t status;
    text_t col_name;
    lex_t *lex = stmt->session->lex;
    knl_part_column_def_t *part_col = NULL;
    knl_part_obj_def_t *def = table_def->part_def;

    // table column info is not complete, must be create table (...) as select or create table as select
    if (def->delay_partition == 0) {
        if (table_def->columns.count == 0) {
            def->save_key = word->text;
            def->delay_partition = OG_TRUE;
            *expect_as = OG_TRUE;

            return OG_SUCCESS;
        } else {
            for (uint32 i = 0; i < table_def->columns.count; i++) {
                knl_column_def_t *column_def = cm_galist_get(&table_def->columns, i);
                if (column_def->typmod.datatype == OG_TYPE_UNKNOWN) {
                    def->save_key = word->text;
                    *expect_as = OG_TRUE;
                    def->delay_partition = OG_TRUE;
                    return OG_SUCCESS;
                }
            }
        }
    }

    for (;;) {
        status = lex_expected_fetch_variant(lex, word);
        OG_RETURN_IFERR(status);

        if (def->is_composite) {
            status = cm_galist_new(&def->subpart_keys, sizeof(knl_part_column_def_t), (pointer_t *)&part_col);
        } else {
            status = cm_galist_new(&def->part_keys, sizeof(knl_part_column_def_t), (pointer_t *)&part_col);
        }
        OG_RETURN_IFERR(status);

        status = sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &col_name);
        OG_RETURN_IFERR(status);

        part_col->column_id = OG_INVALID_ID32;
        for (uint32 i = 0; i < table_def->columns.count; i++) {
            knl_column_def_t *column_def = cm_galist_get(&table_def->columns, i);

            if (cm_text_equal(&col_name, &column_def->name)) {
                part_col->column_id = i;

                status = sql_part_verify_key_type(&column_def->typmod);
                OG_RETURN_IFERR(status);
                part_col->datatype = column_def->datatype;
                if (column_def->typmod.size > OG_MAX_PART_COLUMN_SIZE) {
                    OG_THROW_ERROR(ERR_MAX_PART_CLOUMN_SIZE, T2S(&column_def->name), OG_MAX_PART_COLUMN_SIZE);
                    return OG_ERROR;
                }
                part_col->is_char = column_def->typmod.is_char;
                part_col->precision = column_def->typmod.precision;
                part_col->scale = column_def->typmod.scale;
                part_col->size = column_def->size;
                break;
            }
        }

        if (part_col->column_id == OG_INVALID_ID32) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition key %s can not find in table",
                W2S(word));
            return OG_ERROR;
        }

        if (sql_check_part_keys(word, table_def, part_col) != OG_SUCCESS) {
            return OG_ERROR;
        }

        status = lex_fetch(lex, word);
        OG_RETURN_IFERR(status);

        if (word->type == WORD_TYPE_EOF) {
            return OG_SUCCESS;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }
}

status_t sql_list_store_define_key(part_key_t *curr_key, knl_part_def_t *parent_part_def, knl_part_obj_def_t *obj_def,
    const text_t *part_name)
{
    int32 cmp_result;
    part_key_t *prev_key = NULL;
    galist_t *tmp_part_keys = &obj_def->part_keys;
    galist_t *temp_group_keys = &obj_def->group_keys;

    if (parent_part_def != NULL) {
        tmp_part_keys = &obj_def->subpart_keys;
        temp_group_keys = &parent_part_def->group_subkeys;
    }

    for (uint32 i = 0; i < temp_group_keys->count; i++) {
        prev_key = cm_galist_get(temp_group_keys, i);
        cmp_result = knl_compare_defined_key(tmp_part_keys, prev_key, curr_key);
        if (cmp_result == 0) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate value in partition %s", T2S(part_name));
            return OG_ERROR;
        }
    }

    return cm_galist_insert(temp_group_keys, curr_key);
}

static status_t sql_list_verify_multi_key(sql_stmt_t *stmt, galist_t *expr_list, knl_part_def_t *parent_part_def,
    knl_part_obj_def_t *obj_def, const text_t *part_name)
{
    variant_t value;
    part_key_t *curr_key = NULL;
    knl_part_column_def_t *def_key = NULL;
    galist_t *tmp_part_key = &obj_def->part_keys;

    if (parent_part_def != NULL) {
        tmp_part_key = &obj_def->subpart_keys;
    }

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&curr_key));
    if (expr_list->count < tmp_part_key->count) {
        OG_THROW_ERROR_EX(ERR_ASSERT_ERROR, "expr_list->count(%u) >= obj_def->part_keys.count(%u)", expr_list->count,
            tmp_part_key->count);
        return OG_ERROR;
    }
    part_key_init(curr_key, tmp_part_key->count);

    for (uint32 i = 0; i < tmp_part_key->count; i++) {
        expr_tree_t *expr = cm_galist_get(expr_list, expr_list->count - tmp_part_key->count + i);

        OG_RETURN_IFERR(sql_exec_expr(stmt, expr, &value));
        def_key = cm_galist_get(tmp_part_key, i);
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, def_key->datatype, def_key->size, def_key->is_char,
            def_key->precision, def_key->scale, curr_key));
    }

    return sql_list_store_define_key(curr_key, parent_part_def, obj_def, part_name);
}

static status_t sql_list_verify_one_key(sql_stmt_t *stmt, expr_tree_t *expr, knl_part_def_t *parent_part_def,
    knl_part_obj_def_t *obj_def, const text_t *part_name)
{
    variant_t value;
    part_key_t *curr_key = NULL;
    galist_t *tmp_part_keys = &obj_def->part_keys;

    if (parent_part_def != NULL) {
        tmp_part_keys = &obj_def->subpart_keys;
    }

    knl_part_column_def_t *def_key = cm_galist_get(tmp_part_keys, 0);
    OG_RETURN_IFERR(sql_exec_expr(stmt, expr, &value));
    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&curr_key));
    part_key_init(curr_key, 1);
    OG_RETURN_IFERR(sql_part_put_key(stmt, &value, def_key->datatype, def_key->size, def_key->is_char,
        def_key->precision, def_key->scale, curr_key));
    return sql_list_store_define_key(curr_key, parent_part_def, obj_def, part_name);
}

static status_t sql_part_parse_bracket_value(sql_stmt_t *stmt, word_t *word, knl_part_def_t *parent_part_def,
    knl_part_def_t *part_def, uint32 *count, knl_part_obj_def_t *obj_def, bool32 is_multi_key)
{
    status_t status;
    bool32 result = OG_FALSE;
    expr_tree_t *value_expr = NULL;
    lex_t *lex = stmt->session->lex;
    galist_t *value_list = &part_def->value_list;
    sql_verifier_t verf = { 0 };
    verf.context = stmt->context;
    verf.stmt = stmt;
    verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_COLUMN | SQL_EXCL_STAR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
        SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_DEFAULT | SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;

    *count = 0;
    for (;;) {
        status = lex_try_fetch(lex, PART_VALUE_DEFAULT, &result);
        OG_RETURN_IFERR(status);

        if (result) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but (%s) found", W2S(word));
            return OG_ERROR;
        }

        status = sql_create_expr_until(stmt, &value_expr, word);
        OG_RETURN_IFERR(status);

        status = sql_verify_expr(&verf, value_expr);
        OG_RETURN_IFERR(status);

        status = cm_galist_insert(value_list, value_expr);
        OG_RETURN_IFERR(status);
        (*count)++;
        if (!is_multi_key) {
            OG_RETURN_IFERR(sql_list_verify_one_key(stmt, value_expr, parent_part_def, obj_def, &part_def->name));
        }

        if (word->type == WORD_TYPE_EOF) {
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }
    return OG_SUCCESS;
}

static status_t sql_parse_list_values(sql_stmt_t *stmt, word_t *word, knl_part_def_t *parent_part_def,
    knl_part_def_t *part_def, knl_part_obj_def_t *obj_def)
{
    bool32 end = OG_FALSE;
    uint32 count;
    status_t status;
    lex_t *lex = stmt->session->lex;
    galist_t *tmp_part_keys = parent_part_def == NULL ? &obj_def->part_keys : &obj_def->subpart_keys;

    OG_RETURN_IFERR(sql_parse_list_default(lex, parent_part_def, part_def, obj_def, &end));
    OG_RETSUC_IFTRUE(end);

    if (tmp_part_keys->count == 1) {
        status = sql_part_parse_bracket_value(stmt, word, parent_part_def, part_def, &count, obj_def, OG_FALSE);
        OG_RETURN_IFERR(status);
    } else {
        for (;;) {
            status = lex_expected_fetch_bracket(lex, word);
            OG_RETURN_IFERR(status);

            OG_RETURN_IFERR(lex_push(lex, &word->text));

            if (sql_part_parse_bracket_value(stmt, word, parent_part_def, part_def, &count, obj_def, OG_TRUE) !=
                OG_SUCCESS) {
                lex_pop(lex);
                return OG_ERROR;
            }

            if (count != tmp_part_keys->count) {
                lex_pop(lex);
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "value count must equal to partition keys");
                return OG_ERROR;
            }
            if (sql_list_verify_multi_key(stmt, &part_def->value_list, parent_part_def, obj_def, &part_def->name) !=
                OG_SUCCESS) {
                lex_pop(lex);
                return OG_ERROR;
            }
            lex_pop(lex);

            status = lex_fetch(lex, word);
            OG_RETURN_IFERR(status);

            if (word->type == WORD_TYPE_EOF) {
                break;
            }

            if (!IS_SPEC_CHAR(word, ',')) {
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
                return OG_ERROR;
            }
        }
    }

    return sql_put_list_key(stmt, part_def, obj_def, parent_part_def);
}

static status_t sql_parse_range_partition(sql_stmt_t *stmt, word_t *word, knl_part_def_t *part_def,
    knl_part_obj_def_t *obj_def, knl_part_def_t *parent_def)
{
    bool32 result = OG_FALSE;
    lex_t *lex = stmt->session->lex;

    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "VALUES"));

    if (stmt->context->type == OGSQL_TYPE_ALTER_TABLE) {
        OG_RETURN_IFERR(lex_try_fetch(lex, "LESS", &result));
        if (!result) {
            OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_TYPE, "key", "not consistent with partition type");
            return OG_ERROR;
        }
    } else {
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "LESS"));
    }
    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "THAN"));
    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition boundary expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_copy_text(stmt->context, &word->text.value, &part_def->hiboundval));

    OG_RETURN_IFERR(lex_push(lex, &word->text));

    if (sql_parse_range_values(stmt, word, part_def, obj_def, parent_def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }
    if (sql_range_verify_keys(stmt, obj_def, part_def, parent_def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }

    lex_pop(lex);
    return OG_SUCCESS;
}

static status_t sql_parse_list_partition(sql_stmt_t *stmt, word_t *word, knl_part_def_t *part_def,
    knl_part_obj_def_t *obj_def, knl_part_def_t *parent_part_def)
{
    bool32 result = OG_FALSE;
    lex_t *lex = stmt->session->lex;

    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "VALUES"));

    if (stmt->context->type == OGSQL_TYPE_ALTER_TABLE) {
        OG_RETURN_IFERR(lex_try_fetch_bracket(lex, word, &result));
        if (!result) {
            OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_TYPE, "key", "not consistent with partition type");
            return OG_ERROR;
        }
    } else {
        OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    }

    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition boundary expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_copy_text(stmt->context, &word->text.value, &part_def->hiboundval));

    OG_RETURN_IFERR(lex_push(lex, &word->text));

    if (sql_parse_list_values(stmt, word, parent_part_def, part_def, obj_def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }

    lex_pop(lex);
    return OG_SUCCESS;
}

static bool32 sql_check_sys_interval_part(knl_part_obj_def_t *obj_def, text_t *part_name)
{
    if (obj_def->part_type == PART_TYPE_RANGE && obj_def->is_interval &&
        part_name->len >= NEW_PREFIX_SYS_PART_NAME_LEN &&
        strncmp(part_name->str, NEW_PREFIX_SYS_PART_NAME, NEW_PREFIX_SYS_PART_NAME_LEN) == 0) {
        return OG_TRUE;
    }

    return OG_FALSE;
}

status_t sql_parse_partition_attrs(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_part_def_t *def)
{
    status_t status;
    def->pctfree = OG_INVALID_ID32;
    def->is_csf = OG_INVALID_ID8;
    uint32 flags = lex->flags;
    lex->flags = LEX_SINGLE_WORD;
    uint8 algo = COMPRESS_NONE;
    uint8 type = COMPRESS_TYPE_NO;

    for (;;) {
        if (lex_fetch(lex, word) != OG_SUCCESS) {
            lex->flags = flags;
            return OG_ERROR;
        }
        if (word->type != WORD_TYPE_KEYWORD) {
            break;
        }
        switch (word->id) {
            case KEY_WORD_TABLESPACE:
                status = sql_parse_space(stmt, lex, word, &def->space);
                break;

            case KEY_WORD_INITRANS:
                status = sql_parse_trans(lex, word, &def->initrans);
                break;

            case KEY_WORD_PCTFREE:
                status = sql_parse_pctfree(lex, word, &def->pctfree);
                break;

            case KEY_WORD_STORAGE:
                status = sql_parse_storage(lex, word, &def->storage_def, OG_FALSE);
                break;

            case KEY_WORD_COMPRESS:
                if (!def->exist_subparts) {
                    status = sql_parse_table_compress(stmt, lex, &type, &algo);
                    def->compress_type = type;
                    def->compress_algo = algo;
                    break;
                }
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
                    "unexpected text %s, table part doesn't support compress if exists subpartitons", W2S(word));
                return OG_ERROR;

            case KEY_WORD_FORMAT:
                // don't support format clause
                if (!def->support_csf) {
                    OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "unexpected text format clause.");
                    lex->flags = flags;
                    return OG_ERROR;
                }

                status = sql_parse_row_format(lex, word, &def->is_csf);
                break;

            default:
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
                lex->flags = flags;
                return OG_ERROR;
        }

        if (status != OG_SUCCESS) {
            lex->flags = flags;
            return OG_ERROR;
        }
    }
    lex->flags = flags;
    return OG_SUCCESS;
}

status_t sql_parse_subpartition_attrs(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_part_def_t *part_def)
{
    status_t status;
    part_def->pctfree = OG_INVALID_ID32;
    uint32 flags = lex->flags;
    lex->flags = LEX_SINGLE_WORD;

    for (;;) {
        if (lex_fetch(lex, word) != OG_SUCCESS) {
            lex->flags = flags;
            return OG_ERROR;
        }

        if (word->type != WORD_TYPE_KEYWORD) {
            break;
        }

        switch (word->id) {
            case KEY_WORD_TABLESPACE:
                status = sql_parse_space(stmt, lex, word, &part_def->space);
                break;

            case KEY_WORD_INITRANS:
            case KEY_WORD_PCTFREE:
            case KEY_WORD_STORAGE:
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
                    "this physical attribute may not be specified for a table subpartition");
                return OG_ERROR;

            default:
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
                lex->flags = flags;
                return OG_ERROR;
        }

        if (status != OG_SUCCESS) {
            lex->flags = flags;
            return OG_ERROR;
        }
    }

    lex->flags = flags;
    return OG_SUCCESS;
}

static status_t sql_part_parse_partition_key(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *part_obj,
    knl_part_def_t *part_def, knl_part_def_t *parent_def)
{
    part_key_t *partkey = NULL;
    uint32 alloc_size = sizeof(part_key_t);
    status_t status;
    part_type_t part_type = part_obj->part_type;

    OG_RETURN_IFERR(sql_push(stmt, OG_MAX_COLUMN_SIZE, (void **)&partkey));
    MEMS_RETURN_IFERR(memset_s(partkey, OG_MAX_COLUMN_SIZE, 0x00, OG_MAX_COLUMN_SIZE));
    part_def->partkey = partkey;

    if (parent_def != NULL) {
        part_type = part_obj->subpart_type;
    }

    cm_galist_init(&part_def->value_list, stmt->context, sql_alloc_mem);
    switch (part_type) {
        case PART_TYPE_LIST:
            status = sql_parse_list_partition(stmt, word, part_def, part_obj, parent_def);
            break;

        case PART_TYPE_RANGE:
            status = sql_parse_range_partition(stmt, word, part_def, part_obj, parent_def);
            break;

        default:
            status = OG_SUCCESS;
            break;
    }

    if (status == OG_ERROR) {
        return OG_ERROR;
    }

    if (partkey->size > 0) {
        alloc_size = partkey->size;
    }

    if (sql_alloc_mem(stmt->context, alloc_size, (pointer_t *)&part_def->partkey) != OG_SUCCESS) {
        return OG_ERROR;
    }

    MEMS_RETURN_IFERR(memcpy_sp(part_def->partkey, alloc_size, partkey, alloc_size));

    return OG_SUCCESS;
}


static status_t sql_part_parse_partition(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *obj_def,
    knl_part_def_t *part_def)
{
    lex_t *lex = stmt->session->lex;
    uint32 flags;

    flags = lex->flags;
    lex->flags = LEX_SINGLE_WORD;
    if (lex_expected_fetch_variant(lex, word) != OG_SUCCESS) {
        cm_reset_error();
        OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_NAME);
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &part_def->name));
    lex->flags = flags;
    if (sql_check_sys_interval_part(obj_def, &part_def->name)) {
        OG_THROW_ERROR(ERR_OPERATIONS_NOT_ALLOW, "create table with interval part name _SYS_P");
        return OG_ERROR;
    }

    OGSQL_SAVE_STACK(stmt);
    if (sql_part_parse_partition_key(stmt, word, obj_def, part_def, NULL) != OG_SUCCESS) {
        OGSQL_RESTORE_STACK(stmt);
        return OG_ERROR;
    }
    OGSQL_RESTORE_STACK(stmt);

    part_def->support_csf = OG_TRUE;
    part_def->exist_subparts = obj_def->is_composite ? OG_TRUE : OG_FALSE;
    return sql_parse_partition_attrs(stmt, lex, word, part_def);
}

static bool32 sql_check_sys_interval_subpart(knl_part_obj_def_t *obj_def, text_t *part_name)
{
    if (obj_def->subpart_type == PART_TYPE_RANGE && obj_def->is_interval &&
        part_name->len >= PREFIX_SYS_SUBPART_NAME_LEN &&
        strncmp(part_name->str, PREFIX_SYS_SUBPART_NAME, PREFIX_SYS_SUBPART_NAME_LEN) == 0) {
        return OG_TRUE;
    }

    return OG_FALSE;
}

static status_t sql_part_parse_subpartition(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *obj_def,
    knl_part_def_t *parent_def)
{
    knl_part_def_t *subpart_def = NULL;
    lex_t *lex = stmt->session->lex;
    uint32 flags = lex->flags;
    lex->flags = LEX_SINGLE_WORD;

    if (lex_expected_fetch_variant(lex, word) != OG_SUCCESS) {
        OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_NAME);
        return OG_ERROR;
    }

    OG_RETURN_IFERR(cm_galist_new(&parent_def->subparts, sizeof(knl_part_def_t), (pointer_t *)&subpart_def));
    OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &subpart_def->name));
    lex->flags = flags;

    if (sql_check_sys_interval_subpart(obj_def, &subpart_def->name)) {
        OG_THROW_ERROR(ERR_OPERATIONS_NOT_ALLOW, "create table with interval subpart name _SYS_SUBP");
        return OG_ERROR;
    }

    OGSQL_SAVE_STACK(stmt);
    if (sql_part_parse_partition_key(stmt, word, obj_def, subpart_def, parent_def) != OG_SUCCESS) {
        OGSQL_RESTORE_STACK(stmt);
        return OG_ERROR;
    }
    OGSQL_RESTORE_STACK(stmt);

    return sql_parse_subpartition_attrs(stmt, lex, word, subpart_def);
}

static status_t sql_part_parse_subpartitions(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *obj_def,
    knl_part_def_t *parent_def)
{
    lex_t *lex = stmt->session->lex;
    lex->flags |= LEX_WITH_ARG;
    obj_def->sub_has_default = OG_FALSE;

    for (;;) {
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "SUBPARTITION"));
        OG_RETURN_IFERR(sql_part_parse_subpartition(stmt, word, obj_def, parent_def));

        if (word->type == WORD_TYPE_EOF) {
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }

    return OG_SUCCESS;
}

static status_t sql_part_generate_space_name(sql_stmt_t *stmt, knl_store_in_set_t *store_in, galist_t *part_list)
{
    knl_part_def_t *part_def = NULL;
    text_t *space_name = NULL;

    for (uint32 i = 0; i < store_in->part_cnt; i++) {
        part_def = (knl_part_def_t *)cm_galist_get(part_list, i);
        space_name = (text_t *)cm_galist_get(&store_in->space_list, i % store_in->space_cnt);
        OG_RETURN_IFERR(sql_copy_text(stmt->context, space_name, &part_def->space));
    }

    return OG_SUCCESS;
}

static status_t sql_part_new_part_def(sql_stmt_t *stmt, uint32 part_cnt, galist_t *part_list)
{
    knl_part_def_t *part_def = NULL;

    for (uint32 i = 0; i < part_cnt; i++) {
        OG_RETURN_IFERR(cm_galist_new(part_list, sizeof(knl_part_def_t), (pointer_t *)&part_def));
        OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&part_def->partkey));
        cm_galist_init(&part_def->value_list, stmt->context, sql_alloc_mem);
    }

    return OG_SUCCESS;
}

static status_t sql_part_parse_hash_attrs(sql_stmt_t *stmt, galist_t *part_list, uint32 part_cnt)
{
    knl_part_def_t *part_def = NULL;

    for (uint32 i = 0; i < part_cnt; i++) {
        OG_RETURN_IFERR(cm_galist_new(part_list, sizeof(knl_part_def_t), (pointer_t *)&part_def));
        OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(part_key_t), (pointer_t *)&part_def->partkey));
        cm_galist_init(&part_def->value_list, stmt->context, sql_alloc_mem);
        part_def->is_csf = OG_INVALID_ID8;
        part_def->pctfree = OG_INVALID_ID32;
        part_def->initrans = 0;
    }

    return OG_SUCCESS;
}

static status_t sql_generate_default_subpart(sql_stmt_t *stmt, knl_part_obj_def_t *def, galist_t *part_list)
{
    text_t text;
    knl_part_def_t *subpart_def = NULL;

    OG_RETURN_IFERR(cm_galist_new(part_list, sizeof(knl_part_def_t), (pointer_t *)&subpart_def));
    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&subpart_def->partkey));
    cm_galist_init(&subpart_def->value_list, stmt->context, sql_alloc_mem);
    if (def->subpart_type == PART_TYPE_LIST) {
        text.str = PART_VALUE_DEFAULT;
        text.len = (uint32)strlen(PART_VALUE_DEFAULT);
        OG_RETURN_IFERR(sql_copy_text(stmt->context, &text, &subpart_def->hiboundval));
        part_key_init(subpart_def->partkey, 1);
        part_put_default(subpart_def->partkey);
    }

    if (def->subpart_type == PART_TYPE_RANGE) {
        char *hiboundval_buf = NULL;
        uint32 hiboundval_len = OG_MAX_PARTKEY_COLUMNS * OG_MAX_HIBOUND_VALUE_LEN;
        OG_RETURN_IFERR(sql_push(stmt, hiboundval_len, (void **)&hiboundval_buf));
        MEMS_RETURN_IFERR(memset_sp(hiboundval_buf, hiboundval_len, 0, hiboundval_len));
        part_key_init(subpart_def->partkey, def->subpart_keys.count);
        for (uint32 i = 0; i < def->subpart_keys.count; i++) {
            MEMS_RETURN_IFERR(strcat_sp(hiboundval_buf, hiboundval_len, PART_VALUE_MAX));
            if (i != def->subpart_keys.count - 1) {
                MEMS_RETURN_IFERR(strcat_sp(hiboundval_buf, hiboundval_len, ", "));
            } else {
                MEMS_RETURN_IFERR(strcat_sp(hiboundval_buf, hiboundval_len, "\0"));
            }
            part_put_max(subpart_def->partkey);
        }
        text.str = hiboundval_buf;
        text.len = (uint32)strlen(hiboundval_buf);
        OG_RETURN_IFERR(sql_copy_text(stmt->context, &text, &subpart_def->hiboundval));
    }

    int64 part_name_id;
    text_t part_name;
    char name_arr[OG_NAME_BUFFER_SIZE] = { '\0' };

    OG_RETURN_IFERR(sql_alloc_object_id(stmt, &part_name_id));
    PRTS_RETURN_IFERR(snprintf_s(name_arr, OG_NAME_BUFFER_SIZE, OG_NAME_BUFFER_SIZE - 1, "SYS_SUBP%llX", part_name_id));
    part_name.len = (uint32)strlen(name_arr);
    part_name.str = name_arr;
    OG_RETURN_IFERR(sql_copy_object_name(stmt->context, WORD_TYPE_STRING, &part_name, &subpart_def->name));

    subpart_def->initrans = 0;
    subpart_def->is_csf = OG_INVALID_ID8;
    subpart_def->pctfree = OG_INVALID_ID32;

    return OG_SUCCESS;
}

static status_t sql_part_parse_store_in_space(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_store_in_set_t *store_in)
{
    text_t *space = NULL;
    status_t status;

    lex->flags = LEX_SINGLE_WORD;

    OG_RETURN_IFERR(lex_push(lex, &word->text));

    for (;;) {
        status = lex_expected_fetch_variant(lex, word);
        OG_BREAK_IF_ERROR(status);

        status = cm_galist_new(&store_in->space_list, sizeof(text_t), (pointer_t *)&space);
        OG_BREAK_IF_ERROR(status);

        status = sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, space);
        OG_BREAK_IF_ERROR(status);

        store_in->space_cnt++;

        status = lex_fetch(lex, word);
        OG_BREAK_IF_ERROR(status);

        if (word->type == WORD_TYPE_EOF) {
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            lex_pop(lex);
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }

    lex_pop(lex);
    return status;
}

static status_t sql_part_parse_store_in_clause(sql_stmt_t *stmt, word_t *word, knl_store_in_set_t *store_in)
{
    bool32 result = OG_FALSE;
    lex_t *lex = stmt->session->lex;

    if (lex_try_fetch(lex, "STORE", &result) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (!result) {
        return OG_SUCCESS;
    }

    if (lex_expected_fetch_word(lex, "IN") != OG_SUCCESS) {
        return OG_ERROR;
    }

    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "tablespace expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_part_parse_store_in_space(stmt, lex, word, store_in));
    return OG_SUCCESS;
}

static status_t sql_part_parse_partcnt(sql_stmt_t *stmt, knl_store_in_set_t *store_in)
{
    lex_t *lex = stmt->session->lex;

    if (lex_expected_fetch_uint32(lex, &store_in->part_cnt) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (store_in->part_cnt == 0) {
        OG_SRC_THROW_ERROR_EX(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "invalid partition number");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_parse_subpartitions_for_compart(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_part_obj_def_t *def,
    knl_part_def_t *part_def)
{
    knl_store_in_set_t store_in;

    cm_galist_init(&part_def->subparts, stmt->context, sql_alloc_mem);
    cm_galist_init(&part_def->group_subkeys, stmt->context, sql_alloc_mem);
    if (word->type == WORD_TYPE_VARIANT && cm_compare_text_str_ins(&word->text.value, "SUBPARTITIONS") == 0) {
        store_in.is_store_in = OG_TRUE;
        store_in.space_cnt = 0;
        cm_galist_init(&store_in.space_list, stmt->context, sql_alloc_mem);
        OG_RETURN_IFERR(sql_part_parse_partcnt(stmt, &store_in));
        OG_RETURN_IFERR(sql_part_parse_store_in_clause(stmt, word, &store_in));
        OG_RETURN_IFERR(sql_part_new_part_def(stmt, store_in.part_cnt, &part_def->subparts));
        if (store_in.space_cnt > 0) {
            OG_RETURN_IFERR(sql_part_generate_space_name(stmt, &store_in, &part_def->subparts));
        }
        OG_RETURN_IFERR(lex_fetch(lex, word));
    } else if (def->subpart_store_in.is_store_in) {
        OG_RETURN_IFERR(sql_part_new_part_def(stmt, def->subpart_store_in.part_cnt, &part_def->subparts));
        if (def->subpart_store_in.space_cnt > 0) {
            OG_RETURN_IFERR(sql_part_generate_space_name(stmt, &def->subpart_store_in, &part_def->subparts));
        }
    } else if (word->type == WORD_TYPE_BRACKET) {
        OG_RETURN_IFERR(lex_push(lex, &word->text));
        OG_RETURN_IFERR(sql_part_parse_subpartitions(stmt, word, def, part_def));
        lex_pop(lex);
        OG_RETURN_IFERR(lex_fetch(lex, word));
    } else {
        OG_RETURN_IFERR(sql_generate_default_subpart(stmt, def, &part_def->subparts));
    }

    part_def->is_parent = OG_TRUE;

    return OG_SUCCESS;
}

static status_t sql_part_parse_partitions(sql_stmt_t *stmt, word_t *word, knl_table_def_t *table_def)
{
    status_t status;
    knl_part_def_t *part_def = NULL;
    lex_t *lex = stmt->session->lex;
    uint32 flags = lex->flags;
    knl_part_obj_def_t *def = table_def->part_def;

    lex->flags |= LEX_WITH_ARG;
    for (;;) {
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "PARTITION"));
        OG_RETURN_IFERR(cm_galist_new(&def->parts, sizeof(knl_part_def_t), (pointer_t *)&part_def));
        OG_RETURN_IFERR(sql_part_parse_partition(stmt, word, def, part_def));

        if (def->is_composite) {
            OG_RETURN_IFERR(sql_parse_subpartitions_for_compart(stmt, lex, word, def, part_def));
        }

        if (word->type == WORD_TYPE_EOF) {
            status = OG_SUCCESS;
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            status = OG_ERROR;
            break;
        }
    }

    lex->flags = flags;
    return status;
}

static status_t sql_part_parse_interval_space(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_part_obj_def_t *obj_def)
{
    text_t *interval_space = NULL;
    status_t status;

    lex->flags = LEX_SINGLE_WORD;
    OG_RETURN_IFERR(lex_push(lex, &word->text));

    for (;;) {
        status = lex_expected_fetch_word(lex, "TABLESPACE");
        OG_BREAK_IF_ERROR(status);
        status = lex_expected_fetch_variant(lex, word);
        OG_BREAK_IF_ERROR(status);
        status = cm_galist_new(&obj_def->part_store_in.space_list, sizeof(text_t), (pointer_t *)&interval_space);
        OG_BREAK_IF_ERROR(status);
        obj_def->part_store_in.space_cnt++;

        status = sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, interval_space);
        OG_BREAK_IF_ERROR(status);
        status = lex_fetch(lex, word);
        OG_BREAK_IF_ERROR(status);

        if (word->type == WORD_TYPE_EOF) {
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            lex_pop(lex);
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }
    lex_pop(lex);
    return status;
}

static status_t sql_parse_interval_key(sql_stmt_t *stmt, word_t *word, part_key_t *interval_key,
    knl_part_obj_def_t *obj_def)
{
    expr_tree_t *value_expr = NULL;
    sql_verifier_t verf = { 0 };
    variant_t value;
    knl_part_column_def_t *key = NULL;
    lex_t *lex = stmt->session->lex;

    if (obj_def->part_keys.count > 1) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "only support one interval partition column");
        return OG_ERROR;
    }

    key = cm_galist_get(&obj_def->part_keys, 0);
    if (!OG_IS_NUMERIC_TYPE(key->datatype) && !OG_IS_DATETIME_TYPE(key->datatype)) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid interval partition column data type");
        return OG_ERROR;
    }

    part_key_init(interval_key, 1);

    verf.context = stmt->context;
    verf.stmt = stmt;
    verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_COLUMN | SQL_EXCL_STAR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
        SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_DEFAULT | SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;

    lex->flags |= LEX_WITH_ARG;

    OG_RETURN_IFERR(sql_create_expr_until(stmt, &value_expr, word));

    OG_RETURN_IFERR(sql_verify_expr(&verf, value_expr));

    OG_RETURN_IFERR(sql_exec_expr(stmt, value_expr, &value));

    if (value.is_null) {
        OG_SRC_THROW_ERROR(value_expr->loc, ERR_OPERATIONS_NOT_ALLOW, "set inerval to null");
        return OG_ERROR;
    }

    if (OG_IS_NUMERIC_TYPE(key->datatype)) {
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, key->datatype, key->size, key->is_char, key->precision,
            key->scale, interval_key));
        if (var_as_decimal(&value) != OG_SUCCESS || IS_DEC8_NEG(&value.v_dec)) {
            OG_SRC_THROW_ERROR(value_expr->loc, ERR_INVALID_PART_TYPE, "interval key data", "");
            return OG_ERROR;
        }
    } else {
        if (!OG_IS_DSITVL_TYPE(value.type) && !OG_IS_YMITVL_TYPE(value.type)) {
            OG_SRC_THROW_ERROR(value_expr->loc, ERR_INVALID_PART_TYPE, "interval key data", "");
            return OG_ERROR;
        }
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, value.type, key->size, key->is_char, key->precision, key->scale,
            interval_key));
    }
    return lex_expected_end(lex);
}

static status_t sql_parse_interval(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *obj_def)
{
    bool32 result = OG_FALSE;
    lex_t *lex = stmt->session->lex;
    part_key_t *interval_key = NULL;

    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "interval key expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&interval_key));
    if (obj_def->delay_partition == OG_TRUE) {
        obj_def->save_interval_part = word->text;
    } else {
        OG_RETURN_IFERR(lex_push(lex, &word->text));
        if (sql_copy_text(stmt->context, &word->text.value, &obj_def->interval) != OG_SUCCESS) {
            lex_pop(lex);
            return OG_ERROR;
        }

        if (sql_parse_interval_key(stmt, word, interval_key, obj_def) != OG_SUCCESS) {
            lex_pop(lex);
            return OG_ERROR;
        }
        lex_pop(lex);
    }
    obj_def->binterval.bytes = (uint8 *)interval_key;
    obj_def->binterval.size = interval_key->size;
    OG_RETURN_IFERR(lex_try_fetch(lex, "STORE", &result));

    if (result) {
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "IN"));

        OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
        if (word->text.len == 0) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "tablespace expected");
            return OG_ERROR;
        }
        OG_RETURN_IFERR(sql_part_parse_interval_space(stmt, lex, word, obj_def));
    }
    return OG_SUCCESS;
}

static status_t sql_try_parse_interval(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *obj_def)
{
    lex_t *lex = stmt->session->lex;
    uint32 flags = lex->flags;

    OG_RETURN_IFERR(lex_try_fetch(lex, "INTERVAL", &obj_def->is_interval));
    if (!obj_def->is_interval) {
        return OG_SUCCESS;
    }
    OG_RETURN_IFERR(sql_parse_interval(stmt, word, obj_def));
    lex->flags = flags;
    return OG_SUCCESS;
}

static status_t sql_part_parse_store_in(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *def)
{
    lex_t *lex = stmt->session->lex;

    if (def->part_type != PART_TYPE_HASH) {
        return OG_SUCCESS;
    }

    OG_RETURN_IFERR(lex_try_fetch(lex, "partitions", &def->part_store_in.is_store_in));
    if (def->part_store_in.is_store_in) {
        OG_RETURN_IFERR(sql_part_parse_partcnt(stmt, &def->part_store_in));
        OG_RETURN_IFERR(sql_part_parse_hash_attrs(stmt, &def->parts, def->part_store_in.part_cnt));
        OG_RETURN_IFERR(sql_part_parse_store_in_clause(stmt, word, &def->part_store_in));
        if (def->part_store_in.space_cnt > 0) {
            OG_RETURN_IFERR(sql_part_generate_space_name(stmt, &def->part_store_in, &def->parts));
        }
    }

    return OG_SUCCESS;
}

static status_t sql_subpart_parse_store_in(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *def)
{
    lex_t *lex = stmt->session->lex;

    if (!def->is_composite) {
        return OG_SUCCESS;
    }

    if (def->subpart_type != PART_TYPE_HASH) {
        return OG_SUCCESS;
    }

    OG_RETURN_IFERR(lex_try_fetch(lex, "subpartitions", &def->subpart_store_in.is_store_in));
    if (def->subpart_store_in.is_store_in) {
        OG_RETURN_IFERR(sql_part_parse_partcnt(stmt, &def->subpart_store_in));
        OG_RETURN_IFERR(sql_part_parse_store_in_clause(stmt, word, &def->subpart_store_in));
    }

    return OG_SUCCESS;
}

static status_t sql_generate_subpart_for_storein(sql_stmt_t *stmt, word_t *word, knl_part_obj_def_t *def)
{
    knl_part_def_t *part_def = NULL;

    if (!def->is_composite) {
        return OG_SUCCESS;
    }

    for (uint32 i = 0; i < def->parts.count; i++) {
        part_def = (knl_part_def_t *)cm_galist_get(&def->parts, i);
        part_def->is_parent = OG_TRUE;
        cm_galist_init(&part_def->subparts, stmt->context, sql_alloc_mem);
        if (def->subpart_store_in.is_store_in) { // subpart is also defined in "store in"
            knl_part_def_t *subpart_def = NULL;
            for (uint32 j = 0; j < def->subpart_store_in.part_cnt; j++) {
                OG_RETURN_IFERR(cm_galist_new(&part_def->subparts, sizeof(knl_part_def_t), (pointer_t *)&subpart_def));
                OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(part_key_t), (pointer_t *)&subpart_def->partkey));
                subpart_def->initrans = 0;
                subpart_def->is_parent = OG_FALSE;
                subpart_def->is_csf = OG_INVALID_ID8;
                subpart_def->pctfree = OG_INVALID_ID32;
            }
        } else { // it's not specify subpartition desc
            OG_RETURN_IFERR(sql_generate_default_subpart(stmt, def, &part_def->subparts));
        }
    }

    lex_t *lex = stmt->session->lex;
    if (lex_expected_end(lex) != OG_SUCCESS) {
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_parse_part_type_keys(sql_stmt_t *stmt, word_t *word, bool32 *expect_as, knl_table_def_t *table_def)
{
    lex_t *lex = stmt->session->lex;
    knl_part_obj_def_t *def = table_def->part_def;
    status_t status;

    OG_RETURN_IFERR(sql_part_parse_type(stmt, word, def));
    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition key expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(lex_push(lex, &word->text));
    status = sql_part_parse_keys(stmt, word, expect_as, table_def);
    lex_pop(lex);
    OG_RETURN_IFERR(status);

    if (def->part_type == PART_TYPE_RANGE) {
        OG_RETURN_IFERR(sql_try_parse_interval(stmt, word, def));
    }

    OG_RETURN_IFERR(lex_try_fetch(lex, "SUBPARTITION", &def->is_composite));
    if (def->is_composite) { // parse subpart keys in case of subpartition
        OG_RETURN_IFERR(sql_part_parse_type(stmt, word, def));
        OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
        if (word->text.len == 0) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "subpartition key expected");
            return OG_ERROR;
        }

        if (def->delay_partition) {
            def->save_subkey = word->text;
        } else {
            OG_RETURN_IFERR(lex_push(lex, &word->text));
            status = sql_part_parse_keys(stmt, word, expect_as, table_def);
            lex_pop(lex);
            OG_RETURN_IFERR(status);
        }
    }

    return OG_SUCCESS;
}

status_t sql_part_parse_table(sql_stmt_t *stmt, word_t *word, bool32 *expect_as, knl_table_def_t *table_def)
{
    knl_part_obj_def_t *def = NULL;
    lex_t *lex = stmt->session->lex;
    status_t status;

    if (cm_lic_check(LICENSE_PARTITION) != OG_SUCCESS) {
        OG_THROW_ERROR(ERR_LICENSE_CHECK_FAIL, " effective partition function license is required.");
        return OG_ERROR;
    }

    if (table_def->part_def != NULL) {
        OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR, "duplicate partition definition");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(knl_part_obj_def_t), (pointer_t *)&table_def->part_def));
    def = table_def->part_def;
    table_def->parted = OG_TRUE;

    cm_galist_init(&def->parts, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->group_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_store_in.space_list, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->subpart_store_in.space_list, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->subpart_keys, stmt->context, sql_alloc_mem);

    OG_RETURN_IFERR(sql_parse_part_type_keys(stmt, word, expect_as, table_def));
    OG_RETURN_IFERR(sql_part_parse_store_in(stmt, word, def));
    OG_RETURN_IFERR(sql_subpart_parse_store_in(stmt, word, def));
    if (def->part_store_in.is_store_in) {
        return sql_generate_subpart_for_storein(stmt, word, def);
    }

    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partitions expected");
        return OG_ERROR;
    }
    if (def->delay_partition == OG_TRUE) {
        def->save_part = word->text;
    } else {
        OG_RETURN_IFERR(lex_push(lex, &word->text));
        status = sql_part_parse_partitions(stmt, word, table_def);
        lex_pop(lex);
        OG_RETURN_IFERR(status);
    }

    return OG_SUCCESS;
}


static status_t sql_parse_split_part_def(sql_stmt_t *stmt, word_t *word, knl_altable_def_t *def)
{
    knl_part_def_t *part_def = NULL;
    lex_t *lex = stmt->session->lex;
    uint32 flag = lex->flags;
    lex->flags = LEX_SINGLE_WORD;
    for (uint32 i = 0;; i++) {
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "PARTITION"));
        if (lex_expected_fetch_variant(lex, word) != OG_SUCCESS) {
            cm_reset_error();
            OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_NAME);
            return OG_ERROR;
        }

        part_def = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, i);
        OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &part_def->name));

        part_def->support_csf = OG_FALSE;
        part_def->exist_subparts = def->part_def.obj_def->is_composite ? OG_TRUE : OG_FALSE;
        OG_RETURN_IFERR(sql_parse_partition_attrs(stmt, lex, word, part_def));

        if (i == 1) {
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }

    if (word->type != WORD_TYPE_EOF) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition define error");
        return OG_ERROR;
    }
    lex->flags = flag;
    return OG_SUCCESS;
}


static status_t sql_parse_split_parts_def(sql_stmt_t *stmt, word_t *word, knl_altable_def_t *def)
{
    lex_t *lex = stmt->session->lex;
    knl_part_def_t *first_part = NULL;
    knl_part_def_t *second_part = NULL;

    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition define expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(lex_push(lex, &word->text));
    if (sql_parse_split_part_def(stmt, word, def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }
    lex_pop(lex);

    first_part = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, (uint32)0);
    second_part = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, (uint32)1);
    if ((cm_compare_text(&def->part_def.name, &first_part->name) == 0) ||
        (cm_compare_text(&first_part->name, &second_part->name) == 0)) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "partition name duplicate");
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

static status_t sql_parse_update_index_clause(sql_stmt_t *stmt, word_t *word, knl_altable_def_t *def)
{
    lex_t *lex = stmt->session->lex;
    OG_RETURN_IFERR(lex_fetch(lex, word));

    if (word->type == WORD_TYPE_EOF) {
        def->part_def.global_index_option = OG_FALSE;
        return OG_SUCCESS;
    }

    if (word->id == KEY_WORD_UPDATE) {
        def->part_def.global_index_option = OG_TRUE;
    } else if (word->id == KEY_WORD_INVALIDATE) {
        def->part_def.global_index_option = OG_FALSE;
    } else {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "key word expected but %s found", W2S(word));
        return OG_ERROR;
    }

    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "GLOBAL"));
    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "INDEXES"));

    return lex_expected_end(lex);
}

static status_t sql_parse_first_part_hiboundval(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def, bool32 is_subpart)
{
    word_t word;
    galist_t *part_list = NULL;
    knl_part_def_t *first_part = NULL;
    knl_part_def_t *parent_part = NULL;

    lex->flags |= LEX_WITH_ARG;
    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, &word));
    if (word.text.len == 0) {
        OG_SRC_THROW_ERROR_EX(word.text.loc, ERR_SQL_SYNTAX_ERROR, "split value of partition expected");
        return OG_ERROR;
    }

    if (is_subpart) {
        parent_part = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, 0);
        part_list = &parent_part->subparts;
    } else {
        part_list = &def->part_def.obj_def->parts;
    }

    OG_RETURN_IFERR(cm_galist_new(part_list, sizeof(knl_part_def_t), (pointer_t *)&first_part));
    OG_RETURN_IFERR(sql_copy_text(stmt->context, &word.text.value, &first_part->hiboundval));

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&first_part->partkey));

    OG_RETURN_IFERR(lex_push(lex, &word.text));
    if (sql_parse_range_values(stmt, &word, first_part, def->part_def.obj_def, parent_part) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }
    lex_pop(lex);

    return OG_SUCCESS;
}

static status_t sql_parse_second_part_hiboundval(sql_stmt_t *stmt, knl_dictionary_t *dc, knl_altable_def_t *def,
    bool32 is_subpart)
{
    uint32 part_no;
    uint32 subpart_no;
    galist_t *part_list = NULL;
    knl_part_def_t *secend_part = NULL;
    knl_part_def_t *parent_part = NULL;

    if (is_subpart) {
        parent_part = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, 0);
        part_list = &parent_part->subparts;
    } else {
        part_list = &def->part_def.obj_def->parts;
    }

    OG_RETURN_IFERR(cm_galist_new(part_list, sizeof(knl_part_def_t), (pointer_t *)&secend_part));
    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&secend_part->partkey));

    dc_entity_t *entity = DC_ENTITY(dc);

    if (is_subpart) {
        OG_RETURN_IFERR(knl_find_subpart_by_name(entity, &def->part_def.name, &part_no, &subpart_no));
        table_part_t *table_part = TABLE_GET_PART(&entity->table, part_no);
        table_part_t *table_subpart = PART_GET_SUBENTITY(entity->table.part_table, table_part->subparts[subpart_no]);
        OG_RETURN_IFERR(sql_copy_text(stmt->context, &table_subpart->desc.hiboundval, &secend_part->hiboundval));

        MEMS_RETURN_IFERR(memcpy_s(secend_part->partkey, OG_MAX_COLUMN_SIZE,
            (part_key_t *)table_subpart->desc.bhiboundval.bytes, table_subpart->desc.bhiboundval.size));
    } else {
        OG_RETURN_IFERR(knl_find_table_part_by_name(entity, &def->part_def.name, &part_no));
        table_part_t *table_part = TABLE_GET_PART(&entity->table, part_no);
        OG_RETURN_IFERR(sql_copy_text(stmt->context, &table_part->desc.hiboundval, &secend_part->hiboundval));

        MEMS_RETURN_IFERR(memcpy_s(secend_part->partkey, OG_MAX_COLUMN_SIZE,
            (part_key_t *)table_part->desc.bhiboundval.bytes, table_part->desc.bhiboundval.size));
    }

    if (sql_range_verify_keys(stmt, def->part_def.obj_def, secend_part, parent_part) != OG_SUCCESS) {
        cm_reset_error();
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "split partition value invalid");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_get_tab_part_key(sql_stmt_t *stmt, knl_dictionary_t *dc, knl_altable_def_t *def)
{
    knl_column_t *knl_column = NULL;
    knl_part_column_def_t *part_col = NULL;

    for (uint16 i = 0; i < knl_part_key_count(dc->handle); i++) {
        if (cm_galist_new(&def->part_def.obj_def->part_keys, sizeof(knl_part_column_def_t),
            (pointer_t *)&part_col) != OG_SUCCESS) {
            return OG_ERROR;
        }
        part_col->column_id = knl_part_key_column_id(dc->handle, i);
        knl_column = knl_get_column(dc->handle, part_col->column_id);
        part_col->datatype = knl_column->datatype;
        part_col->size = knl_column->size;
        part_col->precision = knl_column->precision;
        part_col->scale = knl_column->scale;
        part_col->is_char = KNL_COLUMN_IS_CHARACTER(knl_column);
    }

    return OG_SUCCESS;
}

static status_t sql_get_tab_subpart_key(sql_stmt_t *stmt, knl_dictionary_t *dc, knl_altable_def_t *def)
{
    knl_column_t *knl_column = NULL;
    knl_part_column_def_t *part_col = NULL;

    for (uint16 i = 0; i < knl_subpart_key_count(dc->handle); i++) {
        if (cm_galist_new(&def->part_def.obj_def->subpart_keys, sizeof(knl_part_column_def_t),
            (pointer_t *)&part_col) != OG_SUCCESS) {
            return OG_ERROR;
        }
        part_col->column_id = knl_subpart_key_column_id(dc->handle, i);
        knl_column = knl_get_column(dc->handle, part_col->column_id);
        part_col->datatype = knl_column->datatype;
        part_col->size = knl_column->size;
        part_col->precision = knl_column->precision;
        part_col->scale = knl_column->scale;
        part_col->is_char = KNL_COLUMN_IS_CHARACTER(knl_column);
    }

    return OG_SUCCESS;
}

static status_t sql_parse_split_subpart_def(sql_stmt_t *stmt, word_t *word, knl_altable_def_t *def)
{
    knl_part_def_t *part_def = NULL;
    lex_t *lex = stmt->session->lex;
    uint32 flag = lex->flags;
    lex->flags = LEX_SINGLE_WORD;

    knl_part_def_t *parent_part = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, 0);
    for (uint32 i = 0; i < OG_SPLIT_PART_COUNT; i++) {
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "SUBPARTITION"));
        if (lex_expected_fetch_variant(lex, word) != OG_SUCCESS) {
            cm_reset_error();
            OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_NAME);
            return OG_ERROR;
        }

        part_def = (knl_part_def_t *)cm_galist_get(&parent_part->subparts, i);
        OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &part_def->name));
        OG_RETURN_IFERR(sql_parse_subpartition_attrs(stmt, lex, word, part_def));

        if (i == OG_SPLIT_PART_COUNT - 1) {
            break;
        }

        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }

    if (word->type != WORD_TYPE_EOF) {
        OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "partition define error");
        return OG_ERROR;
    }

    lex->flags = flag;
    return OG_SUCCESS;
}

static status_t sql_parse_split_subpart_defs(sql_stmt_t *stmt, word_t *word, knl_altable_def_t *def)
{
    lex_t *lex = stmt->session->lex;
    knl_part_def_t *first_part = NULL;
    knl_part_def_t *second_part = NULL;

    OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, word));
    if (word->text.len == 0) {
        OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR, "subpartition define expected");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(lex_push(lex, &word->text));
    if (sql_parse_split_subpart_def(stmt, word, def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }
    lex_pop(lex);

    knl_part_def_t *parent_part = (knl_part_def_t *)cm_galist_get(&def->part_def.obj_def->parts, 0);
    first_part = (knl_part_def_t *)cm_galist_get(&parent_part->subparts, 0);
    second_part = (knl_part_def_t *)cm_galist_get(&parent_part->subparts, 1);
    if ((cm_compare_text(&def->part_def.name, &first_part->name) == 0) ||
        (cm_compare_text(&first_part->name, &second_part->name) == 0)) {
        OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "subpartition name duplicate");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_parse_split_subpartition_clause(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def,
    knl_dictionary_t *dc)
{
    word_t word;
    OG_RETURN_IFERR(sql_get_tab_subpart_key(stmt, dc, def));
    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "AT"));
    OG_RETURN_IFERR(sql_parse_first_part_hiboundval(stmt, lex, def, OG_TRUE));
    OG_RETURN_IFERR(sql_parse_second_part_hiboundval(stmt, dc, def, OG_TRUE));
    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "INTO"));
    OG_RETURN_IFERR(sql_parse_split_subpart_defs(stmt, &word, def));
    return sql_parse_update_index_clause(stmt, &word, def);
}

static status_t sql_parse_split_subpartition(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def)
{
    word_t word;
    knl_dictionary_t dc;
    knl_session_t *session = &stmt->session->knl_session;

    def->action = ALTABLE_SPLIT_SUBPARTITION;
    if (lex_expected_fetch_variant(lex, &word) != OG_SUCCESS) {
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &def->part_def.name));
    if (knl_open_dc(session, (text_t *)&def->user, (text_t *)&def->name, &dc) != OG_SUCCESS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not found", T2S(&def->name));
        return OG_ERROR;
    }

    if (!knl_is_part_table(dc.handle) || !knl_is_compart_table(dc.handle)) {
        knl_close_dc(&dc);
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s is not composite partition table", T2S(&def->name));
        return OG_ERROR;
    }

    def->part_def.obj_def->part_type = knl_part_table_type(dc.handle);
    def->part_def.obj_def->subpart_type = knl_subpart_table_type(dc.handle);
    if (def->part_def.obj_def->subpart_type != PART_TYPE_RANGE) {
        OG_THROW_ERROR(ERR_OPERATIONS_NOT_SUPPORT, "split subpartition", "non-range subpartitioned table");
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    knl_part_def_t *virtual_part = NULL;
    cm_galist_init(&def->part_def.obj_def->subpart_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_def.obj_def->parts, stmt->context, sql_alloc_mem);
    if (cm_galist_new(&def->part_def.obj_def->parts, sizeof(knl_part_def_t), (pointer_t *)&virtual_part) !=
        OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    cm_galist_init(&virtual_part->subparts, stmt->context, sql_alloc_mem);
    if (sql_parse_split_subpartition_clause(stmt, lex, def, &dc) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    knl_close_dc(&dc);
    return OG_SUCCESS;
}

static status_t sql_split_check_part_type(sql_stmt_t *stmt, knl_dictionary_t *dc, knl_altable_def_t *def)
{
    if (!knl_is_part_table(dc->handle)) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not partition table", T2S(&def->name));
        return OG_ERROR;
    }

    def->part_def.obj_def->part_type = knl_part_table_type(dc->handle);

    table_t *table = DC_TABLE(dc);

    if (def->part_def.obj_def->part_type != PART_TYPE_RANGE || table->part_table->desc.interval_key != NULL) {
        OG_THROW_ERROR(ERR_OPERATIONS_NOT_SUPPORT, "split partition", "non-range partitioned table");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

status_t sql_parse_split_partition(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def)
{
    uint32 match_id;
    word_t word;
    knl_handle_t knl = &stmt->session->knl_session;
    knl_dictionary_t dc;
    status_t status = OG_SUCCESS;

    if (sql_alloc_mem(stmt->context, sizeof(knl_part_obj_def_t), (pointer_t *)&def->part_def.obj_def) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (lex_expected_fetch_1of2(lex, "SUBPARTITION", "PARTITION", &match_id) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (match_id == 0) {
        return sql_parse_split_subpartition(stmt, lex, def);
    }

    def->action = ALTABLE_SPLIT_PARTITION;
    if (lex_expected_fetch_variant(lex, &word) != OG_SUCCESS) {
        return OG_ERROR;
    }
    OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &def->part_def.name));

    if (knl_open_dc(knl, (text_t *)&def->user, (text_t *)&def->name, &dc) != OG_SUCCESS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not find", T2S(&def->name));
        return OG_ERROR;
    }

    if (sql_split_check_part_type(stmt, &dc, def) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    cm_galist_init(&def->part_def.obj_def->part_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_def.obj_def->parts, stmt->context, sql_alloc_mem);

    do {
        status = sql_get_tab_part_key(stmt, &dc, def);
        OG_BREAK_IF_ERROR(status);

        status = lex_expected_fetch_word(lex, "AT");
        OG_BREAK_IF_ERROR(status);

        status = sql_parse_first_part_hiboundval(stmt, lex, def, OG_FALSE);
        OG_BREAK_IF_ERROR(status);

        status = sql_parse_second_part_hiboundval(stmt, &dc, def, OG_FALSE);
        OG_BREAK_IF_ERROR(status);

        status = lex_expected_fetch_word(lex, "INTO");
        OG_BREAK_IF_ERROR(status);

        status = sql_parse_split_parts_def(stmt, &word, def);
        OG_BREAK_IF_ERROR(status);

        status = sql_parse_update_index_clause(stmt, &word, def);
    } while (OG_FALSE);

    knl_close_dc(&dc);
    return status;
}

static status_t sql_parse_altable_set_range_part(knl_dictionary_t *dc)
{
    part_table_t *part_table = NULL;

    part_table = DC_ENTITY(dc)->table.part_table;
    if (part_table->desc.interval_key == NULL) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "not support convert range to range partition");
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

static status_t sql_parse_altable_set_interval_part_core(sql_stmt_t *stmt, lex_t *lex, word_t *word,
    knl_altable_def_t *def, knl_column_t *knl_col)
{
    variant_t value;
    expr_tree_t *expr = NULL;
    sql_verifier_t verf = { 0 };
    part_key_t *partkey = NULL;

    verf.context = stmt->context;
    verf.stmt = stmt;
    verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_COLUMN | SQL_EXCL_STAR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
        SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_DEFAULT | SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;

    lex->flags |= LEX_WITH_ARG;

    OG_RETURN_IFERR(sql_copy_text(stmt->context, &word->text.value, &def->part_def.part_interval.interval));
    OG_RETURN_IFERR(sql_create_expr_until(stmt, &expr, word));
    OG_RETURN_IFERR(sql_verify_expr(&verf, expr));
    OG_RETURN_IFERR(sql_exec_expr(stmt, expr, &value));

    if (value.is_null) {
        OG_SRC_THROW_ERROR(expr->loc, ERR_OPERATIONS_NOT_ALLOW, "set inerval to null");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&partkey));
    part_key_init(partkey, 1);
    if (OG_IS_NUMERIC_TYPE(knl_col->datatype)) {
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, knl_col->datatype, knl_col->size,
            KNL_COLUMN_IS_CHARACTER(knl_col), knl_col->precision, knl_col->scale, partkey));
        if (var_as_decimal(&value) != OG_SUCCESS || IS_DEC8_NEG(&value.v_dec)) {
            OG_SRC_THROW_ERROR(expr->loc, ERR_INVALID_PART_TYPE, "interval key data", "");
            return OG_ERROR;
        }
    } else {
        if (!OG_IS_DSITVL_TYPE(value.type) && !OG_IS_YMITVL_TYPE(value.type)) {
            OG_SRC_THROW_ERROR(expr->loc, ERR_INVALID_PART_TYPE, "interval key data", "");
            return OG_ERROR;
        }
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, value.type, knl_col->size, KNL_COLUMN_IS_CHARACTER(knl_col),
            knl_col->precision, knl_col->scale, partkey));
    }
    def->part_def.part_interval.binterval.bytes = (uint8 *)partkey;
    def->part_def.part_interval.binterval.size = partkey->size;
    return lex_expected_end(lex);
}

static status_t sql_parse_altable_set_interval_part(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_dictionary_t *dc,
    knl_altable_def_t *def)
{
    uint16 col_id;
    knl_column_t *knl_col = NULL;

#ifdef OG_RAC_ING
    if ((IS_COORDINATOR || IS_DATANODE) && def->action == ALTABLE_SET_INTERVAL_PART) {
        OG_THROW_ERROR(ERR_OPERATIONS_NOT_ALLOW, "alter table set interval");
        return OG_ERROR;
    }
#endif

    if (knl_part_key_count(dc->handle) > 1) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "only support one interval partition column");
        return OG_ERROR;
    }

    col_id = knl_part_key_column_id(dc->handle, 0);
    knl_col = knl_get_column(dc->handle, col_id);
    if (!OG_IS_NUMERIC_TYPE(knl_col->datatype) && !OG_IS_DATETIME_TYPE(knl_col->datatype)) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid interval partition column data type");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(lex_push(lex, &word->text));
    status_t status = sql_parse_altable_set_interval_part_core(stmt, lex, word, def, knl_col);
    lex_pop(lex);
    return status;
}

status_t sql_parse_altable_set_clause(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_altable_def_t *def)
{
    status_t status = OG_ERROR;
    knl_dictionary_t dc;

    def->action = ALTABLE_SET_INTERVAL_PART;

    if (knl_open_dc(KNL_SESSION(stmt), (text_t *)&def->user, (text_t *)&def->name, &dc) != OG_SUCCESS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not find", T2S(&def->name));
        return OG_ERROR;
    }

    do {
        if (!knl_is_part_table(dc.handle)) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not partition table", T2S(&def->name));
            break;
        }
        OG_BREAK_IF_ERROR(lex_expected_fetch_word(lex, "INTERVAL"));
        OG_BREAK_IF_ERROR(lex_expected_fetch_bracket(lex, word));
        if (word->text.len == 0) {
            OG_BREAK_IF_ERROR(sql_parse_altable_set_range_part(&dc));
        } else {
            OG_BREAK_IF_ERROR(sql_parse_altable_set_interval_part(stmt, lex, word, &dc, def));
        }
        status = lex_expected_end(lex);
    } while (OG_FALSE);

    knl_close_dc(&dc);
    return status;
}

status_t sql_parse_altable_partition(sql_stmt_t *stmt, word_t *word, knl_altable_def_t *def)
{
    uint32 i;
    knl_part_def_t *part_def = NULL;
    knl_part_def_t *parts_def = NULL;
    lex_t *lex = stmt->session->lex;
    uint32 flag = lex->flags;

    def->logical_log_def.is_parts_logical = OG_TRUE;
    lex->flags = LEX_SINGLE_WORD;
    for (;;) {
        if (lex_expected_fetch_variant(lex, word) != OG_SUCCESS) {
            cm_reset_error();
            OG_SRC_THROW_ERROR(word->text.loc, ERR_INVALID_PART_NAME);
            return OG_ERROR;
        }

        OG_RETURN_IFERR(cm_galist_new(&def->logical_log_def.parts, sizeof(knl_part_def_t), (pointer_t *)&part_def));
        OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word->type, (text_t *)&word->text, &part_def->name));

        for (i = 0; i < def->logical_log_def.parts.count - 1; i++) {
            parts_def = (knl_part_def_t *)cm_galist_get(&def->logical_log_def.parts, i);
            if (cm_text_equal(&part_def->name, &parts_def->name)) {
                OG_THROW_ERROR(ERR_DUPLICATE_NAME, "partition", T2S(&parts_def->name));
                return OG_ERROR;
            }
        }

        if (lex_fetch(lex, word) != OG_SUCCESS) {
            return OG_ERROR;
        }

        if (word->type == WORD_TYPE_EOF) {
            break;
        }
        if (!IS_SPEC_CHAR(word, ',')) {
            OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, ", expected but %s found", W2S(word));
            return OG_ERROR;
        }
    }

    lex->flags = flag;
    return OG_SUCCESS;
}

static status_t sql_parse_add_subpartitions(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_dictionary_t *dc,
    knl_altable_def_t *def)
{
    knl_column_t *column = NULL;
    knl_part_column_def_t *subpart_column = NULL;
    knl_part_obj_def_t *obj_def = def->part_def.obj_def;
    knl_part_def_t *parent_def = cm_galist_get(&def->part_def.obj_def->parts, 0);

    def->part_def.obj_def->subpart_type = knl_subpart_table_type(dc->handle);
    cm_galist_init(&obj_def->subpart_keys, stmt->context, sql_alloc_mem);
    parent_def->is_parent = OG_TRUE;

    for (uint16 i = 0; i < knl_subpart_key_count(dc->handle); i++) {
        OG_RETURN_IFERR(cm_galist_new(&obj_def->subpart_keys, sizeof(knl_part_column_def_t), (void **)&subpart_column));
        subpart_column->column_id = knl_subpart_key_column_id(dc->handle, i);
        column = knl_get_column(dc->handle, subpart_column->column_id);
        subpart_column->datatype = column->datatype;
        subpart_column->is_char = KNL_COLUMN_IS_CHARACTER(column);
        subpart_column->precision = column->precision;
        subpart_column->scale = column->scale;
        subpart_column->size = column->size;
    }

    cm_galist_init(&parent_def->subparts, stmt->context, sql_alloc_mem);
    cm_galist_init(&parent_def->group_subkeys, stmt->context, sql_alloc_mem);

    if (word->type == WORD_TYPE_BRACKET) {
        OG_RETURN_IFERR(lex_push(lex, &word->text));
        OG_RETURN_IFERR(sql_part_parse_subpartitions(stmt, word, obj_def, parent_def));
        lex_pop(lex);
    } else {
        /* generate default subpartition for parent part */
        OG_RETURN_IFERR(sql_generate_default_subpart(stmt, obj_def, &parent_def->subparts));
    }

    return OG_SUCCESS;
}

static status_t sql_add_partition_parse_partkeys(knl_part_obj_def_t *obj_def, knl_dictionary_t *dc)
{
    knl_column_t *knl_column = NULL;
    knl_part_column_def_t *part_col = NULL;

    for (uint16 i = 0; i < knl_part_key_count(dc->handle); i++) {
        if (cm_galist_new(&obj_def->part_keys, sizeof(knl_part_column_def_t), (pointer_t *)&part_col) !=
            OG_SUCCESS) {
            return OG_ERROR;
        }
        part_col->column_id = knl_part_key_column_id(dc->handle, i);
        knl_column = knl_get_column(dc->handle, part_col->column_id);
        part_col->datatype = knl_column->datatype;
        part_col->size = knl_column->size;
        part_col->precision = knl_column->precision;
        part_col->scale = knl_column->scale;
        part_col->is_char = KNL_COLUMN_IS_CHARACTER(knl_column);
    }

    return OG_SUCCESS;
}

status_t sql_parse_add_partition(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *def)
{
    word_t word;
    knl_dictionary_t dc;
    knl_part_def_t *part_def = NULL;
    knl_handle_t knl = &stmt->session->knl_session;

    def->action = ALTABLE_ADD_PARTITION;

    if (knl_open_dc(knl, (text_t *)&def->user, (text_t *)&def->name, &dc) != OG_SUCCESS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not find", T2S(&def->name));
        return OG_ERROR;
    }

    if (!knl_is_part_table(dc.handle)) {
        knl_close_dc(&dc);
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not partition table", T2S(&def->name));
        return OG_ERROR;
    }

    if (sql_alloc_mem(stmt->context, sizeof(knl_part_obj_def_t), (pointer_t *)&def->part_def.obj_def) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    def->part_def.obj_def->part_type = knl_part_table_type(dc.handle);

    cm_galist_init(&def->part_def.obj_def->part_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_def.obj_def->group_keys, stmt->context, sql_alloc_mem);

    if (sql_add_partition_parse_partkeys(def->part_def.obj_def, &dc) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    lex->flags |= LEX_WITH_ARG;
    cm_galist_init(&def->part_def.obj_def->parts, stmt->context, sql_alloc_mem);
    if (cm_galist_new(&def->part_def.obj_def->parts, sizeof(knl_part_def_t), (void **)&part_def) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    def->part_def.obj_def->is_composite = (bool32)knl_is_compart_table(dc.handle);
    if (sql_part_parse_partition(stmt, &word, def->part_def.obj_def, part_def) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    if (def->part_def.obj_def->is_composite) {
        if (sql_parse_add_subpartitions(stmt, lex, &word, &dc, def) != OG_SUCCESS) {
            knl_close_dc(&dc);
            return OG_ERROR;
        }
    }

    knl_close_dc(&dc);
    if ((word.type != WORD_TYPE_EOF) && (word.id != RES_WORD_DEFAULT)) {
        OG_SRC_THROW_ERROR_EX(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "expected end but %s found", W2S(&word));
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

static status_t sql_parse_modify_partition_storage(sql_stmt_t *stmt, lex_t *lex, word_t *word,
    knl_altable_def_t *tab_def)
{
    tab_def->action = ALTABLE_MODIFY_PART_STORAGE;
    OG_RETURN_IFERR(sql_parse_storage(lex, word, &tab_def->part_def.storage_def, OG_TRUE));

    return OG_SUCCESS;
}

static status_t sql_parse_modify_partition_initrans(sql_stmt_t *stmt, lex_t *lex, word_t *word,
    knl_altable_def_t *tab_def)
{
    tab_def->action = ALTABLE_MODIFY_PART_INITRANS;
    OG_RETURN_IFERR(sql_parse_trans(lex, word, &tab_def->part_def.part_prop.initrans));
    return lex_fetch(lex, word);
}

static status_t sql_parse_add_subpartition(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_altable_def_t *def)
{
    knl_dictionary_t dc;

    def->action = ALTABLE_ADD_SUBPARTITION;

    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "SUBPARTITION"));
    if (knl_open_dc(&stmt->session->knl_session, &def->user, &def->name, &dc) != OG_SUCCESS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not find", T2S(&def->name));
        return OG_ERROR;
    }

    if (!knl_is_part_table(dc.handle) || !knl_is_compart_table(dc.handle)) {
        knl_close_dc(&dc);
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, " %s not composite partition table", T2S(&def->name));
        return OG_ERROR;
    }

    if (sql_alloc_mem(stmt->context, sizeof(knl_part_obj_def_t), (void **)&def->part_def.obj_def) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    def->part_def.obj_def->subpart_type = knl_subpart_table_type(dc.handle);
    def->part_def.obj_def->is_composite = OG_TRUE;
    cm_galist_init(&def->part_def.obj_def->subpart_keys, stmt->context, sql_alloc_mem);

    knl_column_t *column = NULL;
    knl_part_column_def_t *part_col = NULL;
    for (uint16 i = 0; i < knl_subpart_key_count(dc.handle); i++) {
        if (cm_galist_new(&def->part_def.obj_def->subpart_keys, sizeof(knl_part_column_def_t), (void **)&part_col) !=
            OG_SUCCESS) {
            knl_close_dc(&dc);
            return OG_ERROR;
        }
        part_col->column_id = knl_subpart_key_column_id(dc.handle, i);
        column = knl_get_column(dc.handle, part_col->column_id);
        part_col->datatype = column->datatype;
        part_col->size = column->size;
        part_col->precision = column->precision;
        part_col->scale = column->scale;
        part_col->is_char = KNL_COLUMN_IS_CHARACTER(column);
    }

    knl_part_def_t *part_def = NULL;
    lex->flags |= LEX_WITH_ARG;
    cm_galist_init(&def->part_def.obj_def->parts, stmt->context, sql_alloc_mem);
    cm_galist_new(&def->part_def.obj_def->parts, sizeof(knl_part_def_t), (void **)&part_def);
    cm_galist_init(&part_def->subparts, stmt->context, sql_alloc_mem);
    cm_galist_init(&part_def->group_subkeys, stmt->context, sql_alloc_mem);
    if (sql_part_parse_subpartition(stmt, word, def->part_def.obj_def, part_def) != OG_SUCCESS) {
        knl_close_dc(&dc);
        return OG_ERROR;
    }

    knl_close_dc(&dc);
    return OG_SUCCESS;
}

static status_t sql_parse_coalesce_subpartition(sql_stmt_t *stmt, lex_t *lex, word_t *word, knl_altable_def_t *def)
{
    def->action = ALTABLE_COALESCE_SUBPARTITION;
    OG_RETURN_IFERR(lex_expected_fetch_word(lex, "SUBPARTITION"));
    return lex_fetch(lex, word);
}

status_t sql_parse_modify_partition(sql_stmt_t *stmt, lex_t *lex, knl_altable_def_t *tab_def)
{
    status_t status;
    word_t word;

    if (lex_expected_fetch_variant(lex, &word) != OG_SUCCESS) {
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &tab_def->part_def.name));

    if (lex_fetch(lex, &word) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (word.type != WORD_TYPE_KEYWORD) {
        OG_SRC_THROW_ERROR_EX(word.loc, ERR_SQL_SYNTAX_ERROR, "unexpected word %s found.", W2S(&word));
        return OG_ERROR;
    }

    switch (word.id) {
        case KEY_WORD_STORAGE:
            status = sql_parse_modify_partition_storage(stmt, lex, &word, tab_def);
            break;
        case KEY_WORD_INITRANS:
            status = sql_parse_modify_partition_initrans(stmt, lex, &word, tab_def);
            break;
        case KEY_WORD_ADD:
            status = sql_parse_add_subpartition(stmt, lex, &word, tab_def);
            break;
        case KEY_WORD_COALESCE:
            status = sql_parse_coalesce_subpartition(stmt, lex, &word, tab_def);
            break;
        default:
            OG_SRC_THROW_ERROR_EX(word.loc, ERR_SQL_SYNTAX_ERROR, "unexpected word %s found.", W2S(&word));
            status = OG_ERROR;
            break;
    }

    if (status != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (word.type != WORD_TYPE_EOF) {
        OG_SRC_THROW_ERROR_EX(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "expected end but %s found", W2S(&word));
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

status_t sql_parse_purge_partition(sql_stmt_t *stmt, knl_purge_def_t *def)
{
    word_t word;
    lex_t *lex = stmt->session->lex;
    status_t status;

    lex->flags = LEX_WITH_OWNER;
    stmt->context->entry = def;

    status = lex_expected_fetch_string(lex, &word);
    OG_RETURN_IFERR(status);

    status = sql_convert_object_name(stmt, &word, &def->owner, NULL, &def->name);
    OG_RETURN_IFERR(status);

    def->type = PURGE_PART_OBJECT;

    return lex_expected_end(lex);
}

// verify part attr after column datatype get from  'as select' clause
status_t sql_delay_verify_part_attrs(sql_stmt_t *stmt, knl_table_def_t *def, bool32 *expect_as, word_t *word)
{
    lex_t *lex = stmt->session->lex;
    part_key_t *interval_key = NULL;
    knl_part_obj_def_t *part_def = def->part_def;

    if (part_def == NULL || part_def->delay_partition == OG_FALSE) {
        return OG_SUCCESS;
    }

    bool32 is_composite_old = part_def->is_composite;
    OG_RETURN_IFERR(lex_push(lex, &part_def->save_key));
    part_def->is_composite = OG_FALSE;
    if (sql_part_parse_keys(stmt, word, expect_as, def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }
    lex_pop(lex);
    if (part_def->is_interval) {
        interval_key = (part_key_t *)part_def->binterval.bytes;
        OG_RETURN_IFERR(lex_push(lex, &part_def->save_interval_part)); // &def->part_def->interval
        if (sql_parse_interval_key(stmt, word, interval_key, part_def) != OG_SUCCESS) {
            lex_pop(lex);
            return OG_ERROR;
        }
        lex_pop(lex);
        part_def->binterval.size = interval_key->size;
    }

    part_def->is_composite = is_composite_old;
    if (part_def->is_composite) {
        OG_RETURN_IFERR(lex_push(lex, &part_def->save_subkey));
        if (sql_part_parse_keys(stmt, word, expect_as, def) != OG_SUCCESS) {
            lex_pop(lex);
            return OG_ERROR;
        }
        lex_pop(lex);
    }

    /* in case of store in, it's no need to parse partitions */
    if (part_def->part_store_in.is_store_in) {
        return OG_SUCCESS;
    }

    OG_RETURN_IFERR(lex_push(lex, &part_def->save_part));
    if (sql_part_parse_partitions(stmt, word, def) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }
    lex_pop(lex);

    return OG_SUCCESS;
}

status_t og_parse_partition_attrs(knl_part_def_t *def, galist_t *opts)
{
    index_partition_opt *opt = NULL;
    def->pctfree = OG_INVALID_ID32;
    def->is_csf = OG_INVALID_ID8;

    if (opts == NULL) {
        return OG_SUCCESS;
    }

    for (uint32 i = 0; i < opts->count; i++) {
        opt = (index_partition_opt*)cm_galist_get(opts, i);

        switch (opt->type) {
            case INDEX_PARTITION_OPT_TABLESPACE:
                if (def->space.len != 0) {
                    OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate tablespace specification");
                    return OG_ERROR;
                }
                def->space.str = opt->name;
                def->space.len = strlen(opt->name);
                break;
            case INDEX_PARTITION_OPT_INITRANS:
                if (def->initrans != 0) {
                    OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate initrans specification");
                    return OG_ERROR;
                }
                def->initrans = opt->size;
                break;
            case INDEX_PARTITION_OPT_PCTFREE:
                if (def->pctfree != OG_INVALID_ID32) {
                    OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate pct_free specification");
                    return OG_ERROR;
                }
                def->pctfree = opt->size;
                break;
            case INDEX_PARTITION_OPT_STORAGE:
                if ((def->storage_def.initial > 0) || (def->storage_def.next > 0) || (def->storage_def.maxsize > 0)) {
                    OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate storage option specification");
                    return OG_ERROR;
                }
                def->storage_def = *opt->storage_def;
                break;
            case INDEX_PARTITION_OPT_COMPRESS:
                if (!def->exist_subparts) {
                    def->compress_type = opt->compress_type;
                    def->compress_algo = def->compress_type == COMPRESS_TYPE_GENERAL ? COMPRESS_ZSTD : COMPRESS_NONE;
                    break;
                }
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "table part doesn't support compress if exists subpartitons");
                return OG_ERROR;
            case INDEX_PARTITION_OPT_FORMAT:
                if (!def->support_csf) {
                    OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "unexpected text format clause.");
                    return OG_ERROR;
                }
                def->is_csf = opt->csf;
                break;
            default:
                break;
        }
    }
    return OG_SUCCESS;
}

static inline void og_part_parse_type(knl_part_obj_def_t *part_def, part_type_t part_type)
{
    if (part_def->is_composite) {
        part_def->subpart_type = part_type;
    } else {
        part_def->part_type = part_type;
    }
}

static status_t og_check_part_keys(knl_table_def_t *table_def, knl_part_column_def_t *part_column, char *col_name)
{
    knl_part_column_def_t *column_def = NULL;
    knl_part_obj_def_t *def = table_def->part_def;

    if (def->part_keys.count > OG_MAX_PARTKEY_COLUMNS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "part key count %u more than max value %u",
            def->part_keys.count, OG_MAX_PARTKEY_COLUMNS);
        return OG_ERROR;
    }

    if (def->subpart_keys.count > OG_MAX_PARTKEY_COLUMNS) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, ", subpart key count %u more than max value %u",
            def->subpart_keys.count, OG_MAX_PARTKEY_COLUMNS);
        return OG_ERROR;
    }

    if (def->is_composite) {
        for (uint32 i = 0; i < def->subpart_keys.count - 1; i++) {
            column_def = (knl_part_column_def_t *)cm_galist_get(&def->subpart_keys, i);
            if (part_column->column_id == column_def->column_id) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate column name %s", col_name);
                return OG_ERROR;
            }
        }
    } else {
        for (uint32 i = 0; i < def->part_keys.count - 1; i++) {
            column_def = (knl_part_column_def_t *)cm_galist_get(&def->part_keys, i);
            if (part_column->column_id == column_def->column_id) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate column name %s", col_name);
                return OG_ERROR;
            }
        }
    }

    return OG_SUCCESS;
}

static status_t og_part_parse_keys(sql_stmt_t *stmt, knl_table_def_t *table_def, galist_t *column_list)
{
    status_t status;
    char *col = NULL;
    text_t col_name;
    knl_part_column_def_t *part_col = NULL;
    knl_part_obj_def_t *def = table_def->part_def;

    for (uint32 i = 0; i < column_list->count; i++) {
        col = (char*)cm_galist_get(column_list, i);

        if (def->is_composite) {
            status = cm_galist_new(&def->subpart_keys, sizeof(knl_part_column_def_t), (pointer_t *)&part_col);
        } else {
            status = cm_galist_new(&def->part_keys, sizeof(knl_part_column_def_t), (pointer_t *)&part_col);
        }
        OG_RETURN_IFERR(status);

        col_name.str = col;
        col_name.len = strlen(col);

        part_col->column_id = OG_INVALID_ID32;
        for (uint32 i = 0; i < table_def->columns.count; i++) {
            knl_column_def_t *column_def = cm_galist_get(&table_def->columns, i);

            if (cm_text_equal(&col_name, &column_def->name)) {
                part_col->column_id = i;

                status = sql_part_verify_key_type(&column_def->typmod);
                OG_RETURN_IFERR(status);
                part_col->datatype = column_def->datatype;
                if (column_def->typmod.size > OG_MAX_PART_COLUMN_SIZE) {
                    OG_THROW_ERROR(ERR_MAX_PART_CLOUMN_SIZE, T2S(&column_def->name), OG_MAX_PART_COLUMN_SIZE);
                    return OG_ERROR;
                }
                part_col->is_char = column_def->typmod.is_char;
                part_col->precision = column_def->typmod.precision;
                part_col->scale = column_def->typmod.scale;
                part_col->size = column_def->size;
                break;
            }
        }

        if (part_col->column_id == OG_INVALID_ID32) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "partition key %s can not find in table", col);
            return OG_ERROR;
        }

        if (og_check_part_keys(table_def, part_col, col) != OG_SUCCESS) {
            return OG_ERROR;
        }
    }
    return OG_SUCCESS;
}

static status_t og_parse_interval_key(sql_stmt_t *stmt, part_key_t *interval_key, knl_part_obj_def_t *obj_def,
    expr_tree_t *value_expr)
{
    sql_verifier_t verf = { 0 };
    variant_t value;
    knl_part_column_def_t *key = NULL;

    if (obj_def->part_keys.count > 1) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "only support one interval partition column");
        return OG_ERROR;
    }

    key = cm_galist_get(&obj_def->part_keys, 0);
    if (!OG_IS_NUMERIC_TYPE(key->datatype) && !OG_IS_DATETIME_TYPE(key->datatype)) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid interval partition column data type");
        return OG_ERROR;
    }

    part_key_init(interval_key, 1);

    verf.context = stmt->context;
    verf.stmt = stmt;
    verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_COLUMN | SQL_EXCL_STAR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
        SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_DEFAULT | SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;

    OG_RETURN_IFERR(sql_verify_expr(&verf, value_expr));

    OG_RETURN_IFERR(sql_exec_expr(stmt, value_expr, &value));

    if (value.is_null) {
        OG_SRC_THROW_ERROR(value_expr->loc, ERR_OPERATIONS_NOT_ALLOW, "set inerval to null");
        return OG_ERROR;
    }

    if (OG_IS_NUMERIC_TYPE(key->datatype)) {
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, key->datatype, key->size, key->is_char, key->precision,
            key->scale, interval_key));
        if (var_as_decimal(&value) != OG_SUCCESS || IS_DEC8_NEG(&value.v_dec)) {
            OG_SRC_THROW_ERROR(value_expr->loc, ERR_INVALID_PART_TYPE, "interval key data", "");
            return OG_ERROR;
        }
    } else {
        if (!OG_IS_DSITVL_TYPE(value.type) && !OG_IS_YMITVL_TYPE(value.type)) {
            OG_SRC_THROW_ERROR(value_expr->loc, ERR_INVALID_PART_TYPE, "interval key data", "");
            return OG_ERROR;
        }
        OG_RETURN_IFERR(sql_part_put_key(stmt, &value, value.type, key->size, key->is_char, key->precision, key->scale,
            interval_key));
    }
    return OG_SUCCESS;
}

static status_t og_parse_store_in_space(sql_stmt_t *stmt, knl_store_in_set_t *store_in, galist_t *space_list)
{
    char *space_name = NULL;
    text_t *space = NULL;

    if (space_list == NULL) {
        return OG_SUCCESS;
    }

    for (uint32 i = 0; i < space_list->count; i++) {
        space_name = (char*)cm_galist_get(space_list, i);

        OG_RETURN_IFERR(cm_galist_new(&store_in->space_list, sizeof(text_t), (pointer_t *)&space));
        store_in->space_cnt++;
        cm_str2text(space_name, space);
    }
    return OG_SUCCESS;
}

static status_t og_try_parse_interval(sql_stmt_t *stmt, knl_part_obj_def_t *obj_def, parser_interval_part *interval)
{
    part_key_t *interval_key = NULL;

    if (interval == NULL) {
        return OG_SUCCESS;
    }

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, OG_MAX_COLUMN_SIZE, (pointer_t *)&interval_key));
    obj_def->interval = interval->interval_text;
    OG_RETURN_IFERR(og_parse_interval_key(stmt, interval_key, obj_def, interval->expr));

    obj_def->binterval.bytes = (uint8 *)interval_key;
    obj_def->binterval.size = interval_key->size;

    OG_RETURN_IFERR(og_parse_store_in_space(stmt, &obj_def->part_store_in, interval->tablespaces));
    return OG_SUCCESS;
}

static status_t og_parse_part_type_keys(sql_stmt_t *stmt, knl_table_def_t *table_def, parser_table_part *partition)
{
    knl_part_obj_def_t *def = table_def->part_def;
    status_t status;

    og_part_parse_type(def, partition->part_type);
    status = og_part_parse_keys(stmt, table_def, partition->column_list);
    OG_RETURN_IFERR(status);

    if (def->part_type == PART_TYPE_RANGE) {
        OG_RETURN_IFERR(og_try_parse_interval(stmt, def, partition->interval));
    }

    if (partition->subpart != NULL) {
        def->is_composite = OG_TRUE;
        og_part_parse_type(def, partition->subpart->part_type);

        status = og_part_parse_keys(stmt, table_def, partition->subpart->column_list);
        OG_RETURN_IFERR(status);
    }
    return OG_SUCCESS;
}

static status_t og_part_parse_store_in(sql_stmt_t *stmt, knl_part_obj_def_t *def, part_store_in_clause *store_in)
{
    if (store_in == NULL) {
        return OG_SUCCESS;
    }

    if (def->part_type != PART_TYPE_HASH) {
        return OG_ERROR;
    }

    def->part_store_in.is_store_in = OG_TRUE;
    if (store_in->num == 0) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid partition number");
        return OG_ERROR;
    }
    def->part_store_in.part_cnt = store_in->num;
    OG_RETURN_IFERR(sql_part_parse_hash_attrs(stmt, &def->parts, def->part_store_in.part_cnt));
    OG_RETURN_IFERR(og_parse_store_in_space(stmt, &def->part_store_in, store_in->tablespaces));
    if (def->part_store_in.space_cnt > 0) {
        OG_RETURN_IFERR(sql_part_generate_space_name(stmt, &def->part_store_in, &def->parts));
    }

    return OG_SUCCESS;
}

static status_t og_subpart_parse_store_in(sql_stmt_t *stmt, knl_part_obj_def_t *def, part_store_in_clause *store_in)
{
    if (store_in == NULL) {
        return OG_SUCCESS;
    }

    if (!def->is_composite || def->subpart_type != PART_TYPE_HASH) {
        return OG_ERROR;
    }

    def->subpart_store_in.is_store_in = OG_TRUE;
    if (store_in->num == 0) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid partition number");
        return OG_ERROR;
    }
    def->subpart_store_in.part_cnt = store_in->num;
    OG_RETURN_IFERR(og_parse_store_in_space(stmt, &def->subpart_store_in, store_in->tablespaces));

    return OG_SUCCESS;
}

static status_t og_generate_subpart_for_storein(sql_stmt_t *stmt, knl_part_obj_def_t *def)
{
    knl_part_def_t *part_def = NULL;

    if (!def->is_composite) {
        return OG_SUCCESS;
    }

    for (uint32 i = 0; i < def->parts.count; i++) {
        part_def = (knl_part_def_t *)cm_galist_get(&def->parts, i);
        part_def->is_parent = OG_TRUE;
        cm_galist_init(&part_def->subparts, stmt->context, sql_alloc_mem);
        if (def->subpart_store_in.is_store_in) { // subpart is also defined in "store in"
            knl_part_def_t *subpart_def = NULL;
            for (uint32 j = 0; j < def->subpart_store_in.part_cnt; j++) {
                OG_RETURN_IFERR(cm_galist_new(&part_def->subparts, sizeof(knl_part_def_t), (pointer_t *)&subpart_def));
                OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(part_key_t), (pointer_t *)&subpart_def->partkey));
                subpart_def->initrans = 0;
                subpart_def->is_parent = OG_FALSE;
                subpart_def->is_csf = OG_INVALID_ID8;
                subpart_def->pctfree = OG_INVALID_ID32;
            }
        } else { // it's not specify subpartition desc
            OG_RETURN_IFERR(sql_generate_default_subpart(stmt, def, &part_def->subparts));
        }
    }

    return OG_SUCCESS;
}

static status_t og_part_parse_list_value(sql_stmt_t *stmt, knl_part_def_t *parent_part_def, knl_part_def_t *part_def,
    uint32 key_count, knl_part_obj_def_t *obj_def, galist_t *boundaries)
{
    status_t status;
    expr_tree_t *value_expr = NULL;
    expr_tree_t *tmp_expr = NULL;
    galist_t *value_list = &part_def->value_list;
    uint32 count;
    sql_verifier_t verf = { 0 };
    verf.context = stmt->context;
    verf.stmt = stmt;
    verf.excl_flags = SQL_EXCL_AGGR | SQL_EXCL_COLUMN | SQL_EXCL_STAR | SQL_EXCL_SUBSELECT | SQL_EXCL_JOIN |
        SQL_EXCL_ROWNUM | SQL_EXCL_ROWID | SQL_EXCL_DEFAULT | SQL_EXCL_ROWSCN | SQL_EXCL_WIN_SORT | SQL_EXCL_ROWNODEID;

    for (uint32 i = 0; i < boundaries->count; i++) {
        tmp_expr = (expr_tree_t *)cm_galist_get(boundaries, i);
        count = 0;

        while (tmp_expr != NULL) {
            value_expr = tmp_expr;
            tmp_expr = tmp_expr->next;
            value_expr->next = NULL;

            status = sql_verify_expr(&verf, value_expr);
            OG_RETURN_IFERR(status);

            status = cm_galist_insert(value_list, value_expr);
            OG_RETURN_IFERR(status);
            count++;
        }

        if (count != key_count) {
            OG_SRC_THROW_ERROR_EX(value_expr->loc, ERR_SQL_SYNTAX_ERROR, "value count must equal to partition keys");
        }

        if (key_count == 1) {
            OG_RETURN_IFERR(sql_list_verify_one_key(stmt, value_expr, parent_part_def, obj_def, &part_def->name));
        } else {
            OG_RETURN_IFERR(sql_list_verify_multi_key(stmt, &part_def->value_list, parent_part_def, obj_def,
                &part_def->name));
        }
    }
    return sql_put_list_key(stmt, part_def, obj_def, parent_part_def);
}

static status_t og_parse_list_partition(sql_stmt_t *stmt, knl_part_def_t *part_def, knl_part_obj_def_t *obj_def,
    knl_part_def_t *parent_part_def, part_item_t *item)
{
    bool32 *has_default = parent_part_def != NULL ? &obj_def->sub_has_default : &obj_def->has_default;
    galist_t *tmp_part_keys = parent_part_def == NULL ? &obj_def->part_keys : &obj_def->subpart_keys;
    part_def->hiboundval = item->hiboundval;

    if (*has_default) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "default must be in last partition");
        return OG_ERROR;
    }

    if (item->boundaries == NULL) {
        /* values (DEFAULT) case */
        *has_default = OG_TRUE;
        part_key_init(part_def->partkey, 1);
        part_put_default(part_def->partkey);
        return OG_SUCCESS;
    }

    return og_part_parse_list_value(stmt, parent_part_def, part_def, tmp_part_keys->count, obj_def, item->boundaries);
}

static status_t og_parse_range_values(sql_stmt_t *stmt, knl_part_def_t *part_def, knl_part_obj_def_t *obj_def,
    knl_part_def_t *parent_def, galist_t *boundaries)
{
    variant_t value;
    expr_tree_t *value_expr = NULL;
    sql_verifier_t verf = { 0 };
    knl_part_column_def_t *key = NULL;
    galist_t *tmp_part_keys = &obj_def->part_keys;

    sql_init_verifier(stmt, &verf);

    if (boundaries->count != tmp_part_keys->count) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "value count must equal to partition keys");
        return OG_ERROR;
    }

    if (parent_def != NULL) {
        tmp_part_keys = &obj_def->subpart_keys;
    }

    part_key_init(part_def->partkey, tmp_part_keys->count);

    for (uint32 i = 0; i < boundaries->count; i++) {
        value_expr = (expr_tree_t *)cm_galist_get(boundaries, i);
        if (value_expr->root->type == EXPR_NODE_COLUMN &&
            cm_compare_text_str_ins(&value_expr->root->word.column.name.value, PART_VALUE_MAX) == 0) {
            if ((obj_def->is_interval) && parent_def == NULL) {
                OG_SRC_THROW_ERROR_EX(value_expr->loc, ERR_SQL_SYNTAX_ERROR,
                    "Maxvalue partition cannot be specified for interval partitioned");
                return OG_ERROR;
            }
            part_put_max(part_def->partkey);
        } else {
            key = cm_galist_get(tmp_part_keys, i);
            OG_RETURN_IFERR(sql_verify_expr(&verf, value_expr));
            OG_RETURN_IFERR(sql_exec_expr(stmt, value_expr, &value));
            OG_RETURN_IFERR(sql_part_put_key(stmt, &value, key->datatype, key->size, key->is_char, key->precision,
                key->scale, part_def->partkey));
        }
    }
    return OG_SUCCESS;
}

static status_t og_parse_range_partition(sql_stmt_t *stmt, knl_part_def_t *part_def, knl_part_obj_def_t *obj_def,
    knl_part_def_t *parent_def, part_item_t *item)
{
    part_def->hiboundval = item->hiboundval;

    OG_RETURN_IFERR(og_parse_range_values(stmt, part_def, obj_def, parent_def, item->boundaries));

    return sql_range_verify_keys(stmt, obj_def, part_def, parent_def);
}

static status_t og_part_parse_partition_key(sql_stmt_t *stmt, knl_part_obj_def_t *part_obj, knl_part_def_t *part_def,
    knl_part_def_t *parent_def, part_item_t *item)
{
    part_key_t *partkey = NULL;
    uint32 alloc_size = sizeof(part_key_t);
    status_t status;
    part_type_t part_type = part_obj->part_type;

    OG_RETURN_IFERR(sql_push(stmt, OG_MAX_COLUMN_SIZE, (void **)&partkey));
    MEMS_RETURN_IFERR(memset_s(partkey, OG_MAX_COLUMN_SIZE, 0x00, OG_MAX_COLUMN_SIZE));
    part_def->partkey = partkey;

    if (parent_def != NULL) {
        part_type = part_obj->subpart_type;
    }

    cm_galist_init(&part_def->value_list, stmt->context, sql_alloc_mem);
    switch (part_type) {
        case PART_TYPE_LIST:
            status = og_parse_list_partition(stmt, part_def, part_obj, parent_def, item);
            break;
        case PART_TYPE_RANGE:
            status = og_parse_range_partition(stmt, part_def, part_obj, parent_def, item);
            break;
        default:
            status = OG_SUCCESS;
            break;
    }

    if (status == OG_ERROR) {
        return OG_ERROR;
    }

    if (partkey->size > 0) {
        alloc_size = partkey->size;
    }

    if (sql_alloc_mem(stmt->context, alloc_size, (pointer_t *)&part_def->partkey) != OG_SUCCESS) {
        return OG_ERROR;
    }

    MEMS_RETURN_IFERR(memcpy_sp(part_def->partkey, alloc_size, partkey, alloc_size));

    return status;
}

static status_t og_part_parse_partition(sql_stmt_t *stmt, knl_part_obj_def_t *obj_def, knl_part_def_t *part_def,
    part_item_t *item)
{
    cm_str2text(item->name, &part_def->name);

    if (sql_check_sys_interval_part(obj_def, &part_def->name)) {
        OG_THROW_ERROR(ERR_OPERATIONS_NOT_ALLOW, "create table with interval part name _SYS_P");
        return OG_ERROR;
    }

    OGSQL_SAVE_STACK(stmt);
    if (og_part_parse_partition_key(stmt, obj_def, part_def, NULL, item) != OG_SUCCESS) {
        OGSQL_RESTORE_STACK(stmt);
        return OG_ERROR;
    }
    OGSQL_RESTORE_STACK(stmt);

    part_def->support_csf = OG_TRUE;
    part_def->exist_subparts = obj_def->is_composite ? OG_TRUE : OG_FALSE;
    return og_parse_partition_attrs(part_def, item->opts);
}

static status_t og_part_parse_subpartitions(sql_stmt_t *stmt, knl_part_obj_def_t *obj_def, knl_part_def_t *parent_def,
    galist_t *subpartitions)
{
    knl_part_def_t *subpart_def = NULL;
    part_item_t *item = NULL;

    obj_def->sub_has_default = OG_FALSE;

    for (uint32 i = 0; i < subpartitions->count; i++) {
        item = (part_item_t *)cm_galist_get(subpartitions, i);

        OG_RETURN_IFERR(cm_galist_new(&parent_def->subparts, sizeof(knl_part_def_t), (pointer_t *)&subpart_def));
        cm_str2text(item->name, &subpart_def->name);
        if (sql_check_sys_interval_subpart(obj_def, &subpart_def->name)) {
            OG_THROW_ERROR(ERR_OPERATIONS_NOT_ALLOW, "create table with interval subpart name _SYS_SUBP");
            return OG_ERROR;
        }
        OGSQL_SAVE_STACK(stmt);
        if (og_part_parse_partition_key(stmt, obj_def, subpart_def, parent_def, item) != OG_SUCCESS) {
            OGSQL_RESTORE_STACK(stmt);
            return OG_ERROR;
        }
        OGSQL_RESTORE_STACK(stmt);

        if (item->tablespace != NULL) {
            cm_str2text(item->tablespace, &subpart_def->space);
        }
    }

    return OG_SUCCESS;
}

static status_t og_parse_subpartitions_for_compart(sql_stmt_t *stmt, knl_part_obj_def_t *def, knl_part_def_t *part_def,
    part_item_t *item)
{
    knl_store_in_set_t store_in;
    part_store_in_clause *store_in_clause = NULL;

    cm_galist_init(&part_def->subparts, stmt->context, sql_alloc_mem);
    cm_galist_init(&part_def->group_subkeys, stmt->context, sql_alloc_mem);

    if (def->subpart_store_in.is_store_in) {
        if (item->subpart_clause != NULL) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid subpartition definition");
            return OG_ERROR;
        }
        OG_RETURN_IFERR(sql_part_new_part_def(stmt, def->subpart_store_in.part_cnt, &part_def->subparts));
        if (def->subpart_store_in.space_cnt > 0) {
            OG_RETURN_IFERR(sql_part_generate_space_name(stmt, &def->subpart_store_in, &part_def->subparts));
        }
    } else if (item->subpart_clause != NULL && item->subpart_clause->is_store_in) {
        store_in_clause = item->subpart_clause->subpart_store_in;
        store_in.is_store_in = OG_TRUE;
        store_in.space_cnt = 0;
        cm_galist_init(&store_in.space_list, stmt->context, sql_alloc_mem);
        if (store_in_clause->num == 0) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "invalid partition number");
            return OG_ERROR;
        }
        store_in.part_cnt = store_in_clause->num;
        OG_RETURN_IFERR(og_parse_store_in_space(stmt, &store_in, store_in_clause->tablespaces));
        OG_RETURN_IFERR(sql_part_new_part_def(stmt, store_in.part_cnt, &part_def->subparts));
        if (store_in.space_cnt > 0) {
            OG_RETURN_IFERR(sql_part_generate_space_name(stmt, &store_in, &part_def->subparts));
        }
    } else if (item->subpart_clause != NULL) {
        OG_RETURN_IFERR(og_part_parse_subpartitions(stmt, def, part_def, item->subpart_clause->subparts));
    } else {
        OG_RETURN_IFERR(sql_generate_default_subpart(stmt, def, &part_def->subparts));
    }

    part_def->is_parent = OG_TRUE;

    return OG_SUCCESS;
}

static status_t og_part_parse_partitions(sql_stmt_t *stmt, knl_table_def_t *table_def, galist_t *partitions)
{
    knl_part_def_t *part_def = NULL;
    knl_part_obj_def_t *def = table_def->part_def;
    part_item_t *item = NULL;

    for (uint32 i = 0; i < partitions->count; i++) {
        item = (part_item_t *)cm_galist_get(partitions, i);

        OG_RETURN_IFERR(cm_galist_new(&def->parts, sizeof(knl_part_def_t), (pointer_t *)&part_def));
        OG_RETURN_IFERR(og_part_parse_partition(stmt, def, part_def, item));

        if (def->is_composite) {
            OG_RETURN_IFERR(og_parse_subpartitions_for_compart(stmt, def, part_def, item));
        }
    }
    return OG_SUCCESS;
}

status_t og_part_parse_table(sql_stmt_t *stmt, knl_table_def_t *table_def, parser_table_part *partition)
{
    knl_part_obj_def_t *def = NULL;

    if (cm_lic_check(LICENSE_PARTITION) != OG_SUCCESS) {
        OG_THROW_ERROR(ERR_LICENSE_CHECK_FAIL, " effective partition function license is required.");
        return OG_ERROR;
    }

    if (table_def->part_def != NULL) {
        OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "duplicate partition definition");
        return OG_ERROR;
    }

    OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(knl_part_obj_def_t), (pointer_t *)&table_def->part_def));
    def = table_def->part_def;
    table_def->parted = OG_TRUE;

    cm_galist_init(&def->parts, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->group_keys, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->part_store_in.space_list, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->subpart_store_in.space_list, stmt->context, sql_alloc_mem);
    cm_galist_init(&def->subpart_keys, stmt->context, sql_alloc_mem);

    OG_RETURN_IFERR(og_parse_part_type_keys(stmt, table_def, partition));
    OG_RETURN_IFERR(og_part_parse_store_in(stmt, def, partition->part_store_in));
    OG_RETURN_IFERR(og_subpart_parse_store_in(stmt, def, partition->subpart_store_in));
    if (def->part_store_in.is_store_in) {
        return og_generate_subpart_for_storein(stmt, def);
    }

    return og_part_parse_partitions(stmt, table_def, partition->partitions);
}