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

#include "ddl_sequence_parser.h"
#include "ddl_parser_common.h"
#include "ddl_parser.h"
#include "dtc_dls.h"
#include "dml_parser.h"
#include "scanner.h"

static void sql_init_create_sequence(sql_stmt_t *stmt, knl_sequence_def_t *seq_def)
{
    CM_POINTER(seq_def);
    seq_def->name.len = 0;
    seq_def->start = 1;
    seq_def->step = DDL_SEQUENCE_DEFAULT_INCREMENT;
    seq_def->min_value = 1;
    seq_def->max_value = DDL_ASC_SEQUENCE_DEFAULT_MAX_VALUE;
    seq_def->cache = DDL_SEQUENCE_DEFAULT_CACHE;
    seq_def->is_cycle = OG_FALSE; // default is no cycle
    seq_def->nocache = OG_FALSE;  // no_cache is not specified
    seq_def->nominval = OG_TRUE;  // no_min_value is not specified
    seq_def->nomaxval = OG_TRUE;  // no_max_value is not specified
    seq_def->is_order = OG_FALSE; // order is not specified
    seq_def->is_option_set = (uint32)0;
}

/* ****************************************************************************
Description  : check sequence max/min value is valid or not according to the
grammar
Input        : knl_sequence_def_t * stmt
Output       : None
Modification : Create function
Date         : 2017-02-23
**************************************************************************** */
static status_t sql_check_sequence_scop_value_valid(knl_sequence_def_t *sequence_def)
{
    if (sequence_def->max_value <= sequence_def->min_value) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "MINVALUE must less than MAXVALUE");
        return OG_ERROR;
    }

    int64 next;
    if (sequence_def->step > 0) {
        if ((opr_int64add_overflow(sequence_def->min_value, sequence_def->step, &next)) ||
            (sequence_def->max_value < next)) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "INCREMENT must be less than MAX value minus MIN value");
            return OG_ERROR;
        }
    }

    if (sequence_def->step < 0) {
        if ((opr_int64add_overflow(sequence_def->max_value, sequence_def->step, &next)) ||
            (sequence_def->min_value > next)) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "INCREMENT must be less than MAX value minus MIN value");
            return OG_ERROR;
        }
    }
    return OG_SUCCESS;
}
/* ****************************************************************************
Description  : format sequence max/min value according to the grammar
Input        : knl_sequence_def_t * stmt
Output       : None
Modification : Create function
Date         : 2017-02-23
**************************************************************************** */
static status_t sql_format_sequence_scop_value(knl_sequence_def_t *sequence_def)
{
    sequence_def->nominval = sequence_def->is_minval_set ? OG_FALSE : OG_TRUE;
    sequence_def->nomaxval = sequence_def->is_maxval_set ? OG_FALSE : OG_TRUE;

    /* if minvalue is not specified, then depending on the increment value,
    assign the ascending or descending sequence's default min value */
    if (sequence_def->nominval) {
        sequence_def->min_value =
            sequence_def->step < 0 ? DDL_DESC_SEQUENCE_DEFAULT_MIN_VALUE : DDL_ASC_SEQUENCE_DEFAULT_MIN_VALUE;
        sequence_def->is_minval_set = sequence_def->is_nominval_set ? 1 : 0; /* specify the 'nominvalue' */
    }

    /* if maxvalue is not specified, then depending on the increment value,
    assign the ascending or descending sequence's default max value */
    if (sequence_def->nomaxval) {
        sequence_def->max_value =
            sequence_def->step < 0 ? DDL_DESC_SEQUENCE_DEFAULT_MAX_VALUE : DDL_ASC_SEQUENCE_DEFAULT_MAX_VALUE;
        sequence_def->is_maxval_set = sequence_def->is_nomaxval_set ? 1 : 0; /* specify the 'nomaxvalue' */
    }

    return sql_check_sequence_scop_value_valid(sequence_def);
}
/* ****************************************************************************
Description  : check sequence start with value is valid or not according to
the grammar
Input        : knl_sequence_def_t * stmt
Output       : None
Modification : Create function
Date         : 2017-02-23
**************************************************************************** */
static status_t sql_check_sequence_start_value(knl_sequence_def_t *sequence_def)
{
    if (sequence_def->step > 0) {
        if (sequence_def->start - sequence_def->step > sequence_def->max_value) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "start value cannot be greater than max value plus increment");
            return OG_ERROR;
        }

        if (sequence_def->start < sequence_def->min_value) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "start value cannot be less than min value");
            return OG_ERROR;
        }
    } else {
        if (sequence_def->start - sequence_def->step < sequence_def->min_value) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "start value cannot be less than min value plus increment");
            return OG_ERROR;
        }

        if (sequence_def->start > sequence_def->max_value) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "start value cannot be greater than max value");
            return OG_ERROR;
        }
    }

    return OG_SUCCESS;
}

/* ****************************************************************************
Description  : format sequence start with value according to the grammar
Input        : knl_sequence_def_t * stmt
Output       : None
Modification : Create function
Date         : 2017-02-23
**************************************************************************** */
static status_t sql_format_sequence_start_value(knl_sequence_def_t *sequence_def)
{
    /* if no start value is specified, then min value would be the start value */
    if (!sequence_def->is_start_set) {
        sequence_def->start = sequence_def->step > 0 ? sequence_def->min_value : sequence_def->max_value;
    }

    return sql_check_sequence_start_value(sequence_def);
}

static status_t sql_check_sequence_cache_value(knl_sequence_def_t *sequence_def)
{
    int64 step = cm_abs64(sequence_def->step);
    if (sequence_def->cache <= 1) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "CACHE value must be larger than 1");
        return OG_ERROR;
    }

    if (sequence_def->is_nocache_set) {
        sequence_def->cache = 0;
        sequence_def->is_cache_set = 1;
    }

    if (!sequence_def->nocache && sequence_def->cache < 2) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "number to CACHE must be more than 1");
        return OG_ERROR;
    }

    if (sequence_def->is_cycle && ((uint64)sequence_def->cache >
        ceil((double)((uint64)sequence_def->max_value - sequence_def->min_value) / step))) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "number to CACHE must be less than one cycle");
        return OG_ERROR;
    }

    if (sequence_def->step >= 1 &&
        (sequence_def->cache > (DDL_ASC_SEQUENCE_DEFAULT_MAX_VALUE / cm_abs64(sequence_def->step)))) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "CACHE multiply abs of STEP must be less than DEFAULT MAXVALUE");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_format_sequence(sql_stmt_t *stmt, knl_sequence_def_t *sequence_def)
{
    if (sql_format_sequence_scop_value(sequence_def) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (sql_format_sequence_start_value(sequence_def) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (sql_check_sequence_cache_value(sequence_def) != OG_SUCCESS) {
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_parse_increment(lex_t *lex, knl_sequence_def_t *sequence_def)
{
    int64 increment = 0;

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

    if (lex_expected_fetch_seqval(lex, &increment) != OG_SUCCESS) {
        OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "sequence INCREMENT must be a bigint");
        return OG_ERROR;
    }

    if (increment == 0) {
        OG_SRC_THROW_ERROR(lex->loc, ERR_SEQ_INVALID, "sequence INCREMENT must be a non-zero integer");
        return OG_ERROR;
    }

    sequence_def->step = increment;

    return OG_SUCCESS;
}

static status_t sql_parse_start_with(lex_t *lex, knl_sequence_def_t *sequence_def)
{
    if (lex_expected_fetch_word(lex, "WITH") != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (lex_expected_fetch_seqval(lex, &sequence_def->start) != OG_SUCCESS) {
        OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "sequence START WITH must be a bigint");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t sql_parse_sequence_parameters(sql_stmt_t *stmt, knl_sequence_def_t *seq_def, word_t *word,
    bool32 allow_groupid)
{
    status_t status;
    lex_t *lex = stmt->session->lex;

    for (;;) {
        status = lex_fetch(stmt->session->lex, word);
        OG_RETURN_IFERR(status);

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

        switch (word->id) {
            case (uint32)KEY_WORD_RESTART:
                if (seq_def->is_restart_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate RESTART specifications");
                    return OG_ERROR;
                }
                seq_def->is_restart_set = OG_TRUE;
                break;

            case (uint32)KEY_WORD_INCREMENT:
                if (seq_def->is_step_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate INCREMENT specifications");
                    return OG_ERROR;
                }
                status = sql_parse_increment(lex, seq_def);
                OG_RETURN_IFERR(status);
                seq_def->is_step_set = 1;
                break;

            case (uint32)KEY_WORD_MINVALUE:
                if (seq_def->is_minval_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate MINVALUE specifications");
                    return OG_ERROR;
                }
                if (lex_expected_fetch_seqval(lex, &seq_def->min_value) != OG_SUCCESS) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "sequence MINVALUE must be a bigint");
                    return OG_ERROR;
                }
                seq_def->nominval = OG_FALSE;
                seq_def->is_minval_set = 1;
                break;

            case (uint32)KEY_WORD_NO_MINVALUE:
                if (seq_def->is_nominval_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate NO_MINVALUE specifications");
                    return OG_ERROR;
                }
                seq_def->nominval = OG_TRUE;
                seq_def->is_nominval_set = 1;
                break;

            case (uint32)KEY_WORD_MAXVALUE:
                if (seq_def->is_maxval_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate MAXVALUE specifications");
                    return OG_ERROR;
                }
                if (lex_expected_fetch_seqval(lex, &seq_def->max_value) != OG_SUCCESS) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "sequence MAXVALUE must be a bigint");
                    return OG_ERROR;
                }
                seq_def->nomaxval = OG_FALSE;
                seq_def->is_maxval_set = 1;
                break;

            case (uint32)KEY_WORD_NO_MAXVALUE:
                if (seq_def->is_nomaxval_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate NO_MAXVALUE specifications");
                    return OG_ERROR;
                }
                seq_def->nomaxval = OG_TRUE;
                seq_def->is_nomaxval_set = 1;
                break;

            case (uint32)KEY_WORD_START:
                if (seq_def->is_start_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate START specifications");
                    return OG_ERROR;
                }
                status = sql_parse_start_with(lex, seq_def);
                OG_RETURN_IFERR(status);
                seq_def->is_start_set = OG_TRUE;
                break;

            case (uint32)KEY_WORD_CACHE:
                if (seq_def->is_cache_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate CACHE specifications");
                    return OG_ERROR;
                }
                if (lex_expected_fetch_seqval(lex, &seq_def->cache) != OG_SUCCESS) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "sequence CACHE must be a bigint");
                    return OG_ERROR;
                }
                seq_def->is_cache_set = 1;
                break;

            case (uint32)KEY_WORD_NO_CACHE:
                if (seq_def->is_nocache_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR, "duplicate NO_CACHE specifications");
                    return OG_ERROR;
                }
                seq_def->nocache = OG_TRUE;
                seq_def->is_nocache_set = 1;
                break;

            case (uint32)KEY_WORD_CYCLE:
                if (seq_def->is_cycle_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR,
                        "duplicate or conflicting CYCLE/NOCYCLE specifications");
                    return OG_ERROR;
                }
                seq_def->is_cycle = OG_TRUE;
                seq_def->is_cycle_set = 1;
                break;

            case (uint32)KEY_WORD_NO_CYCLE:
                if (seq_def->is_cycle_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR,
                        "duplicate or conflicting CYCLE/NOCYCLE specifications");
                    return OG_ERROR;
                }
                seq_def->is_cycle = OG_FALSE;
                seq_def->is_cycle_set = 1;
                break;

            case (uint32)KEY_WORD_ORDER:
                if (seq_def->is_order_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR,
                        "duplicate or conflicting ORDER/NOORDER specifications");
                    return OG_ERROR;
                }
                seq_def->is_order = OG_TRUE;
                seq_def->is_order_set = 1;
                break;

            case (uint32)KEY_WORD_NO_ORDER:
                if (seq_def->is_order_set == OG_TRUE) {
                    OG_SRC_THROW_ERROR_EX(lex->loc, ERR_SQL_SYNTAX_ERROR,
                        "duplicate or conflicting ORDER/NOORDER specifications");
                    return OG_ERROR;
                }
                seq_def->is_order = OG_FALSE;
                seq_def->is_order_set = 1;
                break;
            default:
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "syntax error in sequence statement");
                return OG_ERROR;
        }
    }

    return OG_SUCCESS;
}

static status_t sql_check_sequence_conflict_parameters(sql_stmt_t *stmt, knl_sequence_def_t *def)
{
    bool32 result;

    result = (def->is_minval_set && def->is_nominval_set);
    if (result) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting MINVAL/NOMINVAL specifications");
        return OG_ERROR;
    }
    result = (def->is_maxval_set && def->is_nomaxval_set);
    if (result) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting MAX/NOMAXVAL specifications");
        return OG_ERROR;
    }

    result = (def->is_cache_set && def->is_nocache_set);
    if (result) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting CACHE/NOCACHE specifications");
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

static status_t sql_check_sequence_parameters_relation(sql_stmt_t *stmt, knl_sequence_def_t *def)
{
    bool32 result;

    OG_RETURN_IFERR(sql_check_sequence_conflict_parameters(stmt, def));

    result = (!def->is_maxval_set && def->is_cycle && (def->step > 0));
    if (result) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "ascending sequences that CYCLE must specify MAXVALUE");
        return OG_ERROR;
    }

    result = (!def->is_minval_set && def->is_cycle && (def->step < 0));
    if (result) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "descending sequences that CYCLE must specify MINVALUE");
        return OG_ERROR;
    }

    return OG_SUCCESS;
}


status_t sql_parse_create_sequence(sql_stmt_t *stmt)
{
    status_t status;
    knl_sequence_def_t *sequence_def = NULL;
    word_t word;
    lex_t *lex = stmt->session->lex;

    lex->flags |= LEX_WITH_OWNER;

    status = sql_alloc_mem(stmt->context, sizeof(knl_sequence_def_t), (void **)&sequence_def);
    OG_RETURN_IFERR(status);

    sql_init_create_sequence(stmt, sequence_def);
    stmt->context->entry = sequence_def;
    stmt->context->type = OGSQL_TYPE_CREATE_SEQUENCE;
    // parse the sequence name
    status = lex_expected_fetch_variant(stmt->session->lex, &word);
    OG_RETURN_IFERR(status);

    status = sql_convert_object_name(stmt, &word, &sequence_def->user, NULL, &sequence_def->name);
    OG_RETURN_IFERR(status);

    status = sql_parse_sequence_parameters(stmt, sequence_def, &word, OG_TRUE);
    OG_RETURN_IFERR(status);
    status = sql_check_sequence_parameters_relation(stmt, sequence_def);
    OG_RETURN_IFERR(status);

    return sql_format_sequence(stmt, sequence_def);
}

static void sql_parse_alter_sequence_get_param(knl_sequence_def_t *def, dc_sequence_t *seq)
{
    if (def->is_nocache_set) {
        def->cache = 0;
        def->is_cache_set = 1;
    }
    if (def->is_nominval_set) {
        def->min_value = def->step < 0 ? DDL_DESC_SEQUENCE_DEFAULT_MIN_VALUE : DDL_ASC_SEQUENCE_DEFAULT_MIN_VALUE;
        def->is_minval_set = def->is_nominval_set; /* specify the 'nominvalue' */
    }

    if (def->is_nomaxval_set) {
        def->max_value = def->step < 0 ? DDL_DESC_SEQUENCE_DEFAULT_MAX_VALUE : DDL_ASC_SEQUENCE_DEFAULT_MAX_VALUE;
        def->is_maxval_set = def->is_nomaxval_set; /* specify the 'nomaxvalue' */
    }
    return;
}
/*
 * merge param in dc when alter sequence
 * @param[in]   def - sequence defination
 * @param[in]   seq - sequence in dc
 * @return void
 */
static void seq_merge_alter_param(knl_sequence_def_t *seq_def, dc_sequence_t *seq)
{
    seq_def->step = seq_def->is_step_set ? seq_def->step : seq->step;
    seq_def->cache = seq_def->is_cache_set ? seq_def->cache : seq->cache_size;
    seq_def->min_value = seq_def->is_minval_set ? seq_def->min_value : seq->minval;
    seq_def->max_value = seq_def->is_maxval_set ? seq_def->max_value : seq->maxval;
    seq_def->is_cycle = seq_def->is_cycle_set ? seq_def->is_cycle : seq->is_cyclable;
    seq_def->nocache = seq_def->is_nocache_set ? 1 : (seq->is_cache ? 0 : 1);
}
/*
 * check the parameter when alter sequence
 * @param[in]   session - user session
 * @param[in]   def - sequence defination
 * @param[in]   sequence - sequence in dc
 * @return
 * - OG_SUCCESS
 * - OG_ERROR
 */
static status_t seq_check_alter_param(sql_stmt_t *stmt, knl_sequence_def_t *def, dc_sequence_t *sequence)
{
    int64 step = cm_abs64(def->step);
    int64 curr_value = sequence->rsv_nextval;
    int64 next;
    if (!def->is_option_set) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "no options specified for alter sequence");
        return OG_ERROR;
    }
    if (def->is_start_set && !def->is_restart_set) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "cannot alter starting sequence number ");
        return OG_ERROR;
    }
    if (def->max_value <= def->min_value) {
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "MINVALUE must less than MAXVALUE");
        return OG_ERROR;
    }

    if (def->step > 0) {
        if ((opr_int64add_overflow(def->min_value, def->step, &next)) || (def->max_value < next)) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "INCREMENT must be less than MAX value minus MIN value");
            return OG_ERROR;
        }
    }
    if (def->step < 0) {
        if ((opr_int64add_overflow(def->max_value, def->step, &next)) || (def->min_value > next)) {
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "INCREMENT must be less than MAX value minus MIN value");
            return OG_ERROR;
        }
    }

    if (def->is_restart_set) {
        if (def->is_start_set && (def->start < def->min_value || def->start > def->max_value)) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "restart value must be between MINVALUE and MAXVALUE");
            return OG_ERROR;
        }
        curr_value = def->start;
    }

    if (!IS_COORDINATOR || (IS_COORDINATOR && IS_APP_CONN(stmt->session))) {
        if (def->min_value > curr_value) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "MINVALUE cannot be made to exceed the current value");
            return OG_ERROR;
        }

        if (def->max_value < curr_value) {
            OG_THROW_ERROR(ERR_SEQ_INVALID, "MAXVALUE cannot be made to below the current value");
            return OG_ERROR;
        }
    }

    if (def->is_cycle && def->step > 0 && def->is_nomaxval_set) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "cycle must specify maxvalue");
        return OG_ERROR;
    }

    if (def->is_cycle && def->step < 0 && def->is_nominval_set) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "cycle must specify minvalue");
        return OG_ERROR;
    }

    if (def->is_cycle && ((uint64)def->cache > ceil((double)((uint64)def->max_value - def->min_value) / step))) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "number to CACHE must be less than one cycle");
        return OG_ERROR;
    }

    if ((def->is_nocache_set == 0) && (def->is_cache_set == 1) && (def->cache <= 1)) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "CACHE value must be larger than 1");
        return OG_ERROR;
    }

    if (def->is_cycle && ((uint64)def->cache > ceil((double)((uint64)def->max_value - def->min_value) / step))) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "number to CACHE must be less than one cycle");
        return OG_ERROR;
    }

    if (def->step >= 1 && (def->cache > (DDL_ASC_SEQUENCE_DEFAULT_MAX_VALUE / cm_abs64(def->step)))) {
        OG_THROW_ERROR(ERR_SEQ_INVALID, "CACHE multiply abs of STEP must be less than DEFAULT MAXVALUE");
        return OG_ERROR;
    }
    return OG_SUCCESS;
}


static status_t sql_parse_check_param(sql_stmt_t *stmt, knl_sequence_def_t *def)
{
    knl_dictionary_t dc_seq;
    dc_sequence_t *sequence = NULL;
    status_t ret;

    if (OG_SUCCESS != dc_seq_open(&stmt->session->knl_session, &def->user, &def->name, &dc_seq)) {
        return OG_ERROR;
    }
    sequence = (dc_sequence_t *)dc_seq.handle;
    dls_spin_lock(&stmt->session->knl_session, &sequence->entry->lock, NULL);

    if (!sequence->valid || sequence->entry->org_scn > DB_CURR_SCN(KNL_SESSION(stmt))) {
        dls_spin_unlock(&stmt->session->knl_session, &sequence->entry->lock);
        dc_seq_close(&dc_seq);
        OG_THROW_ERROR(ERR_SEQ_NOT_EXIST, T2S(&def->user), T2S_EX(&def->name));
        return OG_ERROR;
    }

    def->step = def->is_step_set ? def->step : sequence->step;
    sql_parse_alter_sequence_get_param(def, sequence);
    seq_merge_alter_param(def, sequence);

    if (def->is_restart_set && !def->is_start_set) {
        def->start = (def->step > 0) ? def->min_value : def->max_value;
    }

    ret = seq_check_alter_param(stmt, def, sequence);
    dls_spin_unlock(&stmt->session->knl_session, &sequence->entry->lock);
    dc_seq_close(&dc_seq);
    return ret;
}

status_t sql_parse_alter_sequence(sql_stmt_t *stmt)
{
    word_t word;
    knl_sequence_def_t *def = NULL;
    lex_t *lex = stmt->session->lex;
    status_t status;

    lex->flags |= LEX_WITH_OWNER;

    status = sql_alloc_mem(stmt->context, sizeof(knl_sequence_def_t), (void **)&def);
    OG_RETURN_IFERR(status);

    sql_init_create_sequence(stmt, def);
    stmt->context->entry = def;
    stmt->context->type = OGSQL_TYPE_ALTER_SEQUENCE;

    status = lex_expected_fetch_variant(stmt->session->lex, &word);
    OG_RETURN_IFERR(status);

    if (word.ex_count > 0) {
        status = sql_copy_prefix_tenant(stmt, (text_t *)&word.text, &def->user, sql_copy_name);
        OG_RETURN_IFERR(status);

        status =
            sql_copy_object_name(stmt->context, word.ex_words[0].type, (text_t *)&word.ex_words[0].text, &def->name);
        OG_RETURN_IFERR(status);
    } else {
        cm_str2text(stmt->session->curr_schema, &def->user);
        status = sql_copy_object_name(stmt->context, word.type, (text_t *)&word.text, &def->name);
        OG_RETURN_IFERR(status);
    }

    status = sql_parse_sequence_parameters(stmt, def, &word, OG_FALSE);
    OG_RETURN_IFERR(status);
    status = sql_check_sequence_conflict_parameters(stmt, def);
    OG_RETURN_IFERR(status);
    if (sql_parse_check_param(stmt, def) != OG_SUCCESS) {
        sql_check_user_priv(stmt, &def->user);
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

status_t sql_parse_drop_sequence(sql_stmt_t *stmt)
{
    knl_drop_def_t *def = NULL;
    bool32 is_cascade = OG_FALSE;
    lex_t *lex = stmt->session->lex;

    lex->flags = LEX_WITH_OWNER;

    stmt->context->type = OGSQL_TYPE_DROP_SEQUENCE;

    if (sql_alloc_mem(stmt->context, sizeof(knl_drop_def_t), (void **)&def) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (sql_parse_drop_object(stmt, def) != OG_SUCCESS) {
        return OG_ERROR;
    }

    stmt->context->entry = def;
    if (lex_try_fetch(lex, "CASCADE", &is_cascade) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (is_cascade) {
        /* NEED TO PARSE CASCADE INFO. */
        OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "cascade option no implement.");
        return OG_ERROR;
    }

    return lex_expected_end(lex);
}


static status_t sql_verify_synonym_def(sql_stmt_t *stmt, knl_synonym_def_t *def)
{
    knl_dictionary_t dc;
    pl_entry_t *pl_entry = NULL;
    bool32 is_tab_found = OG_FALSE;
    bool32 found = OG_FALSE;

    if (knl_open_dc_if_exists(KNL_SESSION(stmt), &def->table_owner, &def->table_name, &dc, &is_tab_found) !=
        OG_SUCCESS) {
        return OG_ERROR;
    }
    if (IS_LTT_BY_NAME(def->table_name.str)) {
        OG_THROW_ERROR(ERR_SQL_SYNTAX_ERROR, "Prevent creating synonyms of local temporary tables");
        return OG_ERROR;
    }
    if (!is_tab_found) {
        if (pl_find_entry(KNL_SESSION(stmt), &def->table_owner, &def->table_name, PL_SYN_LINK_TYPE, &pl_entry,
            &found) != OG_SUCCESS) {
            return OG_ERROR;
        }
        if (!found) {
            OG_THROW_ERROR(ERR_USER_OBJECT_NOT_EXISTS, "The object", T2S(&def->table_owner), T2S_EX(&def->table_name));
            return OG_ERROR;
        }
        def->is_knl_syn = OG_FALSE;
    } else {
        if (SYNONYM_EXIST(&dc) || dc.type > DICT_TYPE_GLOBAL_DYNAMIC_VIEW) {
            OG_THROW_ERROR(ERR_INVALID_SYNONYM_OBJ_TYPE, T2S(&def->table_owner), T2S_EX(&def->table_name));
            knl_close_dc(&dc);
            return OG_ERROR;
        }
        def->ref_uid = dc.uid;
        def->ref_oid = dc.oid;
        def->ref_org_scn = dc.org_scn;
        def->ref_chg_scn = dc.chg_scn;
        def->ref_dc_type = dc.type;
        def->is_knl_syn = OG_TRUE;
        knl_close_dc(&dc);
    }
    return OG_SUCCESS;
}

static inline status_t sql_convert_object_name_ex(sql_stmt_t *stmt, word_t *word, bool32 is_public, text_t *owner,
    text_t *name)
{
    status_t status;
    text_t public_user = { PUBLIC_USER, (uint32)strlen(PUBLIC_USER) };
    sql_copy_func_t sql_copy_func;
    sql_copy_func = sql_copy_name;

    if (word->ex_count == 1) {
        if (is_public) {
            if (cm_compare_text_str_ins((text_t *)&word->text, PUBLIC_USER) != 0) {
                OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR,
                    "owner of object should be public, but is %s", T2S((text_t *)&word->text));
                return OG_ERROR;
            }
            OG_RETURN_IFERR(sql_copy_func(stmt->context, &public_user, owner));
        } else {
            status = sql_copy_prefix_tenant(stmt, (text_t *)&word->text, owner, sql_copy_func);
            OG_RETURN_IFERR(status);
        }

        status = sql_copy_object_name(stmt->context, word->ex_words[0].type, (text_t *)&word->ex_words[0].text, name);
        OG_RETURN_IFERR(status);
    } else {
        if (is_public) {
            OG_RETURN_IFERR(sql_copy_text(stmt->context, &public_user, owner));
        } else {
            cm_str2text(stmt->session->curr_schema, owner);
        }

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

    return OG_SUCCESS;
}

status_t sql_parse_create_synonym(sql_stmt_t *stmt, uint32 flags)
{
    bool32 is_public = (flags & SYNONYM_IS_PUBLIC) ? OG_TRUE : OG_FALSE;
    word_t word;
    knl_synonym_def_t *def = NULL;
    lex_t *lex = stmt->session->lex;

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

    def->flags = flags;
    stmt->context->type = OGSQL_TYPE_CREATE_SYNONYM;

    lex->flags |= LEX_WITH_OWNER;
    if (lex_expected_fetch_variant(lex, &word) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (OG_SUCCESS != sql_convert_object_name_ex(stmt, &word, is_public, &def->owner, &def->name)) {
        return OG_ERROR;
    }

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

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

    if (sql_convert_object_name(stmt, &word, &def->table_owner, NULL, &def->table_name) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (OG_SUCCESS != lex_expected_end(lex)) {
        return OG_ERROR;
    }

    if (sql_verify_synonym_def(stmt, def) != OG_SUCCESS) {
        sql_check_user_priv(stmt, &def->table_owner);
        return OG_ERROR;
    }

    stmt->context->entry = def;

    return OG_SUCCESS;
}

status_t sql_parse_drop_synonym(sql_stmt_t *stmt, uint32 flags)
{
    bool32 result = OG_FALSE;
    bool32 is_public = (flags & SYNONYM_IS_PUBLIC) ? OG_TRUE : OG_FALSE;
    word_t word;
    knl_drop_def_t *def = NULL;
    lex_t *lex = stmt->session->lex;

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

    stmt->context->type = OGSQL_TYPE_DROP_SYNONYM;

    lex->flags |= LEX_WITH_OWNER;

    if (sql_try_parse_if_exists(lex, &def->options) != OG_SUCCESS) {
        return OG_ERROR;
    }

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

    if (sql_convert_object_name_ex(stmt, &word, is_public, &def->owner, &def->name) != OG_SUCCESS) {
        return OG_ERROR;
    }

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

    if (result) {
        def->options |= DROP_CASCADE_CONS;
    }

    stmt->context->entry = def;
    return lex_expected_end(lex);
}

static status_t og_apply_sequence_option(knl_sequence_def_t *sequence_def, createseq_opt *opt, bool32 allow_restart)
{
    switch (opt->type) {
        case CREATESEQ_INCREMENT_OPT:
            if (sequence_def->is_step_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate INCREMENT specifications");
                return OG_ERROR;
            }
            if (opt->value == 0) {
                OG_THROW_ERROR(ERR_SEQ_INVALID, "sequence INCREMENT must be a non-zero integer");
                return OG_ERROR;
            }
            sequence_def->step = opt->value;
            sequence_def->is_step_set = 1;
            break;

        case CREATESEQ_MINVALUE_OPT:
            if (sequence_def->is_minval_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate MINVALUE specifications");
                return OG_ERROR;
            }
            sequence_def->min_value = opt->value;
            sequence_def->nominval = OG_FALSE;
            sequence_def->is_minval_set = 1;
            break;

        case CREATESEQ_NOMINVALUE_OPT:
            if (sequence_def->is_nominval_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate NO_MINVALUE specifications");
                return OG_ERROR;
            }
            sequence_def->nominval = OG_TRUE;
            sequence_def->is_nominval_set = 1;
            break;

        case CREATESEQ_MAXVALUE_OPT:
            if (sequence_def->is_maxval_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate MAXVALUE specifications");
                return OG_ERROR;
            }
            sequence_def->max_value = opt->value;
            sequence_def->nomaxval = OG_FALSE;
            sequence_def->is_maxval_set = 1;
            break;

        case CREATESEQ_NOMAXVALUE_OPT:
            if (sequence_def->is_nomaxval_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate NO_MAXVALUE specifications");
                return OG_ERROR;
            }
            sequence_def->nomaxval = OG_TRUE;
            sequence_def->is_nomaxval_set = 1;
            break;

        case CREATESEQ_START_OPT:
            if (sequence_def->is_start_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate START specifications");
                return OG_ERROR;
            }
            sequence_def->start = opt->value;
            sequence_def->is_start_set = OG_TRUE;
            break;

        case CREATESEQ_CACHE_OPT:
            if (sequence_def->is_cache_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate CACHE specifications");
                return OG_ERROR;
            }
            sequence_def->cache = opt->value;
            sequence_def->is_cache_set = 1;
            break;

        case CREATESEQ_NOCACHE_OPT:
            if (sequence_def->is_nocache_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate NO_CACHE specifications");
                return OG_ERROR;
            }
            sequence_def->nocache = OG_TRUE;
            sequence_def->is_nocache_set = 1;
            break;

        case CREATESEQ_CYCLE_OPT:
            if (sequence_def->is_cycle_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting CYCLE/NOCYCLE specifications");
                return OG_ERROR;
            }
            sequence_def->is_cycle = OG_TRUE;
            sequence_def->is_cycle_set = 1;
            break;

        case CREATESEQ_NOCYCLE_OPT:
            if (sequence_def->is_cycle_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting CYCLE/NOCYCLE specifications");
                return OG_ERROR;
            }
            sequence_def->is_cycle = OG_FALSE;
            sequence_def->is_cycle_set = 1;
            break;

        case CREATESEQ_ORDER_OPT:
            if (sequence_def->is_order_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting ORDER/NOORDER specifications");
                return OG_ERROR;
            }
            sequence_def->is_order = OG_TRUE;
            sequence_def->is_order_set = 1;
            break;

        case CREATESEQ_NOORDER_OPT:
            if (sequence_def->is_order_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate or conflicting ORDER/NOORDER specifications");
                return OG_ERROR;
            }
            sequence_def->is_order = OG_FALSE;
            sequence_def->is_order_set = 1;
            break;

        case CREATESEQ_RESTART_OPT:
            if (!allow_restart) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "syntax error in sequence statement");
                return OG_ERROR;
            }
            if (sequence_def->is_restart_set == OG_TRUE) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "duplicate RESTART specifications");
                return OG_ERROR;
            }
            sequence_def->is_restart_set = OG_TRUE;
            break;

        default:
            OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "syntax error in sequence statement");
            return OG_ERROR;
    }

    return OG_SUCCESS;
}

static status_t og_apply_sequence_options(knl_sequence_def_t *sequence_def, galist_t *seq_opts, bool32 allow_restart)
{
    createseq_opt *opt = NULL;

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

    for (uint32 i = 0; i < seq_opts->count; i++) {
        opt = (createseq_opt *)cm_galist_get(seq_opts, i);
        OG_RETURN_IFERR(og_apply_sequence_option(sequence_def, opt, allow_restart));
    }

    return OG_SUCCESS;
}

status_t og_parse_create_sequence(sql_stmt_t *stmt, knl_sequence_def_t **def, name_with_owner *seq_name,
    galist_t *seq_opts)
{
    status_t status;
    knl_sequence_def_t *sequence_def = NULL;

    stmt->context->type = OGSQL_TYPE_CREATE_SEQUENCE;

    status = sql_alloc_mem(stmt->context, sizeof(knl_sequence_def_t), (void **)def);
    OG_RETURN_IFERR(status);
    sequence_def = *def;

    sql_init_create_sequence(stmt, sequence_def);

    if (seq_name->owner.len == 0) {
        cm_str2text(stmt->session->curr_schema, &sequence_def->user);
    } else {
        sequence_def->user = seq_name->owner;
    }
    sequence_def->name = seq_name->name;

    status = og_apply_sequence_options(sequence_def, seq_opts, OG_FALSE);
    OG_RETURN_IFERR(status);

    // Check parameter relations
    status = sql_check_sequence_parameters_relation(stmt, sequence_def);
    OG_RETURN_IFERR(status);

    // Format sequence
    status = sql_format_sequence(stmt, sequence_def);
    OG_RETURN_IFERR(status);

    return OG_SUCCESS;
}

status_t og_parse_alter_sequence(sql_stmt_t *stmt, knl_sequence_def_t **def, name_with_owner *seq_name,
    galist_t *seq_opts)
{
    status_t status;
    knl_sequence_def_t *sequence_def = NULL;

    stmt->context->type = OGSQL_TYPE_ALTER_SEQUENCE;

    status = sql_alloc_mem(stmt->context, sizeof(knl_sequence_def_t), (void **)def);
    OG_RETURN_IFERR(status);
    sequence_def = *def;

    sql_init_create_sequence(stmt, sequence_def);
    stmt->context->entry = sequence_def;

    if (seq_name->owner.len == 0) {
        cm_str2text(stmt->session->curr_schema, &sequence_def->user);
    } else {
        sequence_def->user = seq_name->owner;
    }
    sequence_def->name = seq_name->name;

    status = og_apply_sequence_options(sequence_def, seq_opts, OG_TRUE);
    OG_RETURN_IFERR(status);

    status = sql_check_sequence_conflict_parameters(stmt, sequence_def);
    OG_RETURN_IFERR(status);

    if (sql_parse_check_param(stmt, sequence_def) != OG_SUCCESS) {
        sql_check_user_priv(stmt, &sequence_def->user);
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

status_t og_parse_create_synonym(sql_stmt_t *stmt, knl_synonym_def_t **def, name_with_owner *synonym_name,
    name_with_owner *object_name, uint32 flags)
{
    status_t status;
    knl_synonym_def_t *synonym_def = NULL;
    text_t public_user = { PUBLIC_USER, (uint32)strlen(PUBLIC_USER) };
    bool32 is_public = (flags & SYNONYM_IS_PUBLIC) ? OG_TRUE : OG_FALSE;

    stmt->context->type = OGSQL_TYPE_CREATE_SYNONYM;

    status = sql_alloc_mem(stmt->context, sizeof(knl_synonym_def_t), (void **)def);
    OG_RETURN_IFERR(status);
    synonym_def = *def;

    synonym_def->flags = flags;

    // Parse synonym name
    if (synonym_name->owner.len != 0) {
        if (is_public) {
            if (cm_compare_text_str_ins(&synonym_name->owner, PUBLIC_USER) != 0) {
                OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "owner of object should be public");
                return OG_ERROR;
            }
            if (sql_copy_name(stmt->context, &public_user, &synonym_def->owner) != OG_SUCCESS) {
                return OG_ERROR;
            }
        } else if (sql_copy_prefix_tenant(stmt, &synonym_name->owner, &synonym_def->owner,
                                          sql_copy_name) != OG_SUCCESS) {
            return OG_ERROR;
        }
        synonym_def->name = synonym_name->name;
    } else {
        if (is_public && sql_copy_text(stmt->context, &public_user, &synonym_def->owner) != OG_SUCCESS) {
            return OG_ERROR;
        } else if (!is_public) {
            cm_str2text(stmt->session->curr_schema, &synonym_def->owner);
        }
        synonym_def->name = synonym_name->name;
    }

    if (object_name->owner.len > 0) {
        synonym_def->table_owner = object_name->owner;
    } else {
        cm_str2text(stmt->session->curr_schema, &synonym_def->table_owner);
    }
    synonym_def->table_name = object_name->name;

    // Verify synonym definition
    if (sql_verify_synonym_def(stmt, synonym_def) != OG_SUCCESS) {
        sql_check_user_priv(stmt, &synonym_def->table_owner);
        return OG_ERROR;
    }

    return OG_SUCCESS;
}