* 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_plan.h
*
*
* IDENTIFICATION
* src/ogsql/plan/ogsql_plan.h
*
* -------------------------------------------------------------------------
*/
#ifndef __SQL_PLAN_H__
#define __SQL_PLAN_H__
#include "ogsql_plan_defs.h"
#include "ogsql_func.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SAMP_MOD_RATE_THRESHOLD 0.8
static inline expr_node_t *get_like_first_node(expr_node_t *node)
{
if (node->type == EXPR_NODE_FUNC) {
sql_func_t *func = sql_get_func(&node->value.v_func);
if (func->builtin_func_id == ID_FUNC_ITEM_CONCAT || func->builtin_func_id == ID_FUNC_ITEM_CONCAT_WS) {
return node->argument->root;
}
} else {
while (node->type == EXPR_NODE_CAT) {
node = node->left;
}
}
return node;
}
static inline bool32 sql_expr_column_matched(expr_tree_t *expr, var_column_t *column)
{
expr_node_t *node = expr->root;
if (node->type != EXPR_NODE_COLUMN || expr->next != NULL) {
return OG_FALSE;
}
var_column_t *expr_column = VALUE_PTR(var_column_t, &node->value);
return (expr_column->ancestor == 0) && (expr_column->tab == column->tab) && (expr_column->col == column->col);
}
static inline bool32 sql_expr_node_matched(sql_stmt_t *stmt, expr_tree_t *expr, expr_node_t *r_node)
{
expr_node_t *l_node = expr->root;
if (l_node->type != r_node->type || expr->next != NULL) {
return OG_FALSE;
}
return sql_expr_node_equal(stmt, l_node, r_node, NULL);
}
static inline cmp_type_t sql_reverse_cmp(cmp_type_t cmp_type)
{
switch (cmp_type) {
case CMP_TYPE_LESS:
return CMP_TYPE_GREAT;
case CMP_TYPE_LESS_EQUAL:
return CMP_TYPE_GREAT_EQUAL;
case CMP_TYPE_GREAT:
return CMP_TYPE_LESS;
case CMP_TYPE_GREAT_EQUAL:
return CMP_TYPE_LESS_EQUAL;
case CMP_TYPE_EQUAL:
default:
return CMP_TYPE_EQUAL;
}
}
#define HAS_ONLY_NL_OPER(flag) \
(((flag) == JOIN_OPER_NL) || ((flag) == JOIN_OPER_NL_LEFT) || ((flag) == JOIN_OPER_NL_BATCH))
static inline bool32 chk_tab_with_oper_map(plan_assist_t *pa, uint16 tab1, uint16 tab2)
{
if (pa->join_oper_map == NULL) {
return OG_TRUE;
}
uint32 step = pa->table_count;
uint8 oper_flag = pa->join_oper_map[step * tab1 + tab2];
return HAS_ONLY_NL_OPER(oper_flag);
}
static inline bool32 sql_reserved_word_indexable(plan_assist_t *pa, expr_node_t *node, uint32 tab_id)
{
uint32 tab;
switch (VALUE(uint32, &node->value)) {
case RES_WORD_SYSDATE:
case RES_WORD_SYSTIMESTAMP:
case RES_WORD_CURDATE:
case RES_WORD_CURTIMESTAMP:
case RES_WORD_LOCALTIMESTAMP:
case RES_WORD_UTCTIMESTAMP:
case RES_WORD_NULL:
case RES_WORD_TRUE:
case RES_WORD_FALSE:
return OG_TRUE;
case RES_WORD_ROWID:
if (pa == NULL) {
return OG_FALSE;
}
if (node->value.v_rid.ancestor > 0) {
pa->col_use_flag |= USE_ANCESTOR_COL;
pa->max_ancestor = MAX(node->value.v_rid.ancestor, pa->max_ancestor);
return OG_TRUE;
}
tab = node->value.v_rid.tab_id;
if (pa->tables[tab]->plan_id < pa->tables[tab_id]->plan_id) {
if (!chk_tab_with_oper_map(pa, tab, tab_id)) {
return OG_FALSE;
}
pa->col_use_flag |= USE_SELF_JOIN_COL;
return OG_TRUE;
} else if (pa->tables[tab]->plan_id == pa->tables[tab_id]->plan_id) {
return tab < tab_id;
}
return OG_FALSE;
default:
return OG_FALSE;
}
}
static inline bool32 is_analyzed_table(sql_stmt_t *stmt, sql_table_t *table)
{
if (OG_IS_SUBSELECT_TABLE(table->type)) {
return OG_FALSE;
}
return IS_ANALYZED_TABLE(table);
}
status_t sql_create_dml_plan(sql_stmt_t *stmt);
status_t sql_check_table_indexable(sql_stmt_t *stmt, plan_assist_t *pa, sql_table_t *table, cond_tree_t *cond);
status_t sql_create_table_scan_plan(sql_stmt_t *stmt, plan_assist_t *pa, cond_tree_t *cond, sql_table_t *table,
plan_node_t **plan);
status_t sql_get_rowid_cost(sql_stmt_t *stmt, plan_assist_t *pa, cond_node_t *cond, sql_table_t *table, bool32 is_temp);
status_t sql_make_index_col_map(plan_assist_t *pa, sql_stmt_t *stmt, sql_table_t *table);
status_t sql_generate_insert_plan(sql_stmt_t *stmt, sql_insert_t *insert_ctx, plan_assist_t *parent);
status_t sql_generate_merge_into_plan(sql_stmt_t *stmt, sql_merge_t *merge_ctx, plan_assist_t *parent);
status_t sql_generate_delete_plan(sql_stmt_t *stmt, sql_delete_t *delete_ctx, plan_assist_t *plan_ass);
status_t sql_generate_select_plan(sql_stmt_t *stmt, sql_select_t *select_ctx, plan_assist_t *plan_ass);
status_t sql_generate_update_plan(sql_stmt_t *stmt, sql_update_t *update_ctx, plan_assist_t *parent);
status_t sql_create_subselect_plan(sql_stmt_t *stmt, sql_query_t *query, plan_assist_t *plan_ass);
void check_table_stats(sql_stmt_t *stmt);
void cbo_unset_select_node_table_flag(select_node_t *select_node, uint32 cbo_flag, bool32 recurs);
void cbo_set_select_node_table_flag(select_node_t *select_node, uint32 cbo_flag, bool32 recurs);
status_t rbo_table_indexable(sql_stmt_t *stmt, plan_assist_t *pa, sql_table_t *table, cond_node_t *cond,
bool32 *result);
status_t rbo_choose_full_scan_index(sql_stmt_t *stmt, plan_assist_t *pa, sql_table_t *table, bool32 chk_sort);
void sql_prepare_query_plan(sql_stmt_t *stmt, plan_assist_t *plan_ass, sql_query_t *query, sql_node_type_t type,
plan_assist_t *parent);
void reset_select_node_cbo_status(select_node_t *node);
void sql_collect_select_nodes(biqueue_t *queue, select_node_t *node);
typedef status_t (*query_visit_func_t)(sql_stmt_t *stmt, sql_query_t *query);
status_t visit_select_node(sql_stmt_t *stmt, select_node_t *node, query_visit_func_t visit_func);
void sql_init_plan_assist(sql_stmt_t *stmt, plan_assist_t *plan_ass, sql_query_t *query, sql_node_type_t type,
plan_assist_t *parent);
uint32 get_query_plan_flag(sql_query_t *query);
plan_assist_t *sql_get_ancestor_pa(plan_assist_t *curr_pa, uint32 temp_ancestor);
sql_query_t *sql_get_ancestor_query(sql_query_t *query, uint32 anc);
status_t can_rewrite_by_check_index(sql_stmt_t *stmt, sql_query_t *query, ck_type_t cktype, bool32 *result);
status_t rbo_check_index_4_rewrite(sql_stmt_t *stmt, sql_query_t *query, bool32 *result);
plan_node_type_t sql_get_group_plan_type(sql_stmt_t *stmt, sql_query_t *query);
status_t add_cbo_depend_tab(sql_stmt_t *stmt, sql_table_t *table, uint32 tab_no);
uint32 sql_get_plan_hash_rows(sql_stmt_t *stmt, plan_node_t *plan);
status_t sql_create_query_plan_ex(sql_stmt_t *stmt, sql_query_t *query, plan_assist_t *plan_ass, plan_node_t **plan);
bool32 sql_can_hash_mtrl_support_aggtype(sql_stmt_t *stmt, sql_query_t *query);
status_t sql_clone_join_root(sql_stmt_t *stmt, void *ogx, sql_join_node_t *src_join_root,
sql_join_node_t **dst_root, sql_array_t *tables, ga_alloc_func_t alloc_mem_func);
void swap_join_tree_child_node(plan_assist_t *plan_ass, sql_join_node_t *join_root);
bool32 if_is_drive_table(sql_join_node_t *join_node, uint16 table);
void clear_query_cbo_status(sql_query_t *query);
status_t build_query_join_tree(sql_stmt_t *stmt, sql_query_t *query, plan_assist_t *parent, sql_join_node_t **ret_root,
uint32 driver_table_count);
uint32 sql_calc_rownum(sql_stmt_t *stmt, sql_query_t *query);
status_t perfect_tree_and_gen_oper_map(plan_assist_t *pa, uint32 step, sql_join_node_t *join_node);
status_t sql_dynamic_sampling_table_stats(sql_stmt_t *stmt, plan_assist_t *pa);
bool32 sql_query_has_hash_join(sql_query_t *query);
status_t clone_tables_4_subqry(sql_stmt_t *stmt, sql_query_t *query, sql_query_t *sub_query);
void sql_init_plan_assist_impl(sql_stmt_t *stmt, plan_assist_t *plan_ass, sql_query_t *query, sql_node_type_t type,
plan_assist_t *parent);
sql_table_t *sql_get_driver_table(plan_assist_t *plan_ass);
status_t remove_pushed_down_join_cond(sql_stmt_t *stmt, plan_assist_t *pa, sql_array_t *tables);
status_t og_check_index_4_rewrite(sql_stmt_t *statement, sql_query_t *qry, ck_type_t type, bool32 *result);
#define IS_REVERSE_INDEX_AVAILABLE(match_mode, idx_col_id) \
((match_mode) == COLUMN_MATCH_POINT || ((match_mode) == COLUMN_MATCH_LIST && (idx_col_id) == 0))
#define SCALE_BY_LIMIT_EXCL \
(EX_QUERY_WINSORT | EX_QUERY_HAVING | EX_QUERY_CUBE | EX_QUERY_PIVOT | EX_QUERY_FOR_UPDATE | EX_QUERY_SIBL_SORT | \
EX_QUERY_CONNECT)
#define SCALE_BY_ROWNUM_EXCL (EX_QUERY_SIBL_SORT | EX_QUERY_CONNECT)
#define MAX_NL_FULL_DUPL_PLAN_COUNT 4
#define IS_EQUAL_TYPE(join_type) \
((join_type) == JOIN_TYPE_INNER || (join_type) == JOIN_TYPE_COMMA || (join_type) == JOIN_TYPE_CROSS || \
(join_type) == JOIN_TYPE_FULL)
#define IS_INNER_TYPE(join_type) \
((join_type) == JOIN_TYPE_INNER || (join_type) == JOIN_TYPE_COMMA || (join_type) == JOIN_TYPE_CROSS)
#define IS_INNER_JOIN(join_root) IS_INNER_TYPE((join_root)->type)
#define IS_LEFT_JOIN(join_root) ((join_root)->type == JOIN_TYPE_LEFT)
#define IS_FULL_JOIN(join_root) ((join_root)->type == JOIN_TYPE_FULL)
#define IS_SEMI_JOIN(join_root) \
((join_root)->oper == JOIN_OPER_HASH_ANTI || (join_root)->oper == JOIN_OPER_HASH_ANTI_NA || \
(join_root)->oper == JOIN_OPER_HASH_SEMI)
static inline void sql_plan_assist_set_table(plan_assist_t *pa, sql_table_t *table)
{
table->plan_id = pa->plan_count;
* table in jpath which is parameterized path has been copied from table in query,
* so we need to set plan_id of table in query too.
*/
((sql_table_t *)sql_array_get(&pa->query->tables, table->id))->plan_id = table->plan_id;
pa->plan_tables[pa->plan_count++] = table;
if (table->sub_tables != NULL) {
for (uint32 i = 0; i < table->sub_tables->count; i++) {
sql_table_t *sub_table = (sql_table_t *)cm_galist_get(table->sub_tables, i);
sub_table->plan_id = table->plan_id;
}
}
}
static inline status_t sql_create_concate_key(sql_stmt_t *stmt, galist_t *keys, sql_table_t *table)
{
expr_tree_t *rowid = NULL;
OG_RETURN_IFERR(cm_galist_new(keys, sizeof(expr_tree_t), (pointer_t *)&rowid));
rowid->owner = stmt->context;
OG_RETURN_IFERR(sql_alloc_mem(stmt->context, sizeof(expr_node_t), (void **)&rowid->root));
rowid->root->size = ROWID_LENGTH;
rowid->root->type = EXPR_NODE_RESERVED;
rowid->root->datatype = OG_TYPE_STRING;
rowid->root->value.type = OG_TYPE_INTEGER;
rowid->root->value.v_rid.res_id = RES_WORD_ROWID;
rowid->root->value.v_rid.tab_id = table->id;
rowid->root->value.v_rid.ancestor = 0;
return OG_SUCCESS;
}
static inline void sql_convert_to_left_join(sql_join_node_t *join_node)
{
if (join_node->type <= JOIN_TYPE_LEFT) {
return;
}
join_node->type = JOIN_TYPE_LEFT;
join_node->oper = join_node->oper == JOIN_OPER_HASH_FULL ? JOIN_OPER_HASH_LEFT : JOIN_OPER_NL_LEFT;
}
static inline void sql_convert_to_left_or_inner_join(sql_join_node_t *join_node, sql_join_assist_t *join_assist)
{
if (join_node->type < JOIN_TYPE_LEFT) {
return;
}
if (join_node->type == JOIN_TYPE_LEFT) {
join_node->type = JOIN_TYPE_INNER;
join_assist->outer_node_count--;
join_node->oper = join_node->oper == JOIN_OPER_HASH_LEFT ? JOIN_OPER_HASH : JOIN_OPER_NL;
return;
}
join_node->type = JOIN_TYPE_LEFT;
join_node->oper = join_node->oper == JOIN_OPER_HASH_FULL ? JOIN_OPER_HASH_LEFT : JOIN_OPER_NL_LEFT;
SWAP(sql_join_node_t *, join_node->left, join_node->right);
}
#define IF_LOCK_IN_FETCH(query) \
((query)->tables.count == 1 || (query)->connect_by_cond != NULL || (query)->incl_flags & (EXPR_INCL_ROWNUM))
#ifdef __cplusplus
}
#endif
#endif