* 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.
* -------------------------------------------------------------------------
*
* func_parser.c
*
*
* IDENTIFICATION
* src/ogsql/parser/func_parser.c
*
* -------------------------------------------------------------------------
*/
#include "func_parser.h"
#include "cond_parser.h"
#include "ogsql_select_parser.h"
#include "ogsql_self_func.h"
#include "pl_udt.h"
#include "decl_cl.h"
#include "typedef_cl.h"
#include "base_compiler.h"
#include "json/ogsql_json.h"
#ifdef __cplusplus
extern "C" {
#endif
status_t sql_try_fetch_func_arg(sql_stmt_t *stmt, text_t *arg_name)
{
char curr;
char next;
word_t word;
lex_t *lex = stmt->session->lex;
bool32 result = OG_FALSE;
LEX_SAVE(lex);
if (lex_try_fetch_variant(lex, &word, &result) != OG_SUCCESS) {
LEX_RESTORE(lex);
return OG_ERROR;
}
if (!result) {
LEX_RESTORE(lex);
return OG_SUCCESS;
}
lex_trim(lex->curr_text);
lex->loc = lex->curr_text->loc;
lex->begin_addr = lex->curr_text->str;
if (lex->curr_text->len < sizeof("=>") - 1) {
LEX_RESTORE(lex);
return OG_SUCCESS;
}
curr = CM_TEXT_BEGIN(lex->curr_text);
next = lex_skip(lex, 1);
(void)lex_skip(lex, 1);
if (curr != '=' || next != '>') {
LEX_RESTORE(lex);
return OG_SUCCESS;
}
return sql_copy_object_name_ci(stmt->context, word.type, &word.text.value, arg_name);
}
static key_word_t g_pkg_key_words[] = {
{ (uint32)KEY_WORD_ALL, OG_TRUE, { (char *)"all", 3 } }
};
static status_t sql_build_func_args(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node, sql_text_t *arg_text)
{
lex_t *lex = stmt->session->lex;
expr_tree_t **arg_expr = NULL;
bool32 assign_arg = OG_FALSE;
text_t arg_name;
key_word_t *save_key_words = lex->key_words;
uint32 save_key_word_cnt = lex->key_word_count;
arg_expr = &func_node->argument;
lex->key_words = g_pkg_key_words;
lex->key_word_count = ELEMENT_COUNT(g_pkg_key_words);
for (;;) {
arg_name.len = 0;
if (sql_try_fetch_func_arg(stmt, &arg_name)) {
lex->key_words = save_key_words;
lex->key_word_count = save_key_word_cnt;
return OG_ERROR;
}
if (arg_name.len == 0 && assign_arg) {
lex->key_words = save_key_words;
lex->key_word_count = save_key_word_cnt;
OG_SRC_THROW_ERROR(LEX_LOC, ERR_SQL_SYNTAX_ERROR, " '=>' expected");
return OG_ERROR;
}
if (sql_create_expr_until(stmt, arg_expr, word) != OG_SUCCESS) {
lex->key_words = save_key_words;
lex->key_word_count = save_key_word_cnt;
return OG_ERROR;
}
if (arg_name.len > 0) {
assign_arg = OG_TRUE;
(*arg_expr)->arg_name = arg_name;
}
OG_BREAK_IF_TRUE(word->type == WORD_TYPE_EOF);
OG_BREAK_IF_TRUE(word->type == WORD_TYPE_OPERATOR);
if (!IS_SPEC_CHAR(word, ',')) {
lex->key_words = save_key_words;
lex->key_word_count = save_key_word_cnt;
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "',' expected but %s found", W2S(word));
return OG_ERROR;
}
arg_expr = &(*arg_expr)->next;
}
lex->key_words = save_key_words;
lex->key_word_count = save_key_word_cnt;
return OG_SUCCESS;
}
* the parse logic of the special argument expression for trim
* - syntax: TRIM([ { { LEADING | TRAILING | BOTH } [ trim_character ] | trim_character } FROM ] trim_source )
* and it also compatible with the generic function call syntax:
* - compatibility: TRIM(trim_source[, trim_character])
*/
static status_t sql_build_func_args_trim(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node, sql_text_t *arg_text)
{
uint32 match_id;
bool32 exist = OG_FALSE;
bool32 reverse = OG_FALSE;
lex_t *lex = stmt->session->lex;
expr_tree_t *temp = (expr_tree_t *)NULL;
func_node->ext_args = FUNC_BOTH;
OG_RETURN_IFERR(lex_try_fetch_1of3(lex, "TRAILING", "LEADING", "BOTH", &match_id));
if (OG_INVALID_ID32 != match_id) {
if (match_id == 0) {
func_node->ext_args = FUNC_RTRIM;
} else if (match_id == 1) {
func_node->ext_args = FUNC_LTRIM;
} else {
func_node->ext_args = FUNC_BOTH;
}
* if trim type specified, check whether the trim type is next to keyword FROM
* if yes, skip the "FROM" to avoid error in sql_create_expr_until()
* this skip is intended to support TRIM( {LEADING | TRAILING | BOTH} FROM {trimSource} )
*/
OG_RETURN_IFERR(lex_try_fetch(lex, "FROM", &exist));
}
if (sql_create_expr_until(stmt, &func_node->argument, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (word->type == WORD_TYPE_EOF) {
return OG_SUCCESS;
}
if ((word->id != KEY_WORD_FROM) && !(IS_SPEC_CHAR(word, ','))) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "from or \",\" expected but %s found", W2S(word));
return OG_ERROR;
}
if (word->id == KEY_WORD_FROM) {
reverse = OG_TRUE;
}
if (sql_create_expr_until(stmt, &func_node->argument->next, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (word->type != WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "expected end but %s found", W2S(word));
return OG_ERROR;
}
if (reverse) {
temp = func_node->argument;
func_node->argument = temp->next;
temp->next = NULL;
func_node->argument->next = temp;
}
return OG_SUCCESS;
}
* create a expression of a const node according to the c-style string specified.
* the c-style string is expected to be terminated with '\0'
*/
status_t sql_create_const_string_expr(sql_stmt_t *stmt, expr_tree_t **new_expr, const char *char_str)
{
expr_tree_t *ret_val = NULL;
expr_node_t *const_node = NULL;
char *txt_buf = NULL;
uint32 txt_len;
CM_POINTER3(stmt, new_expr, char_str);
OG_RETURN_IFERR(sql_alloc_mem((void *)stmt->context, sizeof(expr_tree_t), (void **)&ret_val));
OG_RETURN_IFERR(sql_alloc_mem((void *)stmt->context, sizeof(expr_node_t), (void **)&const_node));
txt_len = (uint32)strlen(char_str);
if (txt_len > 0) {
OG_RETURN_IFERR(sql_alloc_mem((void *)stmt->context, (uint32)strlen(char_str), (void **)&txt_buf));
MEMS_RETURN_IFERR(memcpy_s(txt_buf, txt_len, char_str, txt_len));
}
const_node->type = EXPR_NODE_CONST;
const_node->owner = ret_val;
const_node->datatype = OG_TYPE_STRING;
const_node->value.is_null = OG_FALSE;
const_node->value.type = OG_TYPE_STRING;
const_node->value.v_text.len = txt_len;
const_node->value.v_text.str = txt_buf;
ret_val->owner = stmt->context;
ret_val->root = const_node;
ret_val->generated = OG_TRUE;
ret_val->next = NULL;
*new_expr = ret_val;
return OG_SUCCESS;
}
static inline void sql_set_arg_target_formal(expr_tree_t **next, expr_tree_t **target, expr_tree_t *target_formal)
{
if (*next != NULL) {
(*next)->next = target_formal;
} else {
(*target) = target_formal;
}
(*next) = target_formal;
}
static status_t sql_build_func_args_group_concat(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node,
sql_text_t *arg_text)
{
lex_t *lex = stmt->session->lex;
expr_tree_t *arg_target = NULL;
expr_tree_t *arg_sep = NULL;
expr_tree_t *arg_next = NULL;
expr_tree_t *target_formal = NULL;
bool32 has_separator = OG_FALSE;
for (;;) {
OG_RETURN_IFERR(sql_create_expr_until(stmt, &target_formal, word));
OG_BREAK_IF_TRUE(word->type == WORD_TYPE_EOF);
OG_BREAK_IF_TRUE(word->type == WORD_TYPE_OPERATOR);
* currently, we only support "GROUP_CONCAT(expr[, expr...] [SEPARATOR sep_char])",
* so we stop the loop for parsing the next argument as the sep_char
*/
if (word->type == WORD_TYPE_KEYWORD && word->id == KEY_WORD_SEPARATOR) {
has_separator = OG_TRUE;
break;
}
if (word->type == WORD_TYPE_KEYWORD && word->id == KEY_WORD_ORDER) {
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(galist_t), (void **)&func_node->sort_items));
cm_galist_init(func_node->sort_items, stmt->context, sql_alloc_mem);
OG_RETURN_IFERR(sql_parse_order_by_items(stmt, func_node->sort_items, word));
has_separator = (word->type == WORD_TYPE_KEYWORD && word->id == KEY_WORD_SEPARATOR);
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;
}
sql_set_arg_target_formal(&arg_next, &arg_target, target_formal);
}
sql_set_arg_target_formal(&arg_next, &arg_target, target_formal);
if (has_separator == OG_TRUE) {
OG_RETURN_IFERR(sql_create_expr_until(stmt, &arg_sep, word));
} else {
OG_RETURN_IFERR(sql_create_const_string_expr(stmt, &arg_sep, ","));
}
arg_sep->next = arg_target;
if (word->type != WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "end expected but \"%s\" found", W2S(word));
return OG_ERROR;
}
func_node->argument = arg_sep;
return OG_SUCCESS;
}
* syntax substr('xxx' from xx for xx)
* parse substr/substring args alone
*/
static status_t sql_build_func_args_substr(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node, sql_text_t *arg_text)
{
bool32 has_from = OG_FALSE;
lex_t *lex = stmt->session->lex;
expr_tree_t **arg_expr = &func_node->argument;
for (;;) {
if (sql_create_expr_until(stmt, arg_expr, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (word->type == WORD_TYPE_EOF || word->type == WORD_TYPE_OPERATOR) {
break;
}
arg_expr = &(*arg_expr)->next;
if (word->id == KEY_WORD_FROM) {
has_from = OG_TRUE;
continue;
}
if (!has_from && !(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 (has_from && word->id != KEY_WORD_FOR) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "'for' expected but %s found", W2S(word));
return OG_ERROR;
}
}
return OG_SUCCESS;
}
* EXTRACT( { YEAR|MONTH|DAY|HOUR|MINUTE|SECOND } FROM { datetime_expr|interval_expr } )
*/
static bool32 sql_is_extract_unit_expr(expr_tree_t *unit_expr)
{
word_t word;
if (unit_expr == NULL || unit_expr->root == NULL || unit_expr->chain.count != 1) {
return OG_FALSE;
}
if (unit_expr->root->type != EXPR_NODE_COLUMN || unit_expr->root->unary != UNARY_OPER_NONE ||
unit_expr->root->word.column.table.len != 0) {
return OG_FALSE;
}
word.text = unit_expr->root->word.column.name;
return lex_match_datetime_unit(&word);
}
static status_t sql_build_func_args_extract(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node,
sql_text_t *arg_text)
{
lex_t *lex = stmt->session->lex;
expr_tree_t *unit_expr = NULL;
expr_tree_t *arg_expr = NULL;
(void)arg_text;
OG_RETURN_IFERR(sql_create_expr_until(stmt, &unit_expr, word));
if (word->type == WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR(func_node->loc, ERR_SQL_SYNTAX_ERROR, "not enough arguments for function");
return OG_ERROR;
}
if (word->id != KEY_WORD_FROM) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "FROM expected but %s found", W2S(word));
return OG_ERROR;
}
if (!sql_is_extract_unit_expr(unit_expr)) {
OG_SRC_THROW_ERROR(unit_expr->loc, ERR_SQL_SYNTAX_ERROR, "invalid datetime unit");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_create_expr_until(stmt, &arg_expr, word));
unit_expr->next = arg_expr;
func_node->argument = unit_expr;
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;
}
static status_t sql_build_func_arg_first_last_value(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node,
sql_text_t *arg_text)
{
lex_t *lex = stmt->session->lex;
expr_tree_t **arg_expr = &func_node->argument;
func_node->ignore_nulls = OG_FALSE;
if (lex->curr_text->len == 0) {
OG_SRC_THROW_ERROR(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "argument expected");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_create_expr_until(stmt, arg_expr, word));
if (word->id == KEY_WORD_IGNORE) {
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "NULLS"));
func_node->ignore_nulls = OG_TRUE;
} else if (word->type != WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR_EX(word->loc, ERR_SQL_SYNTAX_ERROR, "expected end but %s found", T2S(&word->text.value));
return OG_ERROR;
}
return OG_SUCCESS;
}
typedef status_t (*arg_build_func_t)(sql_stmt_t *stmt, word_t *word, expr_node_t *func_node, sql_text_t *arg_text);
typedef struct st_arg_build {
const char *func_name;
arg_build_func_t invoke;
} arg_build_t;
static arg_build_t g_arg_build_tab[] = {
{ "TRIM", sql_build_func_args_trim },
{ "GROUP_CONCAT", sql_build_func_args_group_concat },
{ "SUBSTR", sql_build_func_args_substr },
{ "SUBSTRING", sql_build_func_args_substr },
{ "EXTRACT", sql_build_func_args_extract },
{ "JSON_ARRAY", sql_build_func_args_json_array },
{ "JSON_OBJECT", sql_build_func_args_json_object },
{ "JSON_QUERY", sql_build_func_args_json_query },
{ "JSON_MERGEPATCH", sql_build_func_args_json_query },
{ "JSON_VALUE", sql_build_func_args_json_retrieve },
{ "JSON_EXISTS", sql_build_func_args_json_retrieve },
{ "JSON_SET", sql_build_func_args_json_set },
{ "JSONB_QUERY", sql_build_func_args_json_query },
{ "JSONB_MERGEPATCH", sql_build_func_args_json_query },
{ "JSONB_VALUE", sql_build_func_args_json_retrieve },
{ "JSONB_EXISTS", sql_build_func_args_json_retrieve },
{ "JSONB_SET", sql_build_func_args_json_set },
{ "FIRST_VALUE", sql_build_func_arg_first_last_value },
{ "LAST_VALUE", sql_build_func_arg_first_last_value },
};
static status_t sql_build_func_args_group(sql_stmt_t *stmt, lex_t *lex, expr_node_t *node)
{
word_t bracket_word;
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "group"));
OG_RETURN_IFERR(lex_expected_fetch_bracket(lex, &bracket_word));
lex_push(lex, &bracket_word.text);
galist_t *sort_items = NULL;
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "order"));
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(galist_t), (void **)&sort_items));
cm_galist_init(sort_items, stmt->context, sql_alloc_mem);
OG_RETURN_IFERR(sql_parse_order_by_items(stmt, sort_items, &bracket_word));
if (bracket_word.type != WORD_TYPE_EOF) {
OG_SRC_THROW_ERROR_EX(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "expected end but %s found", W2S(&bracket_word));
lex_pop(lex);
return OG_ERROR;
}
lex_pop(lex);
node->sort_items = sort_items;
return OG_SUCCESS;
}
static status_t sql_expected_build_func_args_within_group(sql_stmt_t *stmt, word_t *word, expr_node_t *node)
{
lex_t *lex = stmt->session->lex;
OG_RETURN_IFERR(lex_expected_fetch_word(lex, "within"));
OG_RETURN_IFERR(sql_build_func_args_group(stmt, lex, node));
return OG_SUCCESS;
}
static status_t sql_try_build_func_args_within_group(sql_stmt_t *stmt, word_t *word, expr_node_t *node)
{
lex_t *lex = stmt->session->lex;
bool32 result = OG_FALSE;
OG_RETURN_IFERR(lex_try_fetch(lex, "within", &result));
if (node->word.func.args.value.len > 0) {
if (result) {
OG_RETURN_IFERR(sql_build_func_args_group(stmt, lex, node));
} else {
OG_SRC_THROW_ERROR_EX(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "%s expected", "within");
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t pl_try_find_cast_type(sql_stmt_t *stmt, lex_t *lex, expr_tree_t *arg, word_t *typword, bool32 *found)
{
plv_decl_t *decl = NULL;
bool32 is_found = OG_FALSE;
uint32 save_flags = lex->flags;
lex->flags = LEX_WITH_OWNER;
OG_RETURN_IFERR(lex_fetch(lex, typword));
lex->flags = save_flags;
*found = OG_FALSE;
if (stmt->pl_compiler != NULL) {
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
plc_find_decl_ex(compiler, typword, PLV_TYPE, NULL, &decl);
if (decl == NULL) {
OG_RETURN_IFERR(plc_try_find_global_type(compiler, typword, &decl, &is_found));
} else {
is_found = OG_TRUE;
}
} else {
pl_compiler_t compiler;
compiler.stmt = stmt;
OG_RETURN_IFERR(plc_try_find_global_type(&compiler, typword, &decl, &is_found));
}
if (is_found) {
if (decl->typdef.type == PLV_COLLECTION) {
if (decl->typdef.collection.attr_type != UDT_SCALAR) {
OG_SRC_THROW_ERROR(lex->loc, ERR_PLSQL_VALUE_ERROR_FMT,
"the 2nd-arg's data type in cast func is not supported");
return OG_ERROR;
}
arg->root->datatype = OG_TYPE_COLLECTION;
arg->root->udt_type = &decl->typdef.collection;
arg->loc = typword->loc;
*found = OG_TRUE;
} else {
OG_SRC_THROW_ERROR(lex->loc, ERR_FUNC_ARGUMENT_WRONG_TYPE, 2, "udt collection, global or local");
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static status_t sql_create_cast_arg2(sql_stmt_t *stmt, lex_t *lex, expr_tree_t **arg2)
{
expr_tree_t *arg = NULL;
word_t typword;
typmode_t *v_type = NULL;
if (sql_create_expr(stmt, arg2) != OG_SUCCESS) {
return OG_ERROR;
}
arg = *arg2;
if (sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&arg->root) != OG_SUCCESS) {
return OG_ERROR;
}
v_type = &arg->root->value.v_type;
bool32 is_found = OG_FALSE;
word_t tword;
LEX_SAVE(lex);
if (lex_try_fetch_datatype(lex, &tword, &is_found) != OG_SUCCESS) {
return OG_ERROR;
}
if (is_found) {
OG_RETURN_IFERR(sql_parse_datatype_typemode(lex, PM_NORMAL, v_type, &typword, &tword));
arg->root->value.type = OG_TYPE_TYPMODE;
arg->root->typmod = *v_type;
arg->root->type = EXPR_NODE_CONST;
arg->loc = typword.loc;
static because we need temporay (rather than persistent) vm_lob pages to save the
array elements. Once optimized, the vm_lob page will be freed after the first
execution of statement, and next time we will get an invalid vm page, and this
must cause an error.
*/
if (v_type->is_array != OG_TRUE) {
SQL_SET_OPTMZ_MODE(arg->root, OPTIMIZE_AS_CONST);
}
} else {
LEX_RESTORE(lex);
OG_RETURN_IFERR(pl_try_find_cast_type(stmt, lex, arg, &typword, &is_found));
}
if (!is_found) {
OG_SRC_THROW_ERROR_EX(typword.loc, ERR_SQL_SYNTAX_ERROR, "datatype expected, but got '%s'", W2S(&typword));
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t sql_build_cast_node(sql_stmt_t *stmt, lex_t *lex, word_t *word, expr_node_t *node)
{
sql_text_t *arg_text = &word->ex_words[0].text;
bool32 wd_signed = OG_FALSE;
node->word.func.name = word->text;
node->type = EXPR_NODE_FUNC;
if (sql_copy_object_name_loc(stmt->context, word->type, &word->text, &node->word.func.name) != OG_SUCCESS) {
return OG_ERROR;
}
if (lex_push(lex, arg_text) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_expr_until(stmt, &node->argument, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (word->id != KEY_WORD_AS) {
lex_pop(lex);
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "key word AS expected but %s found", W2S(word));
return OG_ERROR;
}
if (lex_try_fetch(lex, "signed", &wd_signed) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (sql_create_cast_arg2(stmt, lex, &node->argument->next) != OG_SUCCESS) {
return OG_ERROR;
}
if (wd_signed && node->argument->next->root->datatype != OG_TYPE_INTEGER) {
lex_pop(lex);
OG_SRC_THROW_ERROR(node->argument->next->root->loc, ERR_SQL_SYNTAX_ERROR, "type INTEGER expected");
return OG_ERROR;
}
if (lex_expected_end(lex) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
lex_pop(lex);
return OG_SUCCESS;
}
static status_t sql_build_convert_node(sql_stmt_t *stmt, lex_t *lex, word_t *word, expr_node_t *node)
{
sql_text_t *arg_text = &word->ex_words[0].text;
bool32 wd_signed = OG_FALSE;
node->word.func.name = word->text;
node->type = EXPR_NODE_FUNC;
if (sql_copy_object_name_loc(stmt->context, word->type, &word->text, &node->word.func.name) != OG_SUCCESS) {
return OG_ERROR;
}
lex_remove_brackets(arg_text);
if (lex_push(lex, arg_text) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_expr_until(stmt, &node->argument, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
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;
}
if (lex_try_fetch(lex, "signed", &wd_signed) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (sql_create_cast_arg2(stmt, lex, &node->argument->next) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (wd_signed && node->argument->next->root->datatype != OG_TYPE_INTEGER) {
lex_pop(lex);
OG_SRC_THROW_ERROR(node->argument->next->root->loc, ERR_SQL_SYNTAX_ERROR, "type INTEGER expected");
return OG_ERROR;
}
if (lex_expected_end(lex) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
lex_pop(lex);
return OG_SUCCESS;
}
static status_t sql_build_if_node(sql_stmt_t *stmt, lex_t *lex, word_t *word, expr_node_t *node)
{
sql_text_t *arg_text = &word->ex_words[0].text;
node->type = EXPR_NODE_FUNC;
OG_RETURN_IFERR(sql_copy_object_name_loc(stmt->context, word->type, &word->text, &node->word.func.name));
OG_RETURN_IFERR(lex_push(lex, arg_text));
lex->flags |= LEX_IN_FUNCTION;
if (sql_create_cond_until(stmt, &node->cond_arg, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (sql_create_expr_until(stmt, &node->argument, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (sql_create_expr_until(stmt, &node->argument->next, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (lex_expected_end(lex) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
lex->flags &= ~LEX_IN_FUNCTION;
lex_pop(lex);
return OG_SUCCESS;
}
static status_t sql_build_case_node(sql_stmt_t *stmt, word_t *word, expr_node_t *node)
{
bool32 result = OG_FALSE;
case_pair_t *pair = NULL;
case_expr_t *case_expr = NULL;
sql_text_t *arg_text = &word->ex_words[0].text;
node->type = EXPR_NODE_CASE;
if (sql_alloc_mem(stmt->context, sizeof(case_expr_t), (void **)&case_expr) != OG_SUCCESS) {
return OG_ERROR;
}
cm_galist_init(&case_expr->pairs, (void *)stmt->context, (ga_alloc_func_t)sql_alloc_mem);
case_expr->is_cond = OG_FALSE;
OG_RETURN_IFERR(lex_push(stmt->session->lex, arg_text));
if (sql_create_expr_until(stmt, &case_expr->expr, word) != OG_SUCCESS) {
lex_pop(stmt->session->lex);
return OG_ERROR;
}
lex_pop(stmt->session->lex);
OG_RETURN_IFERR(lex_try_fetch(stmt->session->lex, "WHEN", &result));
if (!result) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "WHEN expected but %s found", W2S(word));
return OG_ERROR;
}
for (;;) {
OG_RETURN_IFERR(cm_galist_new(&case_expr->pairs, sizeof(case_pair_t), (void **)&pair));
OG_RETURN_IFERR(sql_create_expr_until(stmt, &pair->when_expr, word));
if (word->id != KEY_WORD_THEN) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "THEN expected but %s found", W2S(word));
return OG_ERROR;
}
OG_RETURN_IFERR(sql_create_expr_until(stmt, &pair->value, word));
if (!(word->id == KEY_WORD_WHEN || word->id == KEY_WORD_ELSE || word->id == KEY_WORD_END)) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "WHEN/ELSE/END expected but %s found",
W2S(word));
return OG_ERROR;
}
if (word->id == KEY_WORD_ELSE || word->id == KEY_WORD_END) {
break;
}
}
if (word->id == KEY_WORD_ELSE) {
OG_RETURN_IFERR(sql_create_expr_until(stmt, &case_expr->default_expr, word));
if (word->id != KEY_WORD_END) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "THEN expected but %s found", W2S(word));
return OG_ERROR;
}
}
VALUE(pointer_t, &node->value) = case_expr;
return OG_SUCCESS;
}
static status_t sql_build_lnnvl_node(sql_stmt_t *stmt, lex_t *lex, word_t *word, expr_node_t *node)
{
sql_text_t *arg_text = &word->ex_words[0].text;
node->type = EXPR_NODE_FUNC;
OG_RETURN_IFERR(sql_copy_object_name_loc(stmt->context, word->type, &word->text, &node->word.func.name));
OG_RETURN_IFERR(lex_push(lex, arg_text));
lex->flags |= LEX_IN_FUNCTION;
if (sql_create_cond_until(stmt, &node->cond_arg, word) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
if (lex_expected_end(lex) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
lex->flags &= ~LEX_IN_FUNCTION;
lex_pop(lex);
return OG_SUCCESS;
}
static status_t sql_build_func_node_unconfiged(sql_stmt_t *stmt, word_t *word, expr_node_t *node, bool32 *matched)
{
lex_t *lex = stmt->session->lex;
switch (word->id) {
case KEY_WORD_CAST:
return sql_build_cast_node(stmt, lex, word, node);
case KEY_WORD_CONVERT:
return sql_build_convert_node(stmt, lex, word, node);
case KEY_WORD_IF:
return sql_build_if_node(stmt, lex, word, node);
case KEY_WORD_CASE:
return sql_build_case_node(stmt, word, node);
case KEY_WORD_LNNVL:
return sql_build_lnnvl_node(stmt, lex, word, node);
default:
break;
}
*matched = OG_FALSE;
return OG_SUCCESS;
}
static status_t sql_build_func_node_arg(sql_stmt_t *stmt, word_t *word, expr_node_t *node, sql_text_t *arg_text)
{
lex_t *lex = stmt->session->lex;
OG_RETURN_IFERR(lex_try_fetch(lex, "DISTINCT", &node->dis_info.need_distinct));
if (node->dis_info.need_distinct && ((cm_compare_text_str_ins(&word->text.value, "DENSE_RANK") == 0) ||
(cm_compare_text_str_ins(&word->text.value, "RANK") == 0))) {
OG_THROW_ERROR_EX(ERR_SQL_SYNTAX_ERROR, "DISTINCT option not allowed for this function");
return OG_ERROR;
}
if (!node->dis_info.need_distinct && (cm_compare_text_str_ins((text_t *)&node->word.func.name, "COUNT") ||
cm_compare_text_str_ins((text_t *)&node->word.func.name, "SUM"))) {
bool32 has_all;
OG_RETURN_IFERR(lex_try_fetch(lex, "ALL", &has_all));
}
if (node->word.func.user_func_first) {
return sql_build_func_args(stmt, word, node, arg_text);
}
* for more special functions, the parse logic of the argument expression
* can be called respectively. SUCH AS TRIM expression, SUBSTRING expression, etc.
*/
uint32 len = sizeof(g_arg_build_tab) / sizeof(arg_build_t);
for (uint32 i = 0; i < len; i++) {
if (cm_compare_text_str_ins(&node->word.func.name.value, g_arg_build_tab[i].func_name) == 0) {
return g_arg_build_tab[i].invoke(stmt, word, node, arg_text);
}
}
* execute the generic parse logic by default,
* for the generic argument expression, like func(arg1, arg2, ...)
*/
return sql_build_func_args(stmt, word, node, arg_text);
}
* in SQL standard, the argument expression in some function is not always like "func(arg1, arg2, ...)"
* so it is necessary to implement some special expression rules for the special functions, such as
* - TRIM(arg2 FROM arg1)
* - SUBSTRING(arg1 FROM arg2 FOR arg3)
* - etc.
*
* in the future, we can use a function point(status_t *fn_ptr(sql_stmt_t * stmt, word_t * word, expr_node_t * node)) to
* handle the special argument expression respectively
*/
status_t sql_build_func_node(sql_stmt_t *stmt, word_t *word, expr_node_t *node)
{
sql_text_t *arg_text = NULL;
lex_t *lex = stmt->session->lex;
status_t status;
text_t user;
user.str = stmt->session->db_user;
user.len = (uint32)strlen(stmt->session->db_user);
node->word.func.user_func_first = OG_FALSE;
if (word->ex_count == 1) {
if (sql_self_func_configed(&user, &word->text.value)) {
node->word.func.user_func_first = OG_TRUE;
} else {
bool32 built = OG_TRUE;
OG_RETURN_IFERR(sql_build_func_node_unconfiged(stmt, word, node, &built));
OG_RETSUC_IFTRUE(built);
}
}
OG_RETURN_IFERR(plc_try_obj_access_node(stmt, word, node));
OG_RETSUC_IFTRUE(IS_UDT_EXPR(node->type));
if (word->type != WORD_TYPE_FUNCTION && plc_prepare_noarg_call(word) != OG_SUCCESS) {
OG_SRC_THROW_ERROR(word->loc, ERR_PLSQL_ILLEGAL_LINE_FMT, "too complex function to call");
return OG_ERROR;
}
OG_RETURN_IFERR(sql_word_as_func(stmt, word, &node->word));
1. if call by user.func, build as user func.
2. function is in udf.ini, build as user func first, such as SLEEP, "sleep", so compare case sensitive.
*/
if (node->word.func.user.len > 0 || sql_self_func_configed_direct(&user, &node->word.func.name.value)) {
node->word.func.user_func_first = OG_TRUE;
}
if (cm_compare_text_str_ins(&word->text.value, "LISTAGG") == 0) {
OG_RETURN_IFERR(sql_expected_build_func_args_within_group(stmt, word, node));
}
if (cm_compare_text_str_ins(&word->text.value, "CUME_DIST") == 0 ||
cm_compare_text_str_ins(&word->text.value, "DENSE_RANK") == 0 ||
cm_compare_text_str_ins(&word->text.value, "RANK") == 0) {
OG_RETURN_IFERR(sql_try_build_func_args_within_group(stmt, word, node));
}
arg_text = &node->word.func.args;
if (arg_text->len == 0) {
return OG_SUCCESS;
}
OG_RETURN_IFERR(lex_push(lex, arg_text));
status = sql_build_func_node_arg(stmt, word, node, arg_text);
lex_pop(lex);
return status;
}
static status_t sql_create_winsort_partlist(sql_stmt_t *stmt, word_t *word, winsort_args_t *winsort_args)
{
lex_t *lex = stmt->session->lex;
expr_tree_t *expr = NULL;
if (lex_expected_fetch_word(lex, "by") != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_list(stmt, &winsort_args->group_exprs) != OG_SUCCESS) {
return OG_ERROR;
}
for (;;) {
if (sql_create_expr_until(stmt, &expr, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (cm_galist_insert(winsort_args->group_exprs, expr) != OG_SUCCESS) {
return OG_ERROR;
}
OG_BREAK_IF_TRUE(!IS_SPEC_CHAR(word, ','));
}
return OG_SUCCESS;
}
static status_t sql_create_winsort_sortlist(sql_stmt_t *stmt, word_t *word, winsort_args_t *winsort_args)
{
lex_t *lex = stmt->session->lex;
uint32 matched = OG_INVALID_ID32;
uint32 tmp_flags;
if (lex_expected_fetch_word(lex, "by") != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_list(stmt, &winsort_args->sort_items) != OG_SUCCESS) {
return OG_ERROR;
}
for (;;) {
sort_item_t *item = NULL;
if (cm_galist_new(winsort_args->sort_items, sizeof(sort_item_t), (void **)&item) != OG_SUCCESS) {
return OG_ERROR;
}
item->direction = SORT_MODE_ASC;
item->nulls_pos = SORT_NULLS_DEFAULT;
if (sql_create_expr_until(stmt, &item->expr, word) != OG_SUCCESS) {
return OG_ERROR;
}
tmp_flags = lex->flags;
CM_CLEAN_FLAG(lex->flags, LEX_WITH_ARG);
if (word->id == KEY_WORD_DESC || word->id == KEY_WORD_ASC) {
item->direction = (word->id == KEY_WORD_DESC) ? SORT_MODE_DESC : SORT_MODE_ASC;
if (lex_fetch(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
}
if (word->id == KEY_WORD_NULLS) {
if (lex_expected_fetch_1of2(lex, "FIRST", "LAST", &matched) != OG_SUCCESS) {
return OG_ERROR;
}
item->nulls_pos = (matched == 0) ? SORT_NULLS_FIRST : SORT_NULLS_LAST;
if (lex_fetch(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
}
if (item->nulls_pos == SORT_NULLS_DEFAULT) {
item->nulls_pos = DEFAULT_NULLS_SORTING_POSITION(item->direction);
}
lex->flags = tmp_flags;
OG_BREAK_IF_TRUE(!IS_SPEC_CHAR(word, ','));
}
return OG_SUCCESS;
}
static status_t sql_create_windowing_border(sql_stmt_t *stmt, word_t *word, uint32 *border_type, expr_tree_t **expr)
{
lex_t *lex = stmt->session->lex;
bool32 result;
uint32 is_following;
if (lex_try_fetch(lex, "unbounded", &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
if (lex_expected_fetch_1of2(lex, "preceding", "following", &is_following) != OG_SUCCESS) {
return OG_ERROR;
}
*border_type = is_following ? WB_TYPE_UNBOUNDED_FOLLOW : WB_TYPE_UNBOUNDED_PRECED;
return OG_SUCCESS;
}
if (lex_try_fetch(lex, "current", &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
if (lex_expected_fetch_word(lex, "row") != OG_SUCCESS) {
return OG_ERROR;
}
*border_type = WB_TYPE_CURRENT_ROW;
return OG_SUCCESS;
}
if (sql_create_expr_until(stmt, expr, word) != OG_SUCCESS) {
return OG_ERROR;
}
if (word->id == KEY_WORD_PRECEDING) {
*border_type = WB_TYPE_VALUE_PRECED;
} else if (word->id == KEY_WORD_FOLLOWING) {
*border_type = WB_TYPE_VALUE_FOLLOW;
} else {
OG_SRC_THROW_ERROR(LEX_LOC, ERR_SQL_SYNTAX_ERROR, "preceding or following expected");
return OG_ERROR;
}
return OG_SUCCESS;
}
static status_t sql_create_windowing_clause(sql_stmt_t *stmt, word_t *word, winsort_args_t *winsort_args)
{
windowing_args_t *windowing = NULL;
lex_t *lex = stmt->session->lex;
bool32 result;
if (word->id != KEY_WORD_RANGE && word->id != KEY_WORD_ROWS) {
return OG_SUCCESS;
}
if (sql_alloc_mem(stmt->context, sizeof(windowing_args_t), (void **)&windowing) != OG_SUCCESS) {
return OG_ERROR;
}
windowing->is_range = (word->id == KEY_WORD_RANGE);
if (lex_try_fetch(lex, "between", &result) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_windowing_border(stmt, word, &windowing->l_type, &windowing->l_expr) != OG_SUCCESS) {
return OG_ERROR;
}
if (result) {
if (lex_expected_fetch_word(lex, "and") != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_windowing_border(stmt, word, &windowing->r_type, &windowing->r_expr) != OG_SUCCESS) {
return OG_ERROR;
}
} else {
windowing->r_type = WB_TYPE_CURRENT_ROW;
}
if (lex_fetch(lex, word) != OG_SUCCESS) {
return OG_ERROR;
}
winsort_args->windowing = windowing;
return OG_SUCCESS;
}
static status_t sql_create_winsort_args(sql_stmt_t *stmt, word_t *word, winsort_args_t *winsort_args)
{
lex_t *lex = stmt->session->lex;
bool32 is_order = OG_FALSE;
uint32 matched;
if (lex_expected_fetch_1of2(lex, "partition", "order", &matched) != OG_SUCCESS) {
return OG_ERROR;
}
if (matched == 0) {
if (sql_create_winsort_partlist(stmt, word, winsort_args) != OG_SUCCESS) {
return OG_ERROR;
}
if (word->id == KEY_WORD_ORDER) {
is_order = OG_TRUE;
}
}
if (matched == 1 || is_order) {
if (sql_create_winsort_sortlist(stmt, word, winsort_args) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_create_windowing_clause(stmt, word, winsort_args) != 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;
}
static status_t sql_build_winsort_node(sql_stmt_t *stmt, word_t *word, expr_node_t *node)
{
winsort_args_t *winsort_args = NULL;
sql_text_t *arg_text = NULL;
lex_t *lex = stmt->session->lex;
expr_tree_t *expr = NULL;
var_word_t *var_word = &node->word;
OG_RETURN_IFERR(sql_copy_object_name_loc(stmt->context, word->type, &word->text, &var_word->func.name));
var_word->func.args = word->ex_words[0].text;
var_word->func.pack.value = CM_NULL_TEXT;
var_word->func.pack.loc = word->text.loc;
var_word->func.user.value = CM_NULL_TEXT;
var_word->func.user.loc = word->text.loc;
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(winsort_args_t), (void **)&winsort_args));
arg_text = &var_word->func.args;
if (arg_text->len == 0) {
OG_RETURN_IFERR(sql_create_list(stmt, &winsort_args->group_exprs));
OG_RETURN_IFERR(sql_create_const_expr_false(stmt, &expr, word, 1));
OG_RETURN_IFERR(cm_galist_insert(winsort_args->group_exprs, expr));
node->win_args = winsort_args;
return OG_SUCCESS;
}
OG_RETURN_IFERR(lex_push(lex, arg_text));
if (sql_create_winsort_args(stmt, word, winsort_args) != OG_SUCCESS) {
lex_pop(lex);
return OG_ERROR;
}
lex_pop(lex);
node->win_args = winsort_args;
return OG_SUCCESS;
}
static status_t sql_create_winsort_node(sql_stmt_t *stmt, expr_tree_t *expr, word_t *word, expr_node_t **node)
{
expr_node_t *func_node = *node;
expr_tree_t *n_expr = NULL;
OG_RETURN_IFERR(sql_create_expr(stmt, &n_expr));
APPEND_CHAIN(&n_expr->chain, func_node);
n_expr->root = func_node;
if (sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)node) != OG_SUCCESS) {
return OG_ERROR;
}
(*node)->owner = expr;
(*node)->type = EXPR_NODE_OVER;
(*node)->unary = expr->unary;
(*node)->loc = word->text.loc;
(*node)->argument = n_expr;
return sql_build_winsort_node(stmt, word, *node);
}
static status_t sql_valid_winsort_node_sp(expr_node_t *node, word_t *word)
{
if (cm_compare_text_str_ins(&node->argument->root->word.func.name.value, "LEAD") == 0) {
if (node->win_args->sort_items == NULL || node->win_args->sort_items->count == 0) {
OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"missing ORDER BY expression in the window specification");
return OG_ERROR;
}
for (uint32 i = 0; i < node->win_args->sort_items->count; i++) {
sort_item_t *item = (sort_item_t *)cm_galist_get(node->win_args->sort_items, i);
item->direction = (item->direction == SORT_MODE_DESC) ? SORT_MODE_ASC : SORT_MODE_DESC;
item->nulls_pos = (item->nulls_pos == SORT_NULLS_FIRST) ? SORT_NULLS_LAST : SORT_NULLS_FIRST;
}
} else if (cm_compare_text_str_ins(&node->argument->root->word.func.name.value, "LAG") == 0) {
if (node->win_args->sort_items == NULL || node->win_args->sort_items->count == 0) {
OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"missing ORDER BY expression in the window specification");
return OG_ERROR;
}
} else if (cm_compare_text_str_ins(&node->argument->root->word.func.name.value, "CUME_DIST") == 0) {
if (node->win_args->sort_items == NULL || node->win_args->sort_items->count == 0) {
OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR,
"missing ORDER BY expression in the window specification");
return OG_ERROR;
}
}
return OG_SUCCESS;
}
static inline bool32 sql_check_must_has_over_word(word_t *word)
{
if (cm_compare_text_str_ins(&word->text.value, "CUME_DIST") == 0) {
return OG_TRUE;
}
return OG_FALSE;
}
status_t sql_build_func_over(sql_stmt_t *stmt, expr_tree_t *expr, word_t *word, expr_node_t **node)
{
lex_t *lex = stmt->session->lex;
word_t word_ahead;
bool32 must_has_over = sql_check_must_has_over_word(word);
LEX_SAVE(lex);
OG_RETURN_IFERR(lex_fetch(lex, &word_ahead));
if (cm_text_str_equal_ins((text_t *)&word_ahead.text, "over") && (word_ahead.ex_count != 0)) {
OG_RETURN_IFERR(sql_create_winsort_node(stmt, expr, &word_ahead, node));
OG_RETURN_IFERR(sql_valid_winsort_node_sp(*node, word));
} else {
LEX_RESTORE(lex);
if (must_has_over) {
OG_SRC_THROW_ERROR(word->text.loc, ERR_SQL_SYNTAX_ERROR, "missing window specification for this function");
return OG_ERROR;
}
}
return OG_SUCCESS;
}
status_t sql_build_cast_expr(sql_stmt_t *stmt, source_location_t loc, expr_tree_t *expr, typmode_t *type,
expr_tree_t **res)
{
sql_text_t cast_name;
expr_node_t *cast_node = NULL;
expr_tree_t *arg2 = NULL;
expr_tree_t *cast_expr = NULL;
if (sql_create_expr(stmt, &cast_expr) != OG_SUCCESS) {
return OG_ERROR;
}
cast_expr->expecting = EXPR_EXPECT_OPER;
if (sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&cast_node) != OG_SUCCESS) {
return OG_ERROR;
}
cast_node->owner = cast_expr;
cast_node->type = EXPR_NODE_FUNC;
cast_node->loc = loc;
cm_str2text(CAST_FUNCTION_NAME, &cast_name.value);
cast_name.str = CAST_FUNCTION_NAME;
cast_name.len = (uint32)strlen(CAST_FUNCTION_NAME);
cast_name.loc = loc;
cast_node->word.func.name = cast_name;
cast_node->argument = expr;
if (sql_create_expr(stmt, &expr->next) != OG_SUCCESS) {
return OG_ERROR;
}
arg2 = expr->next;
if (sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&arg2->root) != OG_SUCCESS) {
return OG_ERROR;
}
arg2->root->value.v_type = *type;
arg2->root->value.type = (int16)OG_TYPE_TYPMODE;
arg2->root->datatype = type->datatype;
arg2->root->type = EXPR_NODE_CONST;
arg2->root->exec_default = OG_TRUE;
APPEND_CHAIN(&(cast_expr->chain), cast_node);
*res = cast_expr;
return sql_generate_expr(*res);
}
status_t sql_convert_to_cast(sql_stmt_t *stmt, expr_tree_t *expr, word_t *word)
{
expr_tree_t *arg2 = NULL;
expr_node_t *node = NULL;
expr_node_t *last = NULL;
lex_t *lex = stmt->session->lex;
if (expr->chain.count == 0) {
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "the word \"%s\" is not correct", W2S(word));
return OG_ERROR;
}
if (expr->chain.last->type < EXPR_NODE_OPCEIL && expr->chain.last->left == NULL) {
/ \
a b
error */
OG_SRC_THROW_ERROR_EX(word->text.loc, ERR_SQL_SYNTAX_ERROR, "the word \"%s\" is not correct", W2S(word));
return OG_ERROR;
}
if (sql_create_cast_arg2(stmt, lex, &arg2) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&node) != OG_SUCCESS) {
return OG_ERROR;
}
node->owner = expr;
node->type = EXPR_NODE_FUNC;
node->unary = expr->unary;
node->loc = word->text.loc;
node->word.func.name.str = "CAST";
node->word.func.name.len = sizeof("CAST") - 1;
if (sql_alloc_mem(stmt->context, sizeof(expr_tree_t), (void **)&node->argument) != OG_SUCCESS) {
return OG_ERROR;
}
node->argument->next = arg2;
node->datatype = arg2->root->value.v_type.datatype;
node->typmod = arg2->root->value.v_type;
last = expr->chain.last;
node->argument->root = last;
node->prev = last->prev;
if (last->prev != NULL) {
last->prev->next = node;
}
if (expr->chain.count == 1) {
expr->chain.first = node;
}
expr->chain.last = node;
return OG_SUCCESS;
}
static inline bool32 sql_func_is_pl_compile_context(sql_stmt_t *stmt)
{
if ((stmt->context->type >= OGSQL_TYPE_CREATE_PROC && stmt->context->type < OGSQL_TYPE_PL_CEIL_END) ||
stmt->context->type == OGSQL_TYPE_ANONYMOUS_BLOCK) {
return OG_TRUE;
}
if (stmt->pl_compiler == NULL) {
return OG_FALSE;
}
return ((pl_compiler_t *)stmt->pl_compiler)->current_input != NULL;
}
typedef struct st_pl_collection_elem_expr_arg {
expr_node_t *node;
galist_t *func_name;
expr_tree_t *arg_list;
source_location_t loc;
} pl_collection_elem_expr_arg_t;
static status_t sql_try_create_pl_collection_elem_expr(sql_stmt_t *stmt,
pl_collection_elem_expr_arg_t *arg, bool32 *converted)
{
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
plv_decl_t *decl = NULL;
plc_var_type_t var_type = PLC_NORMAL_VAR;
var_address_pair_t *addr_pair = NULL;
word_t word = { 0 };
*converted = OG_FALSE;
if (!sql_func_is_pl_compile_context(stmt) || compiler == NULL ||
arg->func_name->count != 1 || arg->arg_list == NULL) {
return OG_SUCCESS;
}
word.id = OG_INVALID_ID32;
word.type = WORD_TYPE_VARIANT;
word.ori_type = WORD_TYPE_VARIANT;
word.loc = arg->loc;
word.text = arg->node->word.func.name;
plc_find_decl_ex(compiler, &word, PLV_VARIANT_AND_CUR, &var_type, &decl);
if (decl == NULL || decl->type != PLV_COLLECTION) {
return OG_SUCCESS;
}
OG_RETURN_IFERR(plc_build_var_address(stmt, decl, arg->node, UDT_STACK_ADDR));
OG_RETURN_IFERR(plc_add_udt_pair(stmt, arg->node->value.v_address.pairs, UDT_COLL_ELEMT_ADDR, &addr_pair));
addr_pair->coll_elemt->parent = decl->collection;
addr_pair->coll_elemt->id = arg->arg_list;
OG_RETURN_IFERR(plc_clone_expr_tree(compiler, &addr_pair->coll_elemt->id));
OG_RETURN_IFERR(plc_verify_limit_expr(compiler, addr_pair->coll_elemt->id));
*converted = OG_TRUE;
return OG_SUCCESS;
}
static status_t sql_try_find_pl_construct_type(sql_stmt_t *stmt, text_t *name, source_location_t loc, plv_decl_t **decl,
bool32 *found)
{
word_t word = { 0 };
*decl = NULL;
*found = OG_FALSE;
word.id = OG_INVALID_ID32;
word.type = WORD_TYPE_VARIANT;
word.ori_type = WORD_TYPE_VARIANT;
word.loc = loc;
word.text.value = *name;
word.text.loc = loc;
if (stmt->pl_compiler != NULL) {
pl_compiler_t *compiler = (pl_compiler_t *)stmt->pl_compiler;
plc_find_decl_ex(compiler, &word, PLV_TYPE, NULL, decl);
if (*decl != NULL) {
*found = OG_TRUE;
return OG_SUCCESS;
}
return plc_try_find_global_type(compiler, &word, decl, found);
}
pl_compiler_t compiler = { 0 };
compiler.stmt = stmt;
return plc_try_find_global_type(&compiler, &word, decl, found);
}
static status_t sql_try_create_pl_construct_expr(sql_stmt_t *stmt, pl_collection_elem_expr_arg_t *arg,
bool32 *converted)
{
plv_decl_t *decl = NULL;
bool32 found = OG_FALSE;
udt_constructor_t *v_construct = NULL;
*converted = OG_FALSE;
if (arg->func_name->count != 1 || arg->arg_list == NULL) {
return OG_SUCCESS;
}
OG_RETURN_IFERR(sql_try_find_pl_construct_type(stmt, &arg->node->word.func.name.value, arg->loc, &decl, &found));
if (!found || decl == NULL || decl->type != PLV_TYPE) {
return OG_SUCCESS;
}
if (decl->typdef.type == PLV_COLLECTION) {
if (decl->typdef.collection.type == UDT_HASH_TABLE) {
OG_THROW_ERROR(ERR_PL_SYNTAX_ERROR_FMT, "associative arrays do not support constructor");
return OG_ERROR;
}
arg->node->type = EXPR_NODE_V_CONSTRUCT;
arg->node->value.is_null = OG_FALSE;
v_construct = &arg->node->value.v_construct;
v_construct->is_coll = OG_TRUE;
v_construct->meta = (void *)&decl->typdef.collection;
} else if (decl->typdef.type == PLV_OBJECT) {
arg->node->type = EXPR_NODE_V_CONSTRUCT;
arg->node->value.is_null = OG_FALSE;
v_construct = &arg->node->value.v_construct;
v_construct->is_coll = OG_FALSE;
v_construct->meta = (void *)&decl->typdef.object;
} else {
return OG_SUCCESS;
}
arg->node->argument = arg->arg_list;
*converted = OG_TRUE;
return OG_SUCCESS;
}
static status_t sql_bison_func_name_as_word(galist_t *func_name, word_t *word, source_location_t loc)
{
expr_tree_t *list_expr = NULL;
if (func_name->count == 0 || func_name->count > MAX_EXTRA_TEXTS + 1) {
OG_SRC_THROW_ERROR(loc, ERR_SQL_SYNTAX_ERROR, "improper function name");
return OG_ERROR;
}
word->id = OG_INVALID_ID32;
word->type = WORD_TYPE_VARIANT;
word->ori_type = WORD_TYPE_VARIANT;
word->loc = loc;
list_expr = cm_galist_get(func_name, 0);
word->text.value = list_expr->root->value.v_text;
word->text.loc = list_expr->root->loc;
for (uint32 i = 1; i < func_name->count; i++) {
list_expr = cm_galist_get(func_name, i);
word->ex_words[word->ex_count].type = WORD_TYPE_VARIANT;
word->ex_words[word->ex_count].text.value = list_expr->root->value.v_text;
word->ex_words[word->ex_count].text.loc = list_expr->root->loc;
word->ex_count++;
}
return OG_SUCCESS;
}
static status_t sql_try_create_pl_collection_method_expr(sql_stmt_t *stmt, pl_collection_elem_expr_arg_t *arg,
bool32 *converted)
{
const uint32 min_func_name_count = 2;
word_t word = { 0 };
expr_node_t saved_node;
*converted = OG_FALSE;
if (!sql_func_is_pl_compile_context(stmt) || stmt->pl_compiler == NULL ||
arg->func_name->count < min_func_name_count) {
return OG_SUCCESS;
}
OG_RETURN_IFERR(sql_bison_func_name_as_word(arg->func_name, &word, arg->loc));
saved_node = *arg->node;
OG_RETURN_IFERR(plc_try_obj_access_single(stmt, &word, arg->node));
if (arg->node->type != EXPR_NODE_V_METHOD) {
*arg->node = saved_node;
return OG_SUCCESS;
}
arg->node->argument = arg->arg_list;
*converted = OG_TRUE;
return OG_SUCCESS;
}
static status_t sql_try_create_pl_special_func_expr(sql_stmt_t *stmt, expr_tree_t **expr,
pl_collection_elem_expr_arg_t *arg, bool32 *converted)
{
OG_RETURN_IFERR(sql_try_create_pl_collection_elem_expr(stmt, arg, converted));
if (*converted) {
(*expr)->root = arg->node;
return OG_SUCCESS;
}
OG_RETURN_IFERR(sql_try_create_pl_construct_expr(stmt, arg, converted));
if (*converted) {
(*expr)->root = arg->node;
return OG_SUCCESS;
}
OG_RETURN_IFERR(sql_try_create_pl_collection_method_expr(stmt, arg, converted));
if (*converted) {
(*expr)->root = arg->node;
}
return OG_SUCCESS;
}
status_t sql_create_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, galist_t *func_name,
expr_tree_t *arg_list, source_location_t loc)
{
expr_node_t *node = NULL;
bool32 converted = OG_FALSE;
pl_collection_elem_expr_arg_t pl_arg = { 0 };
if (sql_init_expr_node(stmt, expr, &node, OG_TYPE_INTEGER, EXPR_NODE_FUNC, loc) != OG_SUCCESS) {
return OG_ERROR;
}
text_t user;
expr_tree_t *list_expr;
user.str = stmt->session->db_user;
user.len = (uint32)strlen(stmt->session->db_user);
node->word.func.user_func_first = OG_FALSE;
for (int i = 0; i < func_name->count; i++) {
list_expr = cm_galist_get(func_name, i);
if (list_expr->root->type != EXPR_NODE_CONST || list_expr->root->value.type != OG_TYPE_CHAR) {
OG_SRC_THROW_ERROR(loc, ERR_SQL_SYNTAX_ERROR, "Invalid funcname expr");
return OG_ERROR;
}
}
if (func_name->count == 1) {
list_expr = cm_galist_get(func_name, 0);
text_t text_word = list_expr->root->value.v_text;
if (sql_self_func_configed(&user, &text_word)) {
node->word.func.user_func_first = OG_TRUE;
}
}
OG_RETURN_IFERR(sql_expr_list_as_func(stmt, func_name, &node->word));
pl_arg.node = node;
pl_arg.func_name = func_name;
pl_arg.arg_list = arg_list;
pl_arg.loc = loc;
OG_RETURN_IFERR(sql_try_create_pl_special_func_expr(stmt, expr, &pl_arg, &converted));
if (converted) {
return OG_SUCCESS;
}
* 1. if call by user.func, build as user func.
* 2. function is in udf.ini, build as user func first, such as SLEEP, "sleep", so compare case sensitive.
*/
if (node->word.func.user.len > 0 || sql_self_func_configed_direct(&user, &node->word.func.name.value)) {
node->word.func.user_func_first = OG_TRUE;
}
node->argument = arg_list;
(*expr)->root = node;
return OG_SUCCESS;
}
status_t sql_build_winsort_node_bison(sql_stmt_t *stmt, winsort_args_t **winsort_args, galist_t* group_exprs,
galist_t *sort_items, windowing_args_t *windowing, source_location_t loc)
{
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(winsort_args_t), (void **)winsort_args));
(*winsort_args)->group_exprs = group_exprs;
(*winsort_args)->sort_items = sort_items;
(*winsort_args)->windowing = windowing;
return OG_SUCCESS;
}
status_t sql_create_winsort_node_bison(sql_stmt_t *stmt, expr_tree_t *func_expr, expr_node_t *func_node,
winsort_args_t *winsort_args, source_location_t loc)
{
expr_tree_t *n_expr = NULL;
expr_node_t *over_node = NULL;
sql_text_t over_test;
if (winsort_args != NULL) {
OG_RETURN_IFERR(sql_create_expr(stmt, &n_expr));
APPEND_CHAIN(&n_expr->chain, func_node);
n_expr->root = func_node;
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&over_node));
over_node->owner = func_expr;
over_node->type = EXPR_NODE_OVER;
over_node->unary = func_expr->unary;
over_node->loc = loc;
over_node->argument = n_expr;
over_test.str = "over";
over_test.len = strlen("over");
over_test.loc = loc;
OG_RETURN_IFERR(sql_copy_object_name_loc(stmt->context, WORD_TYPE_UNKNOWN, &over_test,
&over_node->word.func.name));
over_node->word.func.args.value = CM_NULL_TEXT;
over_node->word.func.args.loc = loc;
over_node->word.func.pack.value = CM_NULL_TEXT;
over_node->word.func.pack.loc = loc;
over_node->word.func.user.value = CM_NULL_TEXT;
over_node->word.func.user.loc = loc;
if (winsort_args->sort_items == NULL && winsort_args->group_exprs == NULL && winsort_args->windowing == NULL) {
expr_tree_t *expr = NULL;
OG_RETURN_IFERR(sql_create_list(stmt, &winsort_args->group_exprs));
OG_RETURN_IFERR(sql_create_const_expr_false(stmt, &expr, NULL, 0));
OG_RETURN_IFERR(cm_galist_insert(winsort_args->group_exprs, expr));
}
over_node->win_args = winsort_args;
APPEND_CHAIN(&func_expr->chain, over_node);
} else {
APPEND_CHAIN(&func_expr->chain, func_node);
}
sql_generate_expr(func_expr);
return OG_SUCCESS;
}
status_t sql_create_windowing_arg(sql_stmt_t *stmt, windowing_args_t **windowing_args,
windowing_border_t *l_border, windowing_border_t *r_border)
{
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(windowing_args_t), (void **)windowing_args) != OG_SUCCESS);
(*windowing_args)->l_expr = l_border->expr;
(*windowing_args)->l_type = l_border->border_type;
if (r_border != NULL) {
(*windowing_args)->r_expr = r_border->expr;
(*windowing_args)->r_type = r_border->border_type;
} else {
(*windowing_args)->r_type = WB_TYPE_CURRENT_ROW;
}
return OG_SUCCESS;
}
static status_t sql_create_special_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, expr_node_t **node,
char *func_name, source_location_t loc)
{
if (sql_init_expr_node(stmt, expr, node, OG_TYPE_INTEGER, EXPR_NODE_FUNC, loc) != OG_SUCCESS) {
return OG_ERROR;
}
(*node)->word.func.user_func_first = OG_FALSE;
sql_text_t text;
text.str = func_name;
text.len = strlen(func_name);
text.loc = loc;
if (sql_copy_object_name_loc(stmt->context, WORD_TYPE_UNKNOWN, &text, &(*node)->word.func.name) != OG_SUCCESS) {
return OG_ERROR;
}
return OG_SUCCESS;
}
status_t sql_create_cast_convert_expr(sql_stmt_t *stmt, expr_tree_t **expr, expr_tree_t *arg, type_word_t *type,
char *func_name, source_location_t loc)
{
expr_tree_t *type_expr = NULL;
expr_node_t *node = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, func_name, loc) != OG_SUCCESS) {
return OG_ERROR;
}
node->argument = arg;
if (sql_create_expr(stmt, &type_expr) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&type_expr->root) != OG_SUCCESS) {
return OG_ERROR;
}
if (sql_build_type_expr(stmt, type, type_expr) != OG_SUCCESS) {
return OG_ERROR;
}
node->argument->next = type_expr;
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_if_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, cond_tree_t *cond_tree,
expr_tree_t *first_arg, expr_tree_t *second_arg, source_location_t loc)
{
expr_node_t *node = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, "if", loc) != OG_SUCCESS) {
return OG_ERROR;
}
node->cond_arg = cond_tree;
node->argument = first_arg;
node->argument->next = second_arg;
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_lnnvl_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, cond_tree_t *cond_tree,
source_location_t loc)
{
expr_node_t *node = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, "lnnvl", loc) != OG_SUCCESS) {
return OG_ERROR;
}
node->cond_arg = cond_tree;
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_trim_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, trim_list_t *trim,
func_trim_type_t trim_type, source_location_t loc)
{
expr_node_t *node = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, "trim", loc) != OG_SUCCESS) {
return OG_ERROR;
}
node->ext_args = trim_type;
if (trim->reverse) {
node->argument = trim->second_expr;
node->argument->next = trim->first_expr;
} else {
node->argument = trim->first_expr;
node->argument->next = trim->second_expr;
}
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_groupconcat_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, expr_tree_t *expr_list,
galist_t *sort_list, char *separator, source_location_t loc)
{
expr_node_t *node = NULL;
expr_tree_t *arg_sep = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, "group_concat", loc) != OG_SUCCESS) {
return OG_ERROR;
}
bool32 has_separator = separator != NULL;
node->sort_items = sort_list;
OG_RETURN_IFERR(sql_create_const_string_expr(stmt, &arg_sep, has_separator ? separator : ","));
arg_sep->next = expr_list;
node->argument = arg_sep;
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_substr_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, expr_tree_t *arg_list,
char *func_name, source_location_t loc)
{
expr_node_t *node = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, func_name, loc) != OG_SUCCESS) {
return OG_ERROR;
}
node->argument = arg_list;
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_extract_funccall_expr(sql_stmt_t *stmt, expr_tree_t **expr, extract_list_t *extract_list,
source_location_t loc)
{
expr_node_t *node = NULL;
expr_tree_t *arg_expr = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, "extract", loc) != OG_SUCCESS) {
return OG_ERROR;
}
OG_RETURN_IFERR(sql_create_columnref_expr(stmt, &arg_expr, extract_list->extract_type, NULL,
EXPR_NODE_COLUMN, loc));
node->argument = arg_expr;
node->argument->next = extract_list->arg;
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
status_t sql_create_json_func_expr(sql_stmt_t *stmt, expr_tree_t **expr, expr_tree_t *arg_list,
char *func_name, json_func_attr_t attr, source_location_t loc)
{
expr_node_t *node = NULL;
if (sql_create_special_funccall_expr(stmt, expr, &node, func_name, loc) != OG_SUCCESS) {
return OG_ERROR;
}
node->argument = arg_list;
node->json_func_attr = attr;
if (cm_strcmpi(func_name, "json_array") == 0 || cm_strcmpi(func_name, "json_object") == 0 ||
cm_strcmpi(func_name, "json_query") == 0 || cm_strcmpi(func_name, "json_mergepatch") == 0 ||
cm_strcmpi(func_name, "jsonb_query") == 0 || cm_strcmpi(func_name, "jsonb_mergepatch") == 0) {
node->format_json = OG_TRUE;
} else {
node->format_json = OG_FALSE;
}
APPEND_CHAIN(&((*expr)->chain), node);
sql_generate_expr(*expr);
return OG_SUCCESS;
}
#ifdef __cplusplus
}
#endif