* This file is part of the oGRAC project.
* Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved.
*
* 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_distinct_rewrite.c
*
*
* IDENTIFICATION
* src/ctsql/optimizer/ogsql_distinct_rewrite.c
*
* -------------------------------------------------------------------------
*/
#include "ogsql_distinct_rewrite.h"
#include "srv_instance.h"
#include "ogsql_hint_verifier.h"
#include "dml_parser.h"
#include "ogsql_optim_common.h"
#include "ogsql_stmt.h"
#include "ogsql_cond_rewrite.h"
static bool32 check_query_distinct_eliminated(sql_stmt_t *statement, sql_query_t *qry)
{
OG_RETVALUE_IFTRUE(!qry->has_distinct && !(qry->owner != NULL && qry->owner->type == SELECT_AS_LIST), OG_FALSE);
OG_RETVALUE_IFTRUE(ROWNUM_COND_OCCUR(qry->cond), OG_FALSE);
galist_t *rs_columns = NULL;
if (qry->has_distinct) {
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] Parent query has distinct.");
rs_columns = qry->distinct_columns;
} else {
rs_columns = qry->rs_columns;
}
uint32 idx = 0;
while (idx < rs_columns->count) {
rs_column_t *rs_column = (rs_column_t *)cm_galist_get(rs_columns, idx++);
if (rs_column->type == RS_COL_COLUMN) {
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] The query column must be RS_COL_COLUMN.");
continue;
}
if (!sql_is_const_expr_tree(rs_column->expr) && !TREE_IS_BINDING_PARAM(rs_column->expr)) {
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] RS_COL_CALC is not const or binding param.");
return OG_FALSE;
}
}
return OG_TRUE;
}
typedef struct {
int (*check_func)(const sql_query_t *sub_qry);
const char *err_msg;
} DistinctElimCheckItem;
static int check_has_distinct(const sql_query_t *sub_qry)
{
return !sub_qry->has_distinct;
}
static int check_group_sets(const sql_query_t *sub_qry)
{
return sub_qry->group_sets->count != 0;
}
static int check_winsort_list(const sql_query_t *sub_qry)
{
return sub_qry->winsort_list->count != 0;
}
static int check_limit_clause(const sql_query_t *sub_qry)
{
return LIMIT_CLAUSE_OCCUR(&sub_qry->limit);
}
static int check_rownum_cond(const sql_query_t *sub_qry)
{
return ROWNUM_COND_OCCUR(sub_qry->cond);
}
static bool32 check_subquery_distinct_eliminatable(sql_query_t *sub_qry)
{
const DistinctElimCheckItem check_items[] = {
{check_has_distinct, "[DISTINCT_ELIMATE] Subquery have no distinct."},
{check_group_sets, "[DISTINCT_ELIMATE] Subquery can't have group sets."},
{check_winsort_list, "[DISTINCT_ELIMATE] Subquery can't have window sort."},
{check_limit_clause, "[DISTINCT_ELIMATE] Subquery can't have limit."},
{check_rownum_cond, "[DISTINCT_ELIMATE] Subquery can't have rownum condition."}
};
const int item_count = sizeof(check_items) / sizeof(check_items[0]);
for (int i = 0; i < item_count; ++i) {
const DistinctElimCheckItem *item = &check_items[i];
if (item->check_func(sub_qry)) {
OG_LOG_DEBUG_INF("%s", item->err_msg);
return OG_FALSE;
}
}
return OG_TRUE;
}
static inline status_t check_failed_handler(bool32 check_result, const char *err_msg)
{
if (!check_result) {
OG_LOG_DEBUG_INF("%s", err_msg);
return OG_SUCCESS;
}
return OG_ERROR;
}
static status_t process_subquery_sort_items(sql_stmt_t *statement, sql_query_t *sub_qry)
{
SET_NODE_STACK_CURR_QUERY(statement, sub_qry);
uint32 idx = 0;
while (idx < sub_qry->sort_items->count) {
sort_item_t *item = (sort_item_t *)cm_galist_get(sub_qry->sort_items, idx++);
if (replace_group_expr_node(statement, &item->expr->root) != OG_SUCCESS) {
OG_LOG_RUN_ERR("[DISTINCT_ELIMATE] replace_group_expr_node failed.");
SQL_RESTORE_NODE_STACK(statement);
return OG_ERROR;
}
}
SQL_RESTORE_NODE_STACK(statement);
return OG_SUCCESS;
}
static void do_eliminate_subquery_distinct(sql_query_t *sub_qry)
{
SWAP(galist_t *, sub_qry->rs_columns, sub_qry->distinct_columns);
cm_galist_reset(sub_qry->distinct_columns);
sub_qry->has_distinct = OG_FALSE;
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] Success on sub query %p", sub_qry);
}
static status_t og_eliminate_query_distinct(sql_stmt_t *statement, sql_query_t *qry, sql_query_t *sub_qry)
{
status_t ret = check_failed_handler(check_query_distinct_eliminated(statement, qry),
"[DISTINCT_ELIMATE] Query is not eliminatable.");
if (ret == OG_SUCCESS) {
return OG_SUCCESS;
}
ret = check_failed_handler(check_subquery_distinct_eliminatable(sub_qry),
"[DISTINCT_ELIMATE] Subquery is not eliminatable.");
if (ret == OG_SUCCESS) {
return OG_SUCCESS;
}
if (process_subquery_sort_items(statement, sub_qry) != OG_SUCCESS) {
return OG_ERROR;
}
do_eliminate_subquery_distinct(sub_qry);
return OG_SUCCESS;
}
status_t og_transf_eliminate_distinct(sql_stmt_t *statement, sql_query_t *qry)
{
if (!ogsql_opt_param_is_enable(statement, g_instance->sql.enable_distinct_elimination, OPT_DISTINCT_ELIMINATION)) {
OG_LOG_DEBUG_INF("_OPTIM_DISTINCT_ELIMINATION has been shutted");
return OG_SUCCESS;
}
if (sql_stack_safe(statement) != OG_SUCCESS) {
OG_LOG_RUN_ERR("[DISTINCT_ELIMATE] sql_stack_safe failed.");
return OG_ERROR;
}
uint32 idx = 0;
while (idx < qry->tables.count) {
sql_table_t *tbl = (sql_table_t *)sql_array_get(&qry->tables, idx++);
if (tbl == NULL) {
OG_LOG_RUN_ERR("[DISTINCT_ELIMATE] table is NULL.");
return OG_ERROR;
}
if (tbl->type != VIEW_AS_TABLE && tbl->type != SUBSELECT_AS_TABLE) {
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] Skip tables[%s] type:%d, not view/sub query.", T2S(&(tbl->name)),
tbl->type);
continue;
}
if (tbl->subslct_tab_usage != SUBSELECT_4_NORMAL_JOIN) {
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] Skip tables[%s] sub_usage:%d, not normal join.", T2S(&(tbl->name)),
tbl->subslct_tab_usage);
continue;
}
if (tbl->select_ctx->root->type != SELECT_NODE_QUERY) {
OG_LOG_DEBUG_INF("[DISTINCT_ELIMATE] Skip tables[%s] root_type:%d, not plain query.", T2S(&(tbl->name)),
tbl->select_ctx->root->type);
continue;
}
sql_query_t *sub_qry = tbl->select_ctx->first_query;
if (og_transf_eliminate_distinct(statement, sub_qry) != OG_SUCCESS) {
OG_LOG_RUN_ERR("[DISTINCT_ELIMATE] og_transf_eliminate_distinct failed.");
return OG_ERROR;
}
if (og_eliminate_query_distinct(statement, qry, sub_qry) != OG_SUCCESS) {
OG_LOG_RUN_ERR("[DISTINCT_ELIMATE] og_eliminate_query_distinct failed.");
return OG_ERROR;
}
}
return OG_SUCCESS;
}