* 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_merge_parser.c
*
*
* IDENTIFICATION
* src/ogsql/parser/ogsql_merge_parser.c
*
* -------------------------------------------------------------------------
*/
#include "ogsql_merge_parser.h"
#include "ogsql_insert_parser.h"
#include "ogsql_update_parser.h"
#include "table_parser.h"
#include "ogsql_select_parser.h"
#include "cond_parser.h"
#include "ogsql_hint_parser.h"
#ifdef __cplusplus
extern "C" {
#endif
status_t sql_init_merge(sql_stmt_t *stmt, sql_merge_t *merge_ctx)
{
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(sql_query_t), (void **)&merge_ctx->query));
OG_RETURN_IFERR(sql_init_query(stmt, NULL, stmt->session->lex->loc, merge_ctx->query));
OG_RETURN_IFERR(sql_copy_str(stmt->context, "MRG$1", &merge_ctx->query->block_info->origin_name));
OG_RETURN_IFERR(sql_create_list(stmt, &merge_ctx->pl_dc_lst));
merge_ctx->update_ctx = NULL;
merge_ctx->insert_ctx = NULL;
merge_ctx->plan = NULL;
merge_ctx->hint_info = NULL;
return OG_SUCCESS;
}
static status_t sql_parse_merge_update(sql_stmt_t *stmt, sql_merge_t *merge_ctx, word_t *word)
{
lex_t *lex = stmt->session->lex;
if (lex_expected_fetch_word(lex, "UPDATE") != OG_SUCCESS) {
return OG_ERROR;
}
if (merge_ctx->update_ctx != NULL) {
OG_SRC_THROW_ERROR(word->loc, ERR_SQL_SYNTAX_ERROR, "already have word 'UPDATE'");
return OG_ERROR;
}
if (sql_alloc_mem(stmt->context, sizeof(sql_update_t), (void **)&merge_ctx->update_ctx) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_init_update(stmt, merge_ctx->update_ctx) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_array_put(&merge_ctx->update_ctx->query->tables, sql_array_get(&merge_ctx->query->tables, 0)) !=
OG_SUCCESS) {
return OG_ERROR;
}
if (lex_fetch(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_parse_update_set(stmt, merge_ctx->update_ctx, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (word->id == KEY_WORD_WHERE) {
if (sql_create_cond_until(stmt, &merge_ctx->update_filter_cond, word) != OG_SUCCESS) {
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t sql_parse_merge_insert(sql_stmt_t *stmt, sql_merge_t *merge_ctx, word_t *word)
{
lex_t *lex = stmt->session->lex;
bool32 result;
if (lex_expected_fetch_word(lex, "INSERT") != OG_SUCCESS) {
return OG_ERROR;
}
if (merge_ctx->insert_ctx != NULL) {
OG_SRC_THROW_ERROR(word->loc, ERR_SQL_SYNTAX_ERROR, "already have word 'INSERT'");
return OG_ERROR;
}
if (sql_alloc_mem(stmt->context, sizeof(sql_insert_t), (void **)&merge_ctx->insert_ctx) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_init_insert(stmt, merge_ctx->insert_ctx) != OG_SUCCESS) {
return OG_ERROR;
}
merge_ctx->insert_ctx->table = (sql_table_t *)sql_array_get(&merge_ctx->query->tables, 0);
if (lex_try_fetch(lex, "VALUES", &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
word->id = KEY_WORD_VALUES;
} else {
if (lex_fetch(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_try_parse_insert_columns(stmt, merge_ctx->insert_ctx, word) != OG_SUCCESS) {
return OG_ERROR;
}
}
if (sql_parse_insert_values(stmt, merge_ctx->insert_ctx, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (word->id == KEY_WORD_WHERE) {
if (sql_create_cond_until(stmt, &merge_ctx->insert_filter_cond, word) != OG_SUCCESS) {
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t sql_parse_merge_when(sql_stmt_t *stmt, sql_merge_t *merge_ctx, word_t *word)
{
lex_t *lex = stmt->session->lex;
bool32 result = OG_FALSE;
if (word->id != KEY_WORD_WHEN) {
OG_SRC_THROW_ERROR(word->loc, ERR_SQL_SYNTAX_ERROR, "expect word 'WHEN'");
return OG_ERROR;
}
if (lex_try_fetch(lex, "NOT", &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_expected_fetch_word(lex, "MATCHED") != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_expected_fetch_word(lex, "THEN") != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
return sql_parse_merge_insert(stmt, merge_ctx, word);
} else {
return sql_parse_merge_update(stmt, merge_ctx, word);
}
}
static status_t sql_parse_merge_on(sql_stmt_t *stmt, sql_merge_t *merge_ctx, word_t *word)
{
lex_t *lex = stmt->session->lex;
bool32 is_expr = OG_FALSE;
if (word->id != KEY_WORD_ON) {
OG_SRC_THROW_ERROR(word->loc, ERR_SQL_SYNTAX_ERROR, "expect word 'ON'");
return OG_ERROR;
}
if (lex_expected_fetch_bracket(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
OG_RETURN_IFERR(SQL_SSA_PUSH(stmt, &merge_ctx->query->ssa));
if (sql_create_cond_from_text(stmt, &word->text, &merge_ctx->query->cond, &is_expr) != OG_SUCCESS) {
SQL_SSA_POP(stmt);
return OG_ERROR;
}
SQL_SSA_POP(stmt);
if (is_expr) {
OG_SRC_THROW_ERROR(merge_ctx->query->cond->loc, ERR_SQL_SYNTAX_ERROR, "expect condition text");
return OG_ERROR;
}
return lex_fetch(lex, word);
}
static status_t sql_parse_merge_using(sql_stmt_t *stmt, sql_merge_t *merge_ctx, word_t *word)
{
sql_join_chain_t join_chain = { 0 };
sql_table_t *merge_into_table = (sql_table_t *)sql_array_get(&merge_ctx->query->tables, 0);
source_location_t loc = word->loc;
if (word->id != KEY_WORD_USING) {
OG_SRC_THROW_ERROR(loc, ERR_SQL_SYNTAX_ERROR, "expect word 'USING'");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_parse_comma_join(stmt, &merge_ctx->query->tables, &merge_ctx->query->join_assist, &join_chain,
&merge_into_table, word));
if (merge_ctx->query->tables.count > 2) {
OG_SRC_THROW_ERROR(loc, ERR_SQL_SYNTAX_ERROR, "expect select query after 'USING'");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_form_table_join_with_opers(&join_chain, JOIN_TYPE_COMMA));
merge_ctx->query->join_assist.join_node = join_chain.first;
return OG_SUCCESS;
}
static status_t sql_parse_merge(sql_stmt_t *stmt, sql_merge_t *merge_ctx)
{
word_t word;
status_t status = OG_ERROR;
sql_table_t *merge_into_table = NULL;
lex_t *lex = stmt->session->lex;
OG_RETURN_IFERR(sql_init_merge(stmt, merge_ctx));
OG_RETURN_IFERR(SQL_NODE_PUSH(stmt, merge_ctx->query));
OG_RETURN_IFERR(SQL_SSA_PUSH(stmt, &merge_ctx->query->ssa));
do {
OG_BREAK_IF_ERROR(lex_expected_fetch_word(lex, "INTO"));
OG_BREAK_IF_ERROR(sql_array_new(&merge_ctx->query->tables, sizeof(sql_table_t), (void **)&merge_into_table));
merge_into_table->id = merge_ctx->query->tables.count - 1;
OG_BREAK_IF_ERROR(sql_parse_table(stmt, merge_into_table, &word));
OG_BREAK_IF_ERROR(sql_parse_merge_using(stmt, merge_ctx, &word));
OG_BREAK_IF_ERROR(sql_parse_merge_on(stmt, merge_ctx, &word));
OG_BREAK_IF_ERROR(sql_parse_merge_when(stmt, merge_ctx, &word));
if (word.type != WORD_TYPE_EOF) {
OG_BREAK_IF_ERROR(sql_parse_merge_when(stmt, merge_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));
break;
}
status = OG_SUCCESS;
} while (0);
OG_RETURN_IFERR(sql_set_table_qb_name(stmt, merge_ctx->query));
SQL_SSA_POP(stmt);
SQL_NODE_POP(stmt);
return status;
}
status_t sql_create_merge_context(sql_stmt_t *stmt, sql_text_t *sql, sql_merge_t **merge_ctx)
{
MERGE INTO table
USING{ table | (select query) }[alias]
ON(condition)
WHEN MATCHED THEN UPDATE SET col = expression[, ...]
WHEN NOT MATCHED THEN INSERT(column[, ...]) VALUES(expression[, ...])
*/
lex_t *lex = stmt->session->lex;
if (sql_alloc_mem(stmt->context, sizeof(sql_merge_t), (void **)merge_ctx) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_push(lex, sql) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_expected_fetch_word(lex, "MERGE") != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (sql_parse_merge(stmt, *merge_ctx) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
lex_pop(lex);
return OG_SUCCESS;
}
#ifdef __cplusplus
}
#endif