/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * ogsql_replace_parser.c
 *
 *
 * IDENTIFICATION
 * src/ogsql/parser/ogsql_replace_parser.c
 *
 * -------------------------------------------------------------------------
 */

#include "ogsql_replace_parser.h"
#include "ogsql_insert_parser.h"
#include "ogsql_hint_parser.h"
#include "expr_parser.h"
#include "table_parser.h"

#ifdef __cplusplus
extern "C" {
#endif

status_t sql_init_replace(sql_stmt_t *stmt, sql_replace_t *replace_context)
{
    if (sql_create_list(stmt, &replace_context->insert_ctx.pairs) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (sql_create_list(stmt, &replace_context->insert_ctx.pl_dc_lst) != OG_SUCCESS) {
        return OG_ERROR;
    }

    if (sql_create_array(stmt->context, &replace_context->insert_ctx.ssa, "SUB-SELECT", OG_MAX_SUBSELECT_EXPRS) !=
        OG_SUCCESS) {
        return OG_ERROR;
    }

    if (sql_alloc_mem(stmt->context, sizeof(sql_table_t), (void **)&replace_context->insert_ctx.table) != OG_SUCCESS) {
        return OG_ERROR;
    }

    replace_context->insert_ctx.flags = INSERT_SET_NONE;
    replace_context->insert_ctx.select_ctx = NULL;
    replace_context->insert_ctx.plan = NULL;
    replace_context->insert_ctx.pairs_count = 0;
    replace_context->insert_ctx.hint_info = NULL;
    return OG_SUCCESS;
}

static status_t sql_parse_replace_set(sql_stmt_t *stmt, sql_replace_t *replace_context, word_t *word)
{
    lex_t *lex = stmt->session->lex;
    column_value_pair_t *pair = NULL;
    expr_tree_t *expr = NULL;
    sql_insert_t *insert_ctx = &replace_context->insert_ctx;

    for (;;) {
        OG_RETURN_IFERR(lex_expected_fetch_variant(lex, word));
        OG_RETURN_IFERR(cm_galist_new(insert_ctx->pairs, sizeof(column_value_pair_t), (pointer_t *)&pair));
        OG_RETURN_IFERR(sql_create_list(stmt, &pair->exprs));

        OG_RETURN_IFERR(sql_parse_insert_column_quote_info(word, pair));
        OG_RETURN_IFERR(sql_convert_insert_column(stmt, insert_ctx, word, &pair->column_name));
        OG_RETURN_IFERR(lex_expected_fetch_word(lex, "="));

        OG_RETURN_IFERR(sql_create_expr_until(stmt, &expr, word));
        OG_RETURN_IFERR(cm_galist_insert(pair->exprs, expr));

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

        if (!IS_SPEC_CHAR(word, ',')) {
            break;
        }
    }

    insert_ctx->pairs_count++;
    insert_ctx->flags |= INSERT_COLS_SPECIFIED;
    return OG_SUCCESS;
}

static status_t sql_parse_replace_clause(sql_stmt_t *stmt, sql_replace_t *replace_context, sql_insert_t *insert_ctx)
{
    word_t word;
    bool32 result = OG_FALSE;
    lex_t *lex = stmt->session->lex;

    OG_RETURN_IFERR(sql_parse_table(stmt, insert_ctx->table, &word));

    OG_RETURN_IFERR(sql_try_parse_insert_columns(stmt, insert_ctx, &word));

    OG_RETURN_IFERR(sql_try_parse_insert_select(stmt, insert_ctx, &word, &result));

    if (!result) {
        if (word.id == KEY_WORD_SET) {
            if (insert_ctx->pairs->count != 0) {
                OG_SRC_THROW_ERROR(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "not supported to specify column in replace set");
                return OG_ERROR;
            }
            OG_RETURN_IFERR(sql_parse_replace_set(stmt, replace_context, &word));
        } else {
            OG_RETURN_IFERR(sql_parse_insert_values(stmt, insert_ctx, &word));
        }
    }

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

static status_t sql_parse_replace(sql_stmt_t *stmt, sql_replace_t *replace_context)
{
    lex_t *lex = stmt->session->lex;
    bool32 result = OG_FALSE;
    status_t status;
    sql_insert_t *insert_ctx = &(replace_context->insert_ctx);
    OG_RETURN_IFERR(sql_init_replace(stmt, replace_context));

    OG_RETURN_IFERR(lex_try_fetch(lex, "INTO", &result));

    OG_RETURN_IFERR(SQL_SSA_PUSH(stmt, &insert_ctx->ssa));
    status = sql_parse_replace_clause(stmt, replace_context, insert_ctx);
    SQL_SSA_POP(stmt);
    return status;
}


status_t sql_create_replace_context(sql_stmt_t *stmt, sql_text_t *sql, sql_replace_t **replace_context)
{
    lex_t *lex = stmt->session->lex;

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

    if (lex_push(lex, sql) != OG_SUCCESS) {
        return OG_ERROR;
    }

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

    if (sql_parse_replace(stmt, *replace_context) != OG_SUCCESS) {
        lex_pop(lex);
        return OG_ERROR;
    }

    lex_pop(lex);
    return OG_SUCCESS;
}

#ifdef __cplusplus
}
#endif