2028 lines
90 KiB
C++
2028 lines
90 KiB
C++
/**
|
|
* Copyright (c) 2021 OceanBase
|
|
* OceanBase CE is licensed under Mulan PubL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
|
* You may obtain a copy of Mulan PubL v2 at:
|
|
* http://license.coscl.org.cn/MulanPubL-2.0
|
|
* 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 PubL v2 for more details.
|
|
*/
|
|
|
|
#define USING_LOG_PREFIX SQL_REWRITE
|
|
#include "ob_transform_win_magic.h"
|
|
#include "ob_transform_utils.h"
|
|
#include "sql/resolver/expr/ob_raw_expr_util.h"
|
|
#include "share/schema/ob_table_schema.h"
|
|
#include "sql/optimizer/ob_optimizer_util.h"
|
|
#include "sql/ob_sql_context.h"
|
|
#include "sql/rewrite/ob_stmt_comparer.h"
|
|
#include "common/ob_smart_call.h"
|
|
#include "sql/rewrite/ob_equal_analysis.h"
|
|
|
|
using namespace oceanbase::sql;
|
|
using namespace oceanbase::common;
|
|
using namespace oceanbase::share::schema;
|
|
|
|
/**
|
|
* The origin win magic implements the paper <WinMagic: Subquery Elimination Using Window Aggregation>
|
|
* And the subquery is in where condition in the paper.
|
|
* Now we try realizing it in another way. The subquery is not in where condition, while the from item is view.
|
|
* Thus we can merge some rewrite rules, and make the rewrite path more clear.
|
|
* @brief ObTransformWinMagic::transform_one_stmt
|
|
* @return
|
|
*/
|
|
|
|
int ObTransformWinMagic::transform_one_stmt(common::ObIArray<ObParentDMLStmt> &parent_stmts,
|
|
ObDMLStmt *&stmt,
|
|
bool &trans_happened)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t drill_down_idx = -1;
|
|
int64_t roll_up_idx = -2; // -1 means main stmt
|
|
ObSEArray<TableItem *, 4> trans_tables;
|
|
SMART_VARS_2((ObStmtCompareContext, context), (ObStmtMapInfo, map_info)) {
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(stmt->get_query_ctx())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt is null", K(ret), K(stmt));
|
|
} else if (OB_FAIL(get_view_to_trans(stmt, drill_down_idx, roll_up_idx, context, map_info, trans_tables))) {
|
|
LOG_WARN("get view to trans failed", K(ret));
|
|
} else if (drill_down_idx == -1 || roll_up_idx == -2) {
|
|
// no valid item to do trans
|
|
} else if (OB_FAIL(do_transform(
|
|
parent_stmts, stmt, drill_down_idx, roll_up_idx, map_info, context, trans_happened, trans_tables))) {
|
|
LOG_WARN("do win magic transform for from failed", K(ret));
|
|
}
|
|
} // end smart vars
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_hint_valid(ObDMLStmt &stmt, TableItem &table, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
const ObQueryHint *query_hint = NULL;
|
|
const ObWinMagicHint *hint = static_cast<const ObWinMagicHint*>(get_hint(stmt.get_stmt_hint()));
|
|
if (NULL == hint) {
|
|
is_valid = true;
|
|
LOG_TRACE("check win magic hint is null", K(is_valid), K(table), K(stmt.get_stmt_hint()));
|
|
} else if (OB_ISNULL(query_hint = stmt.get_stmt_hint().query_hint_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret), K(query_hint));
|
|
} else {
|
|
is_valid = hint->enable_win_magic(query_hint->cs_type_, table);
|
|
LOG_TRACE("succeed to check win magic hint valid", K(is_valid), K(table), K(*hint));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::construct_transform_hint(ObDMLStmt &stmt, void *trans_params)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
typedef ObSEArray<TableItem *, 4> single_or_joined_table;
|
|
ObIArray<single_or_joined_table> *win_magic_tables = NULL;
|
|
ObWinMagicHint *hint = NULL;
|
|
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(ctx_->allocator_) || OB_ISNULL(trans_params) ||
|
|
OB_ISNULL(win_magic_tables = static_cast<ObIArray<single_or_joined_table>*>(trans_params)) ||
|
|
OB_UNLIKELY(win_magic_tables->count() < 2)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected null", K(ret), K(ctx_), K(win_magic_tables));
|
|
} else if (OB_FAIL(ObQueryHint::create_hint(ctx_->allocator_, T_WIN_MAGIC, hint))) {
|
|
LOG_WARN("failed to create hint", K(ret));
|
|
} else if (OB_FAIL(ctx_->outline_trans_hints_.push_back(hint))) {
|
|
LOG_WARN("failed to push back hint", K(ret));
|
|
} else if (OB_FAIL(ctx_->add_used_trans_hint(get_hint(stmt.get_stmt_hint())))) {
|
|
LOG_WARN("failed to add used trans hint", K(ret));
|
|
} else {
|
|
hint->set_qb_name(ctx_->src_qb_name_);
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < win_magic_tables->count(); ++i) {
|
|
for (int64_t j = 0; OB_SUCC(ret) && j < win_magic_tables->at(i).count(); ++j) {
|
|
ObTableInHint *hint_table = NULL;
|
|
TableItem *table = win_magic_tables->at(i).at(j);
|
|
if (OB_ISNULL(table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret));
|
|
} else if (OB_FAIL(hint->get_tb_name_list().push_back(ObTableInHint(*table)))) {
|
|
LOG_WARN("failed to push back hint table", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::construct_trans_table(const ObDMLStmt *stmt,
|
|
const TableItem *table,
|
|
ObIArray<ObSEArray<TableItem *, 4>> &trans_basic_tables)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<TableItem *, 4> trans_table;
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret));
|
|
} else if (table->is_joined_table()) {
|
|
const JoinedTable *joined_table = static_cast<const JoinedTable *>(table);
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < joined_table->single_table_ids_.count(); ++i) {
|
|
TableItem *table = stmt->get_table_item_by_id(joined_table->single_table_ids_.at(i));
|
|
if (OB_ISNULL(table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret));
|
|
} else if (OB_FAIL(trans_table.push_back(table))) {
|
|
LOG_WARN("failed to push back trans table", K(ret));
|
|
}
|
|
}
|
|
} else if (OB_FAIL(trans_table.push_back(const_cast<TableItem *>(table)))) {
|
|
LOG_WARN("failed to push back table", K(ret));
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(trans_basic_tables.push_back(trans_table))) {
|
|
LOG_WARN("failed to push back trans tables", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::construct_trans_tables(const ObDMLStmt *stmt,
|
|
const ObDMLStmt *trans_stmt,
|
|
ObIArray<ObSEArray<TableItem *, 4>> &trans_basic_tables,
|
|
ObIArray<TableItem *> &trans_tables)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < trans_tables.count(); ++i) {
|
|
int64_t idx;
|
|
const TableItem *table_item = NULL;
|
|
if (OB_ISNULL(trans_tables.at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get unexpected null", K(ret));
|
|
} else if (OB_FAIL(stmt->get_table_item_idx(trans_tables.at(i), idx))) {
|
|
LOG_WARN("get table item idx failed", K(ret));
|
|
} else if (OB_LIKELY(idx >= 0 && idx < trans_stmt->get_table_size()) &&
|
|
OB_ISNULL(table_item = trans_stmt->get_table_item(idx))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (OB_FAIL(construct_trans_table(stmt,
|
|
table_item,
|
|
trans_basic_tables))) {
|
|
LOG_WARN("failed to construct eliminated table", K(ret));
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
/*
|
|
After aggr first join aggregation, we may get a sql like:
|
|
|
|
SELECT *
|
|
FROM TICKETS P,
|
|
(SELECT film, max(price) as agg FROM TICKETS T GROUP BY film) V
|
|
WHERE P.film = V.film AND P.price < V.agg;
|
|
|
|
The join can be eliminated by the window functions.
|
|
|
|
SELECT *
|
|
FROM (SELECT film, max(price) over (partition by film), ...
|
|
from TICKETS T where ...) V
|
|
WHERE V.price < V.agg;
|
|
|
|
This is a more intuitive transformation for win magic.
|
|
*/
|
|
int ObTransformWinMagic::do_transform(common::ObIArray<ObParentDMLStmt> &parent_stmts,
|
|
ObDMLStmt *&stmt,
|
|
int64_t drill_down_idx,
|
|
int64_t roll_up_idx,
|
|
ObStmtMapInfo &map_info,
|
|
ObStmtCompareContext &context,
|
|
bool &trans_happened,
|
|
ObIArray<TableItem *> &trans_tables)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(parent_stmts);
|
|
ObDMLStmt *trans_stmt = NULL;
|
|
bool accepted = false;
|
|
ObSEArray<ObSEArray<TableItem *, 4>, 4> trans_basic_tables;
|
|
ObTryTransHelper try_trans_helper;
|
|
const ObWinMagicHint *myhint = static_cast<const ObWinMagicHint*>(stmt->get_stmt_hint().get_normal_hint(T_WIN_MAGIC));
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(stmt->get_query_ctx()) || OB_ISNULL(ctx_) ||
|
|
OB_ISNULL(ctx_->stmt_factory_) || OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt or stmt ctx is null", K(ret));
|
|
} else if (NULL != myhint && myhint->is_disable_hint()) {
|
|
LOG_TRACE("hint is diabled", K(*myhint));
|
|
} else if (OB_FAIL(try_trans_helper.fill_helper(stmt->get_query_ctx()))) {
|
|
LOG_WARN("failed to fill try trans helper", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::deep_copy_stmt(*ctx_->stmt_factory_,
|
|
*ctx_->expr_factory_,
|
|
stmt,
|
|
trans_stmt))) {
|
|
LOG_WARN("deep copy stmt failed", K(ret));
|
|
} else if (OB_FAIL(construct_trans_tables(stmt, trans_stmt, trans_basic_tables, trans_tables))) {
|
|
LOG_WARN("construct tans tables faield", K(ret));
|
|
} else if (OB_FAIL(do_transform_from_type(trans_stmt, drill_down_idx, roll_up_idx,
|
|
context, map_info))) {
|
|
LOG_WARN("win magic do transform from type failed", K(ret));
|
|
} else if (OB_FAIL(try_to_push_down_join(trans_stmt))) {
|
|
LOG_WARN("try to push down join failed.", K(*trans_stmt));
|
|
} else if (OB_FAIL(accept_transform(parent_stmts,
|
|
stmt,
|
|
trans_stmt,
|
|
NULL != myhint && myhint->is_enable_hint(),
|
|
false,
|
|
accepted))) {
|
|
LOG_WARN("accept transform failed", K(ret));
|
|
} else if (!accepted) {
|
|
LOG_TRACE("win magic transform not accpeted", K(ret));
|
|
if (OB_FAIL(try_trans_helper.recover(stmt->get_query_ctx()))) {
|
|
LOG_WARN("failed to recover params", K(ret));
|
|
}
|
|
} else if (OB_FAIL(add_transform_hint(*stmt, &trans_basic_tables))) {
|
|
LOG_WARN("failed to add transform hint", K(ret));
|
|
} else if (OB_FAIL(append(stmt->get_query_ctx()->all_equal_param_constraints_,
|
|
map_info.equal_param_map_))) {
|
|
LOG_WARN("failed to append equal params constraints", K(ret));
|
|
} else {
|
|
add_trans_type(ctx_->happened_cost_based_trans_, WIN_MAGIC);
|
|
trans_happened = true;
|
|
LOG_TRACE("equal param constraints", K(map_info.equal_param_map_));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
select ...
|
|
from T, (select agg(c1) aggr, c2
|
|
from T
|
|
group by c2
|
|
) v
|
|
where T.c2 = v.c2
|
|
and ...;
|
|
=>
|
|
select ...
|
|
from (select agg(c1) over(partition by c2) as aggr, c2, ... //partition by's expr is same as group by's
|
|
from T //if has numerous tables,then they should be lossless join
|
|
where c2 is not null) v //the c2 is not null is referred by the T.c2 = V.c2;
|
|
where ...
|
|
*/
|
|
int ObTransformWinMagic::do_transform_from_type(ObDMLStmt *&stmt,
|
|
const int64_t drill_down_idx,
|
|
const int64_t roll_up_idx,
|
|
ObStmtCompareContext &context,
|
|
ObStmtMapInfo &map_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObDMLStmt *main_stmt = stmt;
|
|
TableItem *drill_down_table = NULL;
|
|
TableItem *roll_up_table = NULL;
|
|
ObSelectStmt *drill_down_stmt = NULL;
|
|
ObDMLStmt *roll_up_stmt = NULL;
|
|
bool match_main =false;
|
|
|
|
if (OB_ISNULL(stmt) ||
|
|
OB_ISNULL(drill_down_table = main_stmt->get_table_item(main_stmt->get_from_item(drill_down_idx))) ||
|
|
OB_ISNULL(drill_down_stmt = drill_down_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt is null", K(ret), K(stmt), K(drill_down_table), K(drill_down_stmt));
|
|
} else if (roll_up_idx == -1) {
|
|
roll_up_stmt = main_stmt;
|
|
match_main = true;
|
|
} else if (OB_ISNULL(roll_up_table = main_stmt->get_table_item(main_stmt->get_from_item(roll_up_idx))) ||
|
|
OB_ISNULL(roll_up_stmt = roll_up_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("rollup table is invalid", K(roll_up_table), K(roll_up_stmt));
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (match_main) {
|
|
if (OB_FAIL(adjust_column_and_table(main_stmt, drill_down_table, map_info))) {
|
|
LOG_WARN("adjust column and table failed");
|
|
} else if (OB_FAIL(adjust_agg_to_win(drill_down_stmt))) {
|
|
LOG_WARN("adjust agg to win faield", K(ret));
|
|
}
|
|
} else {
|
|
TableItem *transed_view_table = NULL;
|
|
ObSEArray<ObRawExpr *, 4> transed_output;
|
|
if (OB_FAIL(adjust_view_for_trans(main_stmt, drill_down_table, roll_up_table,
|
|
transed_view_table, transed_output, context, map_info))) {
|
|
LOG_WARN("adjust view for trans failed", K(ret));
|
|
} else if (OB_FAIL(adjust_win_after_group_by(main_stmt, drill_down_table, roll_up_table,
|
|
transed_view_table, transed_output, context, map_info))) {
|
|
LOG_WARN("create view and win failed", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(remove_dup_condition(main_stmt))) {
|
|
LOG_WARN("remove dup condition failed", K(ret));
|
|
} else if (OB_FAIL(stmt->rebuild_tables_hash())) {
|
|
LOG_WARN("failed to rebuild table hash", K(ret));
|
|
} else if (OB_FAIL(stmt->update_column_item_rel_id())) {
|
|
LOG_WARN("failed to update column item relation id", K(ret));
|
|
} else if (OB_FAIL(drill_down_stmt->rebuild_tables_hash())) {
|
|
LOG_WARN("failed to rebuild table hash", K(ret));
|
|
} else if (OB_FAIL(drill_down_stmt->update_column_item_rel_id())) {
|
|
LOG_WARN("failed to update column item relation id", K(ret));
|
|
} else if (OB_FAIL(stmt->formalize_stmt(ctx_->session_info_))) {
|
|
LOG_WARN("failed to formalize stmt info", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::adjust_transform_types(uint64_t &transform_types)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (cost_based_trans_tried_) {
|
|
transform_types &= (~(1 << transformer_type_));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_aggr_expr_validity(ObSelectStmt &subquery, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
is_valid = true;
|
|
if (subquery.get_select_item_size() != 1) {
|
|
is_valid = false;
|
|
} else if (OB_ISNULL(subquery.get_select_item(0).expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("select expr is null", K(ret));
|
|
} else {
|
|
is_valid = subquery.get_select_item(0).expr_->has_flag(CNT_AGG);
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_aggr_item_size(); ++i) {
|
|
if (OB_ISNULL(subquery.get_aggr_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("aggregation expr is null", K(ret));
|
|
} else if (subquery.get_aggr_item(i)->is_param_distinct()) {
|
|
is_valid = false;
|
|
} else if (subquery.get_aggr_item(i)->get_expr_type() != T_FUN_MIN &&
|
|
subquery.get_aggr_item(i)->get_expr_type() != T_FUN_MAX &&
|
|
subquery.get_aggr_item(i)->get_expr_type() != T_FUN_SUM &&
|
|
subquery.get_aggr_item(i)->get_expr_type() != T_FUN_COUNT) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// TODO (link.zt)
|
|
int ObTransformWinMagic::check_lossess_join(ObSelectStmt &subquery,
|
|
ObIArray<FromItem> &from_items,
|
|
ObIArray<ObRawExpr *> &conditions,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(subquery);
|
|
is_valid = from_items.empty() && conditions.empty();
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::create_window_function(ObAggFunRawExpr *agg_expr,
|
|
ObIArray<ObRawExpr *> &partition_exprs,
|
|
ObWinFunRawExpr *&win_expr)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObRawExprFactory *expr_factory = NULL;
|
|
if (OB_ISNULL(ctx_) || OB_ISNULL(expr_factory = ctx_->expr_factory_) ||
|
|
OB_ISNULL(ctx_->session_info_) || OB_ISNULL(agg_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("invalid argument", K(ret), K(ctx_), K(expr_factory), K(agg_expr));
|
|
} else if (OB_FAIL(expr_factory->create_raw_expr(T_WINDOW_FUNCTION,
|
|
win_expr))) {
|
|
LOG_WARN("create window function expr failed", K(ret));
|
|
} else if (OB_ISNULL(win_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("invalid argument", K(ret), K(win_expr));
|
|
} else if (OB_FAIL(win_expr->set_partition_exprs(partition_exprs))) {
|
|
LOG_WARN("fail to set partition exprs", K(ret));
|
|
} else {
|
|
ObItemType func_type = agg_expr->get_expr_type();
|
|
Bound upper;
|
|
Bound lower;
|
|
upper.type_ = BOUND_UNBOUNDED;
|
|
lower.type_ = BOUND_UNBOUNDED;
|
|
upper.is_preceding_ = true;
|
|
lower.is_preceding_ = false;
|
|
win_expr->set_func_type(func_type);
|
|
win_expr->set_window_type(WINDOW_RANGE);
|
|
win_expr->set_is_between(true);
|
|
win_expr->set_upper(upper);
|
|
win_expr->set_lower(lower);
|
|
win_expr->set_agg_expr(agg_expr);
|
|
}
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(win_expr->formalize(ctx_->session_info_))) {
|
|
LOG_WARN("failed to formalize windown function", K(ret));
|
|
} else if (OB_FAIL(win_expr->pull_relation_id())) {
|
|
LOG_WARN("failed to pull relation id and levels", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*-from type-*/
|
|
int ObTransformWinMagic::check_view_table_basic(ObSelectStmt *stmt, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("stmt is null", K(ret));
|
|
//roll up stmt should have coarser granularity.
|
|
} else if (stmt->has_limit()
|
|
|| stmt->has_having()
|
|
|| stmt->is_hierarchical_query()
|
|
|| stmt->is_set_stmt()
|
|
|| stmt->has_window_function()
|
|
|| stmt->has_rollup()
|
|
|| stmt->get_table_size() < 1
|
|
|| stmt->get_user_var_size() > 0) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(stmt->is_query_deterministic(is_valid))) {
|
|
LOG_WARN("has rand failed", K(ret));
|
|
} else if (!is_valid) {
|
|
/* do nothing */
|
|
} else if (OB_FAIL(check_select_expr_validity(*stmt, is_valid))) {
|
|
LOG_WARN("check view select expr validity failed");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_view_valid_to_trans(ObSelectStmt *view, ObStmtMapInfo &map_info, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<FromItem, 4> lossless_froms;
|
|
ObSEArray<ObRawExpr *, 4> lossless_conditions;
|
|
if (OB_ISNULL(view)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
}
|
|
// for select * from t1 a, t1 b, (select * from t1 c) v where ...
|
|
// table c should match which table? a or b ?
|
|
for (int64_t i = 0; is_valid && i < view->get_from_item_size(); ++i) {
|
|
// the from item in view has no match, have to check lossless
|
|
if (OB_INVALID_ID == map_info.from_map_.at(i)) {
|
|
if (OB_FAIL(lossless_froms.push_back(view->get_from_item(i)))) {
|
|
LOG_WARN("failed to push from item into lossless froms array", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
// no table is matched
|
|
} else if (lossless_froms.count() == view->get_from_item_size()) {
|
|
is_valid = false;
|
|
}
|
|
// check if the condition is in main stmt.
|
|
for (int64_t i = 0; is_valid && i < view->get_condition_size(); ++i) {
|
|
if (OB_INVALID_ID == map_info.cond_map_.at(i)) {
|
|
if (OB_FAIL(lossless_conditions.push_back(view->get_condition_expr(i)))) {
|
|
LOG_WARN("failed to push from item into lossless froms array", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && is_valid) {
|
|
if (OB_FAIL(check_lossess_join(*view, lossless_froms, lossless_conditions, is_valid))) {
|
|
LOG_WARN("failed check is lossess join", K(ret));
|
|
} else if (!is_valid) {
|
|
LOG_TRACE("has extra conditions or from items", K(ret));
|
|
OPT_TRACE("view has extra conditions or from items");
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::sanity_check_and_init(ObDMLStmt *stmt,
|
|
ObDMLStmt *view,
|
|
ObStmtMapInfo &map_info,
|
|
ObStmtCompareContext &context)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(view)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (view->get_from_item_size() != map_info.from_map_.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("from item size does not match to from map count", K(ret));
|
|
} else if (view->get_condition_size() != map_info.cond_map_.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("condtition size does not match to cond map count", K(ret));
|
|
} else if (view->get_table_size() != map_info.table_map_.count()) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table size does not match to table map count", K(ret));
|
|
} else if (OB_FAIL(context.init(view, stmt, map_info, nullptr))) {
|
|
LOG_WARN("failed to init", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* if a view can match main stmt ,and also another view.
|
|
* we do the transform with the main stmt first.
|
|
*/
|
|
int ObTransformWinMagic::get_view_to_trans(ObDMLStmt *&stmt,
|
|
int64_t &drill_down_idx,
|
|
int64_t &roll_up_idx,
|
|
ObStmtCompareContext &context,
|
|
ObStmtMapInfo &map_info,
|
|
ObIArray<TableItem *> &trans_tables)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
drill_down_idx = -1;
|
|
roll_up_idx = -2; //-1 means main stmt
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
}
|
|
// compare generated_table with stmt
|
|
for (int64_t i = 0; OB_SUCC(ret) && drill_down_idx == -1 && i < stmt->get_from_item_size(); i++) {
|
|
bool is_valid = true;
|
|
TableItem *rewrite_table = NULL;
|
|
ObSelectStmt *rewrite_view = NULL;
|
|
if (stmt->get_from_item(i).is_joined_) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(rewrite_table = stmt->get_table_item_by_id(stmt->get_from_item(i).table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view stmt is null", K(ret));
|
|
} else if (rewrite_table->is_generated_table()) {
|
|
rewrite_view = rewrite_table->ref_query_;
|
|
OPT_TRACE("try to transform view:", rewrite_table);
|
|
if (OB_ISNULL(rewrite_view)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view stmt is null", K(ret));
|
|
} else if (OB_FAIL(check_view_table_basic(rewrite_view, is_valid))) {
|
|
LOG_WARN("check view table basic failed", K(ret));
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
OPT_TRACE("not a valid stmt");
|
|
} else if (OB_FAIL(ObStmtComparer::compute_stmt_overlap(rewrite_view,
|
|
stmt,
|
|
map_info))) {
|
|
LOG_WARN("compute stmt overlap failed", K(ret));
|
|
} else if (OB_FAIL(sanity_check_and_init(stmt, rewrite_view, map_info, context))) {
|
|
LOG_WARN("sanity check and init failed", K(ret));
|
|
} else if (OB_FAIL(check_view_valid_to_trans(rewrite_view, map_info, is_valid))) {
|
|
LOG_WARN("check view valid to trans failed", K(ret));
|
|
} else if (OB_FAIL(check_stmt_and_view(stmt, rewrite_table, map_info, is_valid, trans_tables))) {
|
|
LOG_WARN("check stmt and view failed", K(ret));
|
|
} else if (is_valid) {
|
|
drill_down_idx = i; //rewrite_view idx
|
|
roll_up_idx = -1;
|
|
} else {
|
|
OPT_TRACE("can not transform");
|
|
}
|
|
}
|
|
}
|
|
|
|
//compare generated_table with generated table
|
|
for (int64_t i = 0; OB_SUCC(ret) && drill_down_idx == -1 && i < stmt->get_from_item_size(); i++) {
|
|
ObSelectStmt *drill_down_view = NULL;
|
|
TableItem *drill_down_table = NULL;
|
|
if (OB_ISNULL(drill_down_table = stmt->get_table_item(stmt->get_from_item(i)))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view stmt is null", K(ret));
|
|
} else if (drill_down_table->is_generated_table()) {
|
|
drill_down_view = drill_down_table->ref_query_;
|
|
bool is_valid = true;
|
|
if (OB_ISNULL(drill_down_view)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view stmt is null", K(ret));
|
|
} else if (OB_FAIL(check_view_table_basic(drill_down_view, is_valid))) {
|
|
LOG_WARN("check table basic failed", K(ret));
|
|
} else if (is_valid) {
|
|
for (int64_t j = 0; OB_SUCC(ret) && drill_down_idx == -1 && j < stmt->get_from_item_size(); j++) {
|
|
bool is_valid = true;
|
|
TableItem *roll_up_table = NULL;
|
|
ObSelectStmt *roll_up_view = NULL;
|
|
if (i == j) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(roll_up_table = stmt->get_table_item(stmt->get_from_item(j)))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view stmt is null", K(ret));
|
|
} else if (roll_up_table->is_generated_table()) {
|
|
roll_up_view = roll_up_table->ref_query_;
|
|
if (OB_ISNULL(roll_up_view)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view stmt is null", K(ret));
|
|
} else if (OB_FAIL(check_view_table_basic(roll_up_view, is_valid))) {
|
|
LOG_WARN("check view table basic failed", K(ret));
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
} else if (drill_down_view->get_from_item_size() != roll_up_view->get_from_item_size()
|
|
|| drill_down_view->get_table_size() != roll_up_view->get_table_size()) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(ObStmtComparer::compute_stmt_overlap(drill_down_view,
|
|
roll_up_view,
|
|
map_info))) {
|
|
LOG_WARN("compute stmt overlap failed", K(ret));
|
|
} else if (OB_FAIL(sanity_check_and_init(roll_up_view, drill_down_view, map_info, context))) {
|
|
LOG_WARN("sanity check and init failed", K(ret));
|
|
} else if (OB_FAIL(check_view_valid_to_trans(drill_down_view, map_info, is_valid))) {
|
|
LOG_WARN("sanity view valid to trans failed", K(ret));
|
|
} else if (OB_FAIL(check_view_and_view(stmt, drill_down_table, roll_up_table,
|
|
map_info, is_valid, trans_tables))) {
|
|
LOG_WARN("check view and view failed", K(ret));
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
//T_FUN_COUNT_SUM window function not implemented.
|
|
} else if (OB_FAIL(check_mode_and_agg_type(roll_up_view, is_valid))) {
|
|
LOG_WARN("check mode and agg type failed", K(ret));
|
|
} else if (is_valid) {
|
|
drill_down_idx = i;
|
|
roll_up_idx = j;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// avoid empty table name produce internal error. should be removed after bugfix.
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < trans_tables.count(); i++) {
|
|
if (trans_tables.at(i)->get_object_name().empty()) {
|
|
drill_down_idx = -1;
|
|
roll_up_idx = -2;
|
|
break;
|
|
}
|
|
}
|
|
LOG_DEBUG("drill_down_idx roll_up_idx", K(drill_down_idx), K(roll_up_idx));
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_mode_and_agg_type(ObSelectStmt *stmt, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (!lib::is_oracle_mode()) {
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < stmt->get_aggr_item_size(); ++i) {
|
|
if (OB_ISNULL(stmt->get_aggr_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("aggregation expr is null", K(ret));
|
|
} else if (stmt->get_aggr_item(i)->get_expr_type() == T_FUN_COUNT) {
|
|
is_valid = false;
|
|
LOG_DEBUG("T_FUN_COUNT_SUM in mysql mode not implemented");
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::get_reverse_map(ObIArray<int64_t> &map, ObIArray<int64_t> &reverse_map, int64_t size)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_FAIL(reverse_map.prepare_allocate(size))) {
|
|
LOG_WARN("pre allocate failed", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; i < reverse_map.count(); i++) {
|
|
reverse_map.at(i) = OB_INVALID_ID;
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < map.count(); i++) {
|
|
if (map.at(i) == OB_INVALID_ID) {
|
|
//do nothing
|
|
} else if (OB_UNLIKELY(map.at(i) >= size || map.at(i) < 0)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("out of range", K(ret));
|
|
} else {
|
|
reverse_map.at(map.at(i)) = i;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_stmt_and_view(ObDMLStmt *stmt,
|
|
TableItem *rewrite_table,
|
|
ObStmtMapInfo &map_info,
|
|
bool &is_valid,
|
|
ObIArray<TableItem *> &trans_tables)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<TableItem *, 4> tables;
|
|
ObSqlBitSet<> tables_id;
|
|
ObSEArray<ObRawExpr *, 4> column_exprs;
|
|
ObSEArray<ObRawExpr *, 4> view_group_exprs;
|
|
ObSEArray<int64_t, 4> expr_map;
|
|
EqualSets dummy_set;
|
|
ObSelectStmt *rewrite_view = NULL;
|
|
int64_t match_count = 0;
|
|
QueryRelation relation;
|
|
|
|
if (OB_ISNULL(stmt) || OB_ISNULL(rewrite_table) || OB_ISNULL(rewrite_view = rewrite_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (!is_valid) {
|
|
} else if (OB_FAIL(check_hint_valid(*stmt, *rewrite_table, is_valid))) {
|
|
LOG_WARN("check hint valid", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("hint reject transform");
|
|
}
|
|
|
|
for (int64_t k = 0; OB_SUCC(ret) && is_valid && k < map_info.table_map_.count(); k++) {
|
|
TableItem *table = NULL;
|
|
if (map_info.table_map_.at(k) == OB_INVALID_ID) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(table = stmt->get_table_item(map_info.table_map_.at(k)))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (OB_FAIL(tables.push_back(table))) {
|
|
LOG_WARN("push back table item failed", K(ret));
|
|
} else if (OB_FAIL(tables_id.add_member(map_info.table_map_.at(k) + 1))) {
|
|
LOG_WARN("push back item failed", K(ret));
|
|
} else if (OB_FAIL(check_hint_valid(*stmt, *table, is_valid))) {
|
|
LOG_WARN("check table valid faield", K(ret));
|
|
}
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < stmt->get_condition_exprs().count(); i++) {
|
|
ObRawExpr *expr = stmt->get_condition_exprs().at(i);
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
} else if (expr->get_expr_type() == T_OP_EQ) {
|
|
if (expr->get_param_count() < 0) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("param count not expected", K(ret));
|
|
} else if (OB_ISNULL(expr->get_param_expr(0)) || OB_ISNULL(expr->get_param_expr(1))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
} else if (expr->get_param_expr(0)->get_relation_ids().is_subset(tables_id) &&
|
|
OB_FAIL(column_exprs.push_back(expr->get_param_expr(0)))) {
|
|
LOG_WARN("push back item failed", K(ret));
|
|
} else if (expr->get_param_expr(1)->get_relation_ids().is_subset(tables_id) &&
|
|
OB_FAIL(column_exprs.push_back(expr->get_param_expr(1)))) {
|
|
LOG_WARN("push back item failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret) || !is_valid) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObStmtComparer::compute_semi_infos_map(rewrite_view, stmt,
|
|
true, map_info, match_count))) {
|
|
LOG_WARN("failed to compute semi info map", K(ret));
|
|
} else if (match_count != rewrite_view->get_semi_info_size() ||
|
|
match_count != stmt->get_semi_info_size()) {
|
|
is_valid = false;
|
|
OPT_TRACE("semi info not match");
|
|
} else if (OB_FAIL(stmt->get_column_exprs(tables, column_exprs))) {
|
|
LOG_WARN("get column exprs failed", K(ret));
|
|
} else if (OB_FAIL(ObStmtComparer::compute_conditions_map(rewrite_view,
|
|
stmt, rewrite_view->get_group_exprs(),
|
|
column_exprs, map_info, expr_map, relation))) {
|
|
LOG_WARN("compute expr map failed", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::get_parent_stmt_exprs(dummy_set, rewrite_table->table_id_,
|
|
*stmt, *rewrite_view,
|
|
rewrite_view->get_group_exprs(),
|
|
view_group_exprs))) {
|
|
LOG_WARN("get parent stmt exprs failed", K(ret));
|
|
} else if (OB_FAIL(check_outer_stmt_conditions(stmt, view_group_exprs, column_exprs,
|
|
expr_map, is_valid))) {
|
|
LOG_WARN("check outer stmt condition failed", K(ret));
|
|
} else if (!is_valid) {
|
|
OPT_TRACE("group expr not match");
|
|
}
|
|
|
|
if (OB_SUCC(ret) && is_valid) {
|
|
if (OB_FAIL(trans_tables.push_back(rewrite_table))) {
|
|
LOG_WARN("push back", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < tables.count(); i++) {
|
|
if (OB_FAIL(trans_tables.push_back(tables.at(i)))) {
|
|
LOG_WARN("push back", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_view_and_view(ObDMLStmt *main_stmt,
|
|
TableItem *drill_down_table,
|
|
TableItem *roll_up_table,
|
|
ObStmtMapInfo &map_info,
|
|
bool &is_valid,
|
|
ObIArray<TableItem *> &trans_tables)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *drill_down_view = NULL;
|
|
ObSelectStmt *roll_up_view = NULL;
|
|
ObSEArray<ObRawExpr *, 4> drill_group_exprs;
|
|
ObSEArray<ObRawExpr *, 4> roll_group_exprs;
|
|
ObSEArray<int64_t, 4> reverse_map;
|
|
ObSqlBitSet<> matched_cond;
|
|
int64_t match_count = 0;
|
|
QueryRelation relation;
|
|
EqualSets dummy_set;
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(drill_down_table) || OB_ISNULL(roll_up_table)
|
|
|| OB_ISNULL(drill_down_view = drill_down_table->ref_query_)
|
|
|| OB_ISNULL(roll_up_view = roll_up_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
// drill map roll => roll map drill
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
} else if (OB_FAIL(check_hint_valid(*main_stmt, *drill_down_table, is_valid))) {
|
|
LOG_WARN("check table valid faield", K(ret));
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
} else if (OB_FAIL(check_hint_valid(*main_stmt, *roll_up_table, is_valid))) {
|
|
LOG_WARN("check table valid faield", K(ret));
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObStmtComparer::compute_semi_infos_map(drill_down_view, roll_up_view,
|
|
true, map_info, match_count))) {
|
|
LOG_WARN("failed to compute semi info map", K(ret));
|
|
} else if (match_count != drill_down_view->get_semi_info_size() ||
|
|
match_count != roll_up_view->get_semi_info_size()) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(ObStmtComparer::compute_conditions_map(drill_down_view,
|
|
roll_up_view, drill_down_view->get_group_exprs(),
|
|
roll_up_view->get_group_exprs(), map_info,
|
|
map_info.group_map_, relation))) {
|
|
LOG_WARN("compute expr map failed", K(ret));
|
|
} else if (OB_FAIL(get_reverse_map(map_info.group_map_, reverse_map, roll_up_view->get_group_exprs().count()))) {
|
|
LOG_WARN("get reverse map failed", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::get_parent_stmt_exprs(dummy_set, drill_down_table->table_id_,
|
|
*main_stmt, *drill_down_view,
|
|
drill_down_view->get_group_exprs(),
|
|
drill_group_exprs))) {
|
|
LOG_WARN("get parent stmt exprs failed", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::get_parent_stmt_exprs(dummy_set, roll_up_table->table_id_,
|
|
*main_stmt, *roll_up_view,
|
|
roll_up_view->get_group_exprs(),
|
|
roll_group_exprs))) {
|
|
LOG_WARN("get parent stmt exprs failed", K(ret));
|
|
} else if (OB_FAIL(check_outer_stmt_conditions(main_stmt, roll_group_exprs, drill_group_exprs,
|
|
reverse_map, is_valid))) {
|
|
LOG_WARN("check outer stmt condition failed", K(ret));
|
|
}
|
|
|
|
if (OB_SUCC(ret) && map_info.from_map_.count() != roll_up_view->get_from_item_size()) {
|
|
is_valid = false;
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < map_info.from_map_.count(); i++) {
|
|
if (map_info.from_map_.at(i) == OB_INVALID_ID) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
|
|
//check if cnt agg
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < roll_up_view->get_select_item_size(); i++) {
|
|
ObRawExpr *expr = roll_up_view->get_select_item(i).expr_;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
} else if (!expr->has_flag(CNT_AGG) && !expr->has_flag(CNT_COLUMN)) { // T_FUN_AVG is ok
|
|
is_valid = false;
|
|
}
|
|
}
|
|
|
|
// simple check: conditions should one to one match.
|
|
// begin check
|
|
if (OB_FAIL(ret) || !is_valid) {
|
|
//do nothing
|
|
} else if (roll_up_view->get_condition_size() != drill_down_view->get_condition_size()) {
|
|
is_valid = false;
|
|
}
|
|
|
|
//the condition should be one to one match.
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < roll_up_view->get_condition_size(); i++) {
|
|
if (map_info.cond_map_.at(i) == OB_INVALID_ID) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(matched_cond.add_member(map_info.cond_map_.at(i)))) {
|
|
LOG_WARN("bitset add member failed", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret) || !is_valid) {
|
|
//do nothing
|
|
//the condition num should be same
|
|
} else if (matched_cond.num_members() != roll_up_view->get_condition_size()) {
|
|
is_valid = false;
|
|
//record agg map in select item map
|
|
}
|
|
|
|
if (OB_SUCC(ret) && is_valid) {
|
|
if (OB_FAIL(trans_tables.push_back(drill_down_table)) ||
|
|
OB_FAIL(trans_tables.push_back(roll_up_table))) {
|
|
LOG_WARN("push back failed", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::remove_dup_condition(ObDMLStmt *stmt) {
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
}
|
|
ObSEArray<ObRawExpr *, 4> new_conditions;
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < stmt->get_condition_size(); i++) {
|
|
ObRawExpr *expr = stmt->get_condition_expr(i);
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
} else if (expr->get_expr_type() == T_OP_EQ) {
|
|
if (expr->get_param_count() < 2) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("equal op expr's child less than 2", K(ret));
|
|
} else if (!expr->get_param_expr(0)->same_as(*expr->get_param_expr(1))) {
|
|
if (OB_FAIL(new_conditions.push_back(expr))) {
|
|
LOG_WARN("push back failed", K(ret));
|
|
}
|
|
} else {
|
|
ObRawExpr *is_not_null = NULL;
|
|
if (OB_FAIL(ObTransformUtils::add_is_not_null(ctx_, expr->get_param_expr(0), is_not_null))) {
|
|
LOG_WARN("failed to add is not null expr", K(ret));
|
|
} else if (OB_FAIL(new_conditions.push_back(is_not_null))) {
|
|
LOG_WARN("failed to append is not null exprs to where conditions", K(ret));
|
|
}
|
|
}
|
|
} else if (OB_FAIL(new_conditions.push_back(expr))) {
|
|
LOG_WARN("push back failed", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(stmt->get_condition_exprs().assign(new_conditions))) {
|
|
LOG_WARN("assign exprs failed", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// the group by column must be the join condition.
|
|
// for group by c1,c2
|
|
// the outer stmt must have where v.c1=T.c1 and v.c2=T.c2 and ...
|
|
// roll group exprs is subset of drill group exprs
|
|
int ObTransformWinMagic::check_outer_stmt_conditions(ObDMLStmt *stmt,
|
|
ObIArray<ObRawExpr *> &roll_group_exprs,
|
|
ObIArray<ObRawExpr *> &drill_group_exprs,
|
|
ObIArray<int64_t> &map,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
EqualSets equal_sets;
|
|
if (OB_ISNULL(stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (roll_group_exprs.count() > drill_group_exprs.count() ||
|
|
map.count() != roll_group_exprs.count()) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(ObEqualAnalysis::compute_equal_set(ctx_->allocator_, stmt->get_condition_exprs(), equal_sets))) {
|
|
LOG_WARN("compute equal set failed", K(ret));
|
|
} else {
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < map.count(); i++) {
|
|
if (map.at(i) == OB_INVALID_ID) {
|
|
is_valid = false;
|
|
} else {
|
|
ObRawExpr *roll_expr = roll_group_exprs.at(i);
|
|
ObRawExpr *drill_expr = drill_group_exprs.at(map.at(i));
|
|
if (roll_expr == NULL || drill_expr == NULL) {
|
|
is_valid = false;
|
|
} else if (!ObOptimizerUtil::is_expr_equivalent(roll_expr, drill_expr, equal_sets)) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// simple check only full group by
|
|
int ObTransformWinMagic::check_expr_in_group(ObRawExpr *expr,
|
|
ObIArray<ObRawExpr *> &group_exprs,
|
|
bool &is_valid) {
|
|
int ret = OB_SUCCESS;
|
|
bool found_match = false;
|
|
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && !found_match && i < group_exprs.count(); ++i) {
|
|
if (expr == group_exprs.at(i) || expr->same_as(*group_exprs.at(i))) {
|
|
found_match = true;
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (!found_match) {
|
|
if (expr->is_column_ref_expr()) {
|
|
is_valid = false;
|
|
} else if (IS_AGGR_FUN(expr->get_expr_type()) ||
|
|
expr->get_expr_type() == T_FUN_MIN ||
|
|
expr->get_expr_type() == T_FUN_MAX ||
|
|
expr->get_expr_type() == T_FUN_COUNT ||
|
|
expr->get_expr_type() == T_FUN_SUM ||
|
|
!expr->has_flag(CNT_COLUMN)) {
|
|
// do nothing
|
|
} else {
|
|
// simple check only full group by
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < expr->get_param_count(); ++i) {
|
|
if (OB_ISNULL(expr->get_param_expr(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr child is null", K(ret));
|
|
} else if (OB_FAIL(SMART_CALL(check_expr_in_group(expr->get_param_expr(i),
|
|
group_exprs, is_valid)))) {
|
|
LOG_WARN("failed to recursive check", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::simple_check_group_by(ObIArray<ObRawExpr *> &exprs,
|
|
ObIArray<ObRawExpr *> &group_exprs,
|
|
bool &is_valid) {
|
|
int ret = OB_SUCCESS;
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < exprs.count(); i++) {
|
|
if (OB_FAIL(SMART_CALL(check_expr_in_group(exprs.at(i), group_exprs, is_valid)))) {
|
|
LOG_WARN("failed to recursive check", K(ret));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// subquery select item shold contain aggr function
|
|
int ObTransformWinMagic::check_select_expr_validity(ObSelectStmt &subquery, bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
bool found_agg = false;
|
|
ObSEArray<ObRawExpr *, 4> check_exprs;
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_select_item_size(); ++i) {
|
|
if (OB_ISNULL(subquery.get_select_item(i).expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("select expr is null", K(ret));
|
|
} else if (subquery.get_select_item(i).expr_->has_flag(CNT_AGG)) {
|
|
found_agg = true;
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (FALSE_IT(is_valid = found_agg)) {
|
|
//never reach
|
|
} else if (!is_valid) {
|
|
//do nothing
|
|
} else if (lib::is_oracle_mode()) {
|
|
//do nothing
|
|
} else if (OB_FAIL(subquery.get_select_exprs(check_exprs))) {
|
|
LOG_WARN("subquery get select exprs failed", K(ret));
|
|
} else if (OB_FAIL(subquery.get_order_exprs(check_exprs))) {
|
|
LOG_WARN("append failed", K(ret));
|
|
} else if (OB_FAIL(append(check_exprs, subquery.get_having_exprs()))) {
|
|
LOG_WARN("append failed", K(ret));
|
|
} else if (OB_FAIL(simple_check_group_by(check_exprs, subquery.get_group_exprs(), is_valid))) {
|
|
LOG_WARN("simple check group by failed", K(ret));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < subquery.get_aggr_item_size(); ++i) {
|
|
if (OB_ISNULL(subquery.get_aggr_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("aggregation expr is null", K(ret));
|
|
} else if (subquery.get_aggr_item(i)->is_param_distinct()) {
|
|
is_valid = false;
|
|
} else if (subquery.get_aggr_item(i)->get_expr_type() != T_FUN_MIN &&
|
|
subquery.get_aggr_item(i)->get_expr_type() != T_FUN_MAX &&
|
|
subquery.get_aggr_item(i)->get_expr_type() != T_FUN_SUM &&
|
|
subquery.get_aggr_item(i)->get_expr_type() != T_FUN_COUNT) {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::adjust_column_and_table(ObDMLStmt *main_stmt,
|
|
TableItem *view,
|
|
ObStmtMapInfo &map_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *view_stmt = NULL;
|
|
ObSEArray<TableItem *, 4> main_tables;
|
|
ObSEArray<TableItem *, 4> view_tables;
|
|
ObSEArray<uint64_t, 4> main_table_ids;
|
|
ObSEArray<FromItem, 4> rm_from_items;
|
|
|
|
ObSEArray<ObRawExpr *, 4> merged_column_expr;
|
|
ObSEArray<ObRawExpr *, 4> pushed_column_exprs;
|
|
|
|
ObSEArray<ObRawExpr *, 4> merged_pseudo_column_exprs;
|
|
ObSEArray<ObRawExpr *, 4> pushed_pseudo_column_exprs;
|
|
|
|
ObArray<SemiInfo *> rm_semi_infos;
|
|
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(view) ||
|
|
OB_ISNULL(view_stmt = view->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret), K(main_stmt), K(view), K(view_stmt));
|
|
}
|
|
//init viriables and push down table
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < map_info.table_map_.count(); ++i) {
|
|
int64_t idx = map_info.table_map_.at(i);
|
|
TableItem *table = NULL;
|
|
if (idx == OB_INVALID_ID) {
|
|
// do nothing
|
|
} else if (OB_UNLIKELY(idx < 0 || idx >= main_stmt->get_table_size()) ||
|
|
OB_ISNULL(table = main_stmt->get_table_item(idx))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("index is invalid", K(ret), K(idx), K(table));
|
|
} else if (OB_FAIL(main_tables.push_back(table))) {
|
|
LOG_WARN("failed to push back table item", K(ret));
|
|
} else if (OB_FAIL(view_tables.push_back(view_stmt->get_table_item(i)))) {
|
|
LOG_WARN("failed to push back view table item", K(ret));
|
|
} else if (OB_FAIL(main_table_ids.push_back(table->table_id_))) {
|
|
LOG_WARN("failed to push back table id", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_part_expr_items(table->table_id_))) {
|
|
LOG_WARN("failed to remove part exprs", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::add_table_item(view_stmt, table))) {
|
|
LOG_WARN("failed to add table item", K(ret));
|
|
}
|
|
}
|
|
|
|
// push down column
|
|
ObArray<ColumnItem> column_items;
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(main_stmt->get_column_items(main_table_ids, column_items))) {
|
|
LOG_WARN("get column items failed", K(ret));
|
|
} else if (OB_FAIL(view_stmt->add_column_item(column_items))) {
|
|
LOG_WARN("add column item failed", K(ret));
|
|
}
|
|
|
|
// push down pseudo column
|
|
ObArray<ObRawExpr *> pseudo_columns;
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(main_stmt->get_table_pseudo_column_like_exprs(main_table_ids, pseudo_columns))) {
|
|
LOG_WARN("get table pseudo column like exprs failed", K(ret));
|
|
} else if (OB_FAIL(append(view_stmt->get_pseudo_column_like_exprs(), pseudo_columns))) {
|
|
LOG_WARN("append failed", K(ret));
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < map_info.semi_info_map_.count(); ++i) {
|
|
int64_t idx = map_info.semi_info_map_.at(i);
|
|
SemiInfo *semi = NULL;
|
|
if (idx == OB_INVALID_ID) {
|
|
// do nothing
|
|
} else if (OB_UNLIKELY(idx < 0 || idx >= main_stmt->get_semi_info_size()) ||
|
|
OB_ISNULL(semi = main_stmt->get_semi_infos().at(idx))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("semi info is null", K(ret), K(idx), K(semi));
|
|
} else if (OB_FAIL(rm_semi_infos.push_back(semi))) {
|
|
LOG_WARN("failed to push back semi info", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && ObOptimizerUtil::remove_item(main_stmt->get_semi_infos(),
|
|
rm_semi_infos)) {
|
|
LOG_WARN("failed to remove semi infos", K(ret));
|
|
}
|
|
|
|
//merge table
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < main_tables.count(); ++i) {
|
|
TableItem *main_table = main_tables.at(i);
|
|
TableItem *view_table = view_tables.at(i);
|
|
if (OB_ISNULL(main_table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::merge_table_items(view_stmt,
|
|
view_table,
|
|
main_table,
|
|
NULL,
|
|
&pushed_column_exprs,
|
|
&merged_column_expr,
|
|
&pushed_pseudo_column_exprs,
|
|
&merged_pseudo_column_exprs))) {
|
|
LOG_WARN("failed to merge table items", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_table_in_semi_infos(main_stmt, view_table,
|
|
main_table))) {
|
|
LOG_WARN("failed to replace table info in semi infos", K(ret));
|
|
}
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < map_info.from_map_.count(); i++) {
|
|
int64_t from_idx = map_info.from_map_.at(i);
|
|
if (from_idx != OB_INVALID_ID &&
|
|
OB_FAIL(rm_from_items.push_back(main_stmt->get_from_item(from_idx)))) {
|
|
LOG_WARN("failed to push back from item", K(ret));
|
|
}
|
|
}
|
|
|
|
//remove joined table
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < rm_from_items.count(); i++) {
|
|
if (OB_FAIL(main_stmt->remove_from_item(rm_from_items.at(i).table_id_))) {
|
|
LOG_WARN("failed to remove from item", K(ret));
|
|
} else if (!rm_from_items.at(i).is_joined_) {
|
|
// do nothing
|
|
} else if (OB_FAIL(main_stmt->remove_joined_table_item(
|
|
main_stmt->get_joined_table(rm_from_items.at(i).table_id_)))) {
|
|
LOG_WARN("remove joined table item failed", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret) && (main_stmt->is_update_stmt() || main_stmt->is_delete_stmt())) {
|
|
ObDelUpdStmt *del_upd_stmt = static_cast<ObDelUpdStmt *>(main_stmt);
|
|
ObArray<ObDmlTableInfo *> table_infos;
|
|
if (OB_FAIL(del_upd_stmt->get_dml_table_infos(table_infos))) {
|
|
LOG_WARN("failed to get dml table info", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < table_infos.count(); ++i) {
|
|
int64_t idx = -1;
|
|
if (OB_ISNULL(table_infos.at(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table info is null", K(ret));
|
|
} else if (!ObOptimizerUtil::find_item(main_table_ids,
|
|
table_infos.at(i)->table_id_,
|
|
&idx)) {
|
|
// do nothing
|
|
} else {
|
|
table_infos.at(i)->table_id_ = view->table_id_;
|
|
table_infos.at(i)->loc_table_id_ = view_tables.at(idx)->table_id_;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ObSEArray<ObRawExpr *, 4> new_col_for_pseudo_cols;
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(ObOptimizerUtil::remove_item(main_stmt->get_pseudo_column_like_exprs(),
|
|
pushed_pseudo_column_exprs))) {
|
|
LOG_WARN("remove item failed", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_,
|
|
*view,
|
|
main_stmt,
|
|
merged_pseudo_column_exprs,
|
|
new_col_for_pseudo_cols))) {
|
|
LOG_WARN("create columns for view failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->replace_relation_exprs(pushed_pseudo_column_exprs, new_col_for_pseudo_cols))) {
|
|
LOG_WARN("replace inner stmt expr failed", K(ret));
|
|
}
|
|
|
|
ObSEArray<ObRawExpr *, 4> new_col_in_main;
|
|
//create select item for view
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing.
|
|
} else if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_,
|
|
*view,
|
|
main_stmt,
|
|
merged_column_expr,
|
|
new_col_in_main))) {
|
|
LOG_WARN("create columns for view failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_table_item(main_tables))) {
|
|
LOG_WARN("failed to remove table item", K(ret));
|
|
} else if (OB_FAIL(view_stmt->remove_table_item(main_tables))) {
|
|
LOG_WARN("failed to remove table item", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_column_item(main_table_ids))) {
|
|
LOG_WARN("remove column item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->replace_relation_exprs(pushed_column_exprs, new_col_in_main))) {
|
|
LOG_WARN("replace inner stmt expr failed", K(ret));
|
|
}
|
|
|
|
//add is not null expr
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < view_stmt->get_group_expr_size(); i++) {
|
|
ObRawExpr *expr = view_stmt->get_group_exprs().at(i);
|
|
ObSEArray<ObRawExpr *, 1> is_not_null_exprs;
|
|
ObRawExpr *is_not_null = NULL;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("gourp by expr is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::add_is_not_null(ctx_, expr, is_not_null))) {
|
|
LOG_WARN("failed to add is not null expr", K(ret));
|
|
} else if (OB_FAIL(is_not_null_exprs.push_back(is_not_null))) {
|
|
LOG_WARN("failed to push is not null expr into array", K(ret));
|
|
} else if (OB_FAIL(append(view_stmt->get_condition_exprs(), is_not_null_exprs))) {
|
|
LOG_WARN("failed to append is not null exprs to where conditions", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && (main_stmt->is_update_stmt() || main_stmt->is_delete_stmt())) {
|
|
ObDelUpdStmt *del_upd_stmt = static_cast<ObDelUpdStmt *>(main_stmt);
|
|
if (OB_FAIL(ObTransformUtils::adjust_updatable_view(
|
|
*ctx_->expr_factory_,
|
|
del_upd_stmt,
|
|
*view))) {
|
|
LOG_WARN("failed to adjust updatable view", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::adjust_agg_to_win(ObSelectStmt *view_stmt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSEArray<ObRawExpr *, 4> aggr_exprs;
|
|
ObSEArray<ObRawExpr *, 4> win_exprs;
|
|
ObSEArray<ObRawExpr *, 4> partition_exprs;
|
|
if (OB_FAIL(partition_exprs.assign(view_stmt->get_group_exprs()))) {
|
|
LOG_WARN("failed to assign group exprs", K(ret));
|
|
} else if (OB_FAIL(append(aggr_exprs, view_stmt->get_aggr_items()))) {
|
|
LOG_WARN("failed to append aggr exprs", K(ret));
|
|
} else {
|
|
view_stmt->get_group_exprs().reset();
|
|
view_stmt->clear_aggr_item();
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < aggr_exprs.count(); ++i) {
|
|
ObWinFunRawExpr *win_expr = NULL;
|
|
if (OB_FAIL(create_window_function(static_cast<ObAggFunRawExpr *>(aggr_exprs.at(i)),
|
|
partition_exprs,
|
|
win_expr))) {
|
|
LOG_WARN("failed to create window function", K(ret));
|
|
} else if (OB_FAIL(view_stmt->add_window_func_expr(win_expr))) {
|
|
LOG_WARN("failed to add window func expr", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_SUCC(ret)) {
|
|
if (OB_FAIL(append(aggr_exprs, view_stmt->get_aggr_items()))) {
|
|
LOG_WARN("failed to convert aggregation exprs", K(ret));
|
|
} else if (OB_UNLIKELY(aggr_exprs.count() != view_stmt->get_window_func_exprs().count())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("unexpected aggr and winfun count", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < view_stmt->get_window_func_exprs().count(); i ++) {
|
|
ObRawExpr *expr = view_stmt->get_window_func_exprs().at(i);
|
|
if (OB_FAIL(ObTransformUtils::add_cast_for_replace_if_need(
|
|
*ctx_->expr_factory_, aggr_exprs.at(i), expr, ctx_->session_info_))) {
|
|
LOG_WARN("failed to add cast", K(ret));
|
|
} else if (OB_FAIL(win_exprs.push_back(expr))) {
|
|
LOG_WARN("failed to push back expr", K(ret));
|
|
}
|
|
}
|
|
if (OB_SUCC(ret) && OB_FAIL(view_stmt->replace_relation_exprs(aggr_exprs, win_exprs))) {
|
|
LOG_WARN("failed to replace relation expr", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::adjust_view_for_trans(ObDMLStmt *main_stmt,
|
|
TableItem *&drill_down_table,
|
|
TableItem *roll_up_table,
|
|
TableItem *&transed_view_table,
|
|
ObIArray<ObRawExpr *> &new_transed_output,
|
|
ObStmtCompareContext &context,
|
|
ObStmtMapInfo &map_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *drill_down_stmt = NULL;
|
|
ObSelectStmt *roll_up_stmt = NULL;
|
|
ObSEArray<ObRawExpr *, 4> new_drill_down_view_column_exprs;
|
|
ObSEArray<ObRawExpr *, 4> pushed_column_exprs;
|
|
ObSEArray<ObRawExpr *, 4> new_select_exprs_for_drill;
|
|
ObSEArray<ObRawExpr *, 4> new_drill_down_view_output_columns;
|
|
ObSEArray<ObRawExpr *, 4> new_select_exprs_for_trans;
|
|
ObSEArray<ObRawExpr *, 4> old_roll_up_view_output_columns;
|
|
ObSEArray<ObRawExpr *, 4> new_transed_view_output_columns;
|
|
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(drill_down_table) || OB_ISNULL(roll_up_table) ||
|
|
OB_ISNULL(drill_down_stmt = drill_down_table->ref_query_) ||
|
|
OB_ISNULL(roll_up_stmt = roll_up_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_with_empty_view(ctx_,
|
|
main_stmt,
|
|
transed_view_table,
|
|
drill_down_table))) {
|
|
LOG_WARN("failed to create empty view table", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_inline_view(ctx_,
|
|
main_stmt,
|
|
transed_view_table,
|
|
drill_down_table))) {
|
|
LOG_WARN("failed to create inline view", K(ret));
|
|
} else if (OB_ISNULL(transed_view_table) ||
|
|
OB_ISNULL(transed_view_table->ref_query_) ||
|
|
OB_UNLIKELY(transed_view_table->ref_query_->get_table_size() != 1)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("transed view table is null", K(ret), K(transed_view_table));
|
|
} else {
|
|
drill_down_table = transed_view_table->ref_query_->get_table_item(0);
|
|
}
|
|
|
|
// push down table
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < roll_up_stmt->get_table_size(); i++) {
|
|
TableItem *table = NULL;
|
|
ObSEArray<ObDMLStmt::PartExprItem, 4> part_exprs;
|
|
if (OB_ISNULL(table = roll_up_stmt->get_table_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::add_table_item(drill_down_stmt, table))) {
|
|
LOG_WARN("add table item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->get_part_expr_items(table->table_id_, part_exprs))) {
|
|
LOG_WARN("failed to get part expr items", K(ret));
|
|
} else if (!part_exprs.empty() && OB_FAIL(drill_down_stmt->set_part_expr_items(part_exprs))) {
|
|
LOG_WARN("failed to set part expr item", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_part_expr_items(table->table_id_))) {
|
|
LOG_WARN("failed to remove part epxr", K(ret));
|
|
}
|
|
}
|
|
|
|
// push down column
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < roll_up_stmt->get_column_size(); i++) {
|
|
if (OB_ISNULL(roll_up_stmt->get_column_item(i)) ||
|
|
OB_ISNULL(roll_up_stmt->get_column_item(i)->get_expr())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("column or column expr is null", K(ret));
|
|
} else if (OB_FAIL(drill_down_stmt->add_column_item(roll_up_stmt->get_column_items().at(i)))) {
|
|
LOG_WARN("add column item failed", K(ret));
|
|
}
|
|
}
|
|
|
|
ObSEArray<ObRawExpr *, 4> agg_in_roll;
|
|
ObSEArray<ObRawExpr *, 4> agg_in_drill;
|
|
// push aggr item
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < roll_up_stmt->get_aggr_item_size(); i++) {
|
|
if (OB_ISNULL(roll_up_stmt->get_aggr_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("agg expr is null", K(ret));
|
|
} else {
|
|
bool found = false;
|
|
for(int64_t j = 0; OB_SUCC(ret) && !found && j < drill_down_stmt->get_aggr_item_size(); j++) {
|
|
if (drill_down_stmt->get_aggr_item(j)->same_as(*roll_up_stmt->get_aggr_item(i), &context)) {
|
|
found = true;
|
|
if (OB_FAIL(agg_in_roll.push_back(roll_up_stmt->get_aggr_item(i))) ||
|
|
OB_FAIL(agg_in_drill.push_back(drill_down_stmt->get_aggr_item(j)))) {
|
|
LOG_WARN("push back expr into array failed", K(ret));
|
|
} else if (OB_FAIL(append(map_info.equal_param_map_, context.equal_param_info_))) {
|
|
LOG_WARN("failed to append equal param map", K(ret));
|
|
} else {
|
|
context.equal_param_info_.reset();
|
|
}
|
|
}
|
|
}
|
|
if (OB_FAIL(ret) || found) {
|
|
//do nothing
|
|
} else if (OB_FAIL(drill_down_stmt->add_agg_item(*roll_up_stmt->get_aggr_item(i)))) {
|
|
LOG_WARN("add agg item failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
//push
|
|
//merge table
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < map_info.table_map_.count(); i++) {
|
|
TableItem *table_in_roll_up = NULL;
|
|
TableItem *table_in_drill_down = NULL;
|
|
if (map_info.table_map_.at(i) == OB_INVALID_ID) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(table_in_roll_up = roll_up_stmt->get_table_item(map_info.table_map_.at(i))) ||
|
|
OB_ISNULL(table_in_drill_down = drill_down_stmt->get_table_item(i))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("table item is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::merge_table_items(drill_down_stmt,
|
|
table_in_drill_down,
|
|
table_in_roll_up,
|
|
NULL,
|
|
&pushed_column_exprs,
|
|
&new_drill_down_view_column_exprs))) {
|
|
LOG_WARN("failed to merge table items", K(ret));
|
|
} else if (OB_FAIL(drill_down_stmt->remove_table_item(table_in_roll_up))) {
|
|
LOG_WARN("remove table item failed", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_table_in_semi_infos(main_stmt, transed_view_table,
|
|
table_in_drill_down))) {
|
|
LOG_WARN("failed to replace table info in semi infos", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_table_in_semi_infos(main_stmt, transed_view_table,
|
|
table_in_roll_up))) {
|
|
LOG_WARN("failed to replace table info in semi infos", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
//create new select for drill down table begin
|
|
} else if (OB_FAIL(append(new_select_exprs_for_drill, roll_up_stmt->get_group_exprs()) ||
|
|
OB_FAIL(append(new_select_exprs_for_drill, roll_up_stmt->get_aggr_items())))) {
|
|
LOG_WARN("append exprs after array failed", K(ret));
|
|
//make the exprs in roll up stmt become exprs in drill down stmt
|
|
} else if (OB_FAIL(ObTransformUtils::replace_exprs(pushed_column_exprs,
|
|
new_drill_down_view_column_exprs,
|
|
new_select_exprs_for_drill))) {
|
|
LOG_WARN("replace exprs failed", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_exprs(agg_in_roll, agg_in_drill,
|
|
new_select_exprs_for_drill))) {
|
|
LOG_WARN("replace exprs failed", K(ret));
|
|
//create select item for drill_down_stmt
|
|
} else if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_, *drill_down_table,
|
|
transed_view_table->ref_query_,
|
|
new_select_exprs_for_drill,
|
|
new_drill_down_view_output_columns))) {
|
|
LOG_WARN("create column for view failed", K(ret));
|
|
//create new select for drill down table end
|
|
//create new select for transed table begin
|
|
} else if (OB_FAIL(main_stmt->get_view_output(*roll_up_table, new_select_exprs_for_trans,
|
|
old_roll_up_view_output_columns))) {
|
|
LOG_WARN("get view output failed", K(ret));
|
|
//make the exprs in roll up stmt become exprs in drill down stmt
|
|
} else if (OB_FAIL(ObTransformUtils::replace_exprs(pushed_column_exprs,
|
|
new_drill_down_view_column_exprs,
|
|
new_select_exprs_for_trans))) {
|
|
LOG_WARN("replace exprs failed", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_exprs(agg_in_roll, agg_in_drill,
|
|
new_select_exprs_for_trans))) {
|
|
LOG_WARN("replace exprs failed", K(ret));
|
|
//map the select exprs in drill to column exprs in transed
|
|
} else if (OB_FAIL(ObTransformUtils::replace_exprs(new_select_exprs_for_drill,
|
|
new_drill_down_view_output_columns,
|
|
new_select_exprs_for_trans))) {
|
|
LOG_WARN("replace exprs failed", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_columns_for_view(ctx_, *transed_view_table,
|
|
main_stmt,
|
|
new_select_exprs_for_trans,
|
|
new_transed_view_output_columns,
|
|
true, true))) {
|
|
LOG_WARN("create columns for view failed", K(ret));
|
|
//create new select for transed table end
|
|
} else if (OB_FAIL(main_stmt->remove_column_item(roll_up_table->table_id_))) {
|
|
LOG_WARN("remove column item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->replace_relation_exprs(old_roll_up_view_output_columns,
|
|
new_transed_view_output_columns))) {
|
|
LOG_WARN("replace inner stmt expr failed", K(ret));
|
|
} else if (OB_FAIL(new_transed_output.assign(new_transed_view_output_columns))) {
|
|
LOG_WARN("assign array failed", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::change_agg_to_win_func(ObDMLStmt *main_stmt,
|
|
ObSelectStmt *roll_up_stmt,
|
|
ObSelectStmt *drill_down_stmt,
|
|
TableItem *transed_view_table,
|
|
ObStmtMapInfo &map_info,
|
|
ObStmtCompareContext &context,
|
|
ObIArray<ObRawExpr *> &old_col,
|
|
ObIArray<ObRawExpr *> &new_win)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
UNUSED(main_stmt);
|
|
ObSEArray<ObRawExpr *, 4> partition_exprs;
|
|
ObRawExpr *col_expr = NULL;
|
|
ObItemType type;
|
|
ObSelectStmt *transed_stmt = NULL;
|
|
ObAggFunRawExpr *new_agg_expr = NULL;
|
|
ObWinFunRawExpr *win_expr = NULL;
|
|
ObRawExpr *cast_win_expr = NULL;
|
|
|
|
ObSEArray<ObRawExpr *, 4> drill_select_exprs;
|
|
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(roll_up_stmt) || OB_ISNULL(drill_down_stmt)
|
|
|| OB_ISNULL(transed_view_table) || OB_ISNULL(transed_stmt = transed_view_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (OB_FAIL(drill_down_stmt->get_select_exprs(drill_select_exprs))) {
|
|
LOG_WARN("get select exprs failed", K(ret));
|
|
}
|
|
|
|
//get partition exprs
|
|
for(int64_t i = 0; OB_SUCC(ret) && i < roll_up_stmt->get_group_exprs().count(); i++) {
|
|
bool found = false;
|
|
ObRawExpr *group_expr_in_roll = NULL;
|
|
ObRawExpr *group_expr_in_drill = NULL;
|
|
for(int64_t j = 0; OB_SUCC(ret) && !found && j < drill_down_stmt->get_group_exprs().count(); j++) {
|
|
if (OB_ISNULL(group_expr_in_roll = roll_up_stmt->get_group_exprs().at(i)) ||
|
|
OB_ISNULL(group_expr_in_drill = drill_down_stmt->get_group_exprs().at(j))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("group expr is null", K(ret));
|
|
} else if (group_expr_in_drill->same_as(*group_expr_in_roll, &context)) {
|
|
found = true;
|
|
int64_t idx = -1;
|
|
if (OB_FAIL(ObTransformUtils::get_expr_idx(drill_select_exprs, group_expr_in_drill, idx))) {
|
|
LOG_WARN("get expr idx faield", K(ret));
|
|
} else if (idx == -1) {
|
|
// do nothing
|
|
} else if (OB_FAIL(partition_exprs.push_back(transed_stmt->get_column_expr_by_id(
|
|
transed_stmt->get_table_item(0)->table_id_, idx + OB_APP_MIN_COLUMN_ID)))) {
|
|
LOG_WARN("push back expr failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < partition_exprs.count(); i++) {
|
|
ObRawExpr *expr = partition_exprs.at(i);
|
|
ObSEArray<ObRawExpr *, 1> is_not_null_exprs;
|
|
ObRawExpr *is_not_null = NULL;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("gourp by expr is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::add_is_not_null(ctx_, expr, is_not_null))) {
|
|
LOG_WARN("failed to add is not null expr", K(ret));
|
|
} else if (OB_FAIL(is_not_null_exprs.push_back(is_not_null))) {
|
|
LOG_WARN("failed to push is not null expr into array", K(ret));
|
|
} else if (OB_FAIL(append(transed_stmt->get_condition_exprs(), is_not_null_exprs))) {
|
|
LOG_WARN("failed to append is not null exprs to where conditions", K(ret));
|
|
}
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < roll_up_stmt->get_aggr_item_size(); i++) {
|
|
ObRawExpr *agg_expr = roll_up_stmt->get_aggr_item(i);
|
|
int64_t sel_idx = -1;
|
|
|
|
if (OB_ISNULL(agg_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
}
|
|
|
|
for (int64_t j = 0; OB_SUCC(ret) && sel_idx == -1 && j < drill_down_stmt->get_select_item_size(); j++) {
|
|
if (drill_down_stmt->get_select_item(j).expr_->same_as(*agg_expr, &context)) {
|
|
sel_idx = j;
|
|
if (OB_FAIL(append(map_info.equal_param_map_, context.equal_param_info_))) {
|
|
LOG_WARN("failed to append equal param map", K(ret));
|
|
} else {
|
|
context.equal_param_info_.reset();
|
|
}
|
|
}
|
|
}
|
|
ColumnItem *col_in_transed = NULL;
|
|
if (sel_idx == -1) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("select item idx is out of range", K(ret));
|
|
} else if (OB_ISNULL(col_in_transed = transed_stmt->get_column_item_by_id(
|
|
transed_stmt->get_table_item(0)->table_id_,
|
|
sel_idx + OB_APP_MIN_COLUMN_ID))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("can not find the corresponding select item", K(ret), K(sel_idx));
|
|
} else if (OB_ISNULL(col_expr = col_in_transed->get_expr())) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("col expr is null", K(ret));
|
|
} else if (FALSE_IT(type = (lib::is_oracle_mode() ?
|
|
(agg_expr->get_expr_type() == T_FUN_COUNT ?
|
|
T_FUN_SUM : agg_expr->get_expr_type()) :
|
|
(agg_expr->get_expr_type() == T_FUN_COUNT ?
|
|
T_FUN_COUNT_SUM : agg_expr->get_expr_type())))) {
|
|
//never reach
|
|
} else if (OB_FAIL(ObTransformUtils::create_aggr_expr(ctx_, type, new_agg_expr, col_expr))) {
|
|
LOG_WARN("creat aggr expr failed", K(ret));
|
|
} else if (OB_ISNULL(new_agg_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("new agg expr is null", K(ret));
|
|
} else if (OB_FAIL(create_window_function(new_agg_expr, partition_exprs, win_expr))) {
|
|
LOG_WARN("create window function failed", K(ret));
|
|
} else if (OB_ISNULL(win_expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("win function expr is null", K(ret));
|
|
} else if (OB_FAIL(transed_stmt->add_window_func_expr(win_expr))) {
|
|
LOG_WARN("add windon func expr failed", K(ret));
|
|
} else if (FALSE_IT(cast_win_expr = win_expr)) {
|
|
} else if (OB_FAIL(ObTransformUtils::add_cast_for_replace_if_need(
|
|
*ctx_->expr_factory_, col_expr, cast_win_expr, ctx_->session_info_))) {
|
|
LOG_WARN("failed to add cast", K(ret));
|
|
} else if (OB_FAIL(old_col.push_back(col_expr))) {
|
|
LOG_WARN("failed to push expr into array", K(ret));
|
|
} else if (OB_FAIL(new_win.push_back(cast_win_expr))) {
|
|
LOG_WARN("failed to push expr into array", K(ret));
|
|
} else {
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::adjust_win_after_group_by(ObDMLStmt *main_stmt,
|
|
TableItem *drill_down_table,
|
|
TableItem *roll_up_table,
|
|
TableItem *transed_view_table,
|
|
ObIArray<ObRawExpr *> &new_transed_view_output_columns,
|
|
ObStmtCompareContext &context,
|
|
ObStmtMapInfo &map_info)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *drill_down_stmt = NULL;
|
|
ObSelectStmt *roll_up_stmt = NULL;
|
|
ObSelectStmt *transed_stmt = NULL;
|
|
ObSEArray<ObRawExpr *, 4> old_agg;
|
|
ObSEArray<ObRawExpr *, 4> new_win;
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(drill_down_table) || OB_ISNULL(transed_view_table)
|
|
|| OB_ISNULL(roll_up_table) || OB_ISNULL(drill_down_stmt = drill_down_table->ref_query_)
|
|
|| OB_ISNULL(roll_up_stmt = roll_up_table->ref_query_)
|
|
|| OB_ISNULL(transed_stmt = transed_view_table->ref_query_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (OB_FAIL(change_agg_to_win_func(main_stmt, roll_up_stmt, drill_down_stmt, transed_view_table,
|
|
map_info, context,
|
|
old_agg, new_win))) {
|
|
LOG_WARN("change agg to window function failed", K(ret));
|
|
}
|
|
|
|
for (int64_t i = 0 ;OB_SUCC(ret) && i < new_transed_view_output_columns.count(); i++) {
|
|
ObRawExpr *&expr = transed_stmt->get_select_item(
|
|
static_cast<ObColumnRefRawExpr *>(new_transed_view_output_columns.at(i))->get_column_id() -
|
|
OB_APP_MIN_COLUMN_ID).expr_;
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::replace_expr(old_agg, new_win, expr))) {
|
|
LOG_WARN("replace exprs failed", K(ret));
|
|
}
|
|
ObSEArray<ObRawExpr *, 4> col_exprs;
|
|
ObSEArray<ObRawExpr *, 4> sel_exprs;
|
|
transed_stmt->get_column_exprs(drill_down_table->table_id_, col_exprs);
|
|
transed_stmt->get_select_exprs(sel_exprs);
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (OB_FAIL(main_stmt->remove_column_item(roll_up_table->table_id_))) {
|
|
LOG_WARN("remove column item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_table_item(roll_up_table))) {
|
|
LOG_WARN("remove table item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_from_item(roll_up_table->table_id_))) {
|
|
LOG_WARN("remove from item failed", K(ret));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::check_join_push_down(ObDMLStmt *main_stmt,
|
|
TableItem *view_table,
|
|
TableItem *push_down_table,
|
|
ObIArray<ObRawExpr *> &cond_to_push_down,
|
|
bool &is_valid)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
int64_t view_table_idx = -1;
|
|
int64_t push_down_table_idx = -1;
|
|
EqualSets dummy_set;
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(view_table) || OB_ISNULL(view_table->ref_query_) ||
|
|
OB_ISNULL(push_down_table)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("view table ref query is null", K(ret));
|
|
} else if (view_table->ref_query_->has_group_by() ||
|
|
view_table->ref_query_->has_limit() ||
|
|
view_table->ref_query_->is_hierarchical_query() ||
|
|
view_table->ref_query_->has_rollup() ||
|
|
view_table->ref_query_->is_set_stmt() ||
|
|
view_table->ref_query_->has_sequence() ||
|
|
!view_table->ref_query_->has_window_function()) {
|
|
is_valid = false;
|
|
} else {
|
|
// to calculate the parition exprs intersection among win functions.
|
|
ObSEArray<ObSEArray<ObRawExpr *, 4>, 2> partition_expr_sets;
|
|
ObSEArray<ObRawExpr *, 4> intersection;
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < view_table->ref_query_->get_window_func_exprs().count(); i++) {
|
|
ObSEArray<ObRawExpr *, 4> parition_exprs;
|
|
if (OB_FAIL(append(parition_exprs, view_table->ref_query_->get_window_func_expr(i)->get_partition_exprs()))) {
|
|
LOG_WARN("append partition exprs failed", K(ret));
|
|
} else if (OB_FAIL(partition_expr_sets.push_back(parition_exprs))) {
|
|
LOG_WARN("failed to push back exprs into array", K(ret));
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObOptimizerUtil::intersect(partition_expr_sets, intersection))) {
|
|
LOG_WARN("failed to calculate the intersection of sets", K(ret));
|
|
} else if (intersection.count() == 0) {
|
|
is_valid = false;
|
|
}
|
|
|
|
ObSEArray<ObRawExpr *, 4> intersection_in_parent;
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < intersection.count(); i++) {
|
|
ObRawExpr *parent_stmt_expr = NULL;
|
|
if (OB_FAIL(ObOptimizerUtil::get_parent_stmt_expr(dummy_set, view_table->table_id_, *main_stmt,
|
|
*view_table->ref_query_, intersection.at(i),
|
|
parent_stmt_expr))) {
|
|
LOG_WARN("get parent stmt expr failed", K(ret));
|
|
} else if (parent_stmt_expr == NULL) {
|
|
//do nothing
|
|
} else if (OB_FAIL(intersection_in_parent.push_back(parent_stmt_expr))) {
|
|
LOG_WARN("failed to push back intersection in parent", K(ret));
|
|
}
|
|
}
|
|
//select * from T2, (select ..., agg(..) over(parition by c1,c2) from T1) v where T2.c1 = v.c1
|
|
//to push down T2
|
|
//1. the join condition should be equal condition
|
|
//2. the join column c from v should be in the partition exprs intersection. It can be function f(c).
|
|
//3. the set of join column from T2 should be unique. It can't be f(c), like abs(c) may break the unique property.
|
|
ObSEArray<ObRawExpr *, 4> exprs_unqiue_check;
|
|
bool is_unique = false;
|
|
if (OB_FAIL(ret) || !is_valid) {
|
|
//do nothing
|
|
} else if (intersection_in_parent.count() <= 0) {
|
|
is_valid = false;
|
|
} else if (OB_FAIL(main_stmt->get_table_item_idx(view_table->table_id_, view_table_idx))) {
|
|
LOG_WARN("get table item idx failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->get_table_item_idx(push_down_table->table_id_, push_down_table_idx))) {
|
|
LOG_WARN("get table item idx failed", K(ret));
|
|
} else {
|
|
view_table_idx++;
|
|
push_down_table_idx++;
|
|
}
|
|
|
|
for (int64_t i = 0; OB_SUCC(ret) && is_valid && i < main_stmt->get_condition_size(); i++) {
|
|
ObRawExpr *expr = main_stmt->get_condition_expr(i);
|
|
|
|
if (OB_ISNULL(expr)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
//if has condition like T1.c1 op v.c1 op T3.c2 T2 could not be push down.
|
|
} else if (expr->get_relation_ids().num_members() > 2 &&
|
|
expr->get_relation_ids().has_member(view_table_idx) &&
|
|
expr->get_relation_ids().has_member(push_down_table_idx)) {
|
|
is_valid = false;
|
|
//for condition like T2.c1 = const, it could be push down.
|
|
} else if (expr->get_relation_ids().num_members() == 1 &&
|
|
expr->get_relation_ids().has_member(push_down_table_idx)) {
|
|
if (OB_FAIL(cond_to_push_down.push_back(expr))) {
|
|
LOG_WARN("failed to push expr into array",K(ret));
|
|
}
|
|
// for condition like v.c1 op T2.c1,
|
|
//we now only support f(v.c1)=T2.c1 for simple, v.c1+T2.c1+1=0 won't be recognized.
|
|
} else if (expr->get_relation_ids().num_members() == 2 &&
|
|
expr->get_relation_ids().has_member(view_table_idx) &&
|
|
expr->get_relation_ids().has_member(push_down_table_idx)) {
|
|
ObSEArray<ObRawExpr *, 4> column_exprs;
|
|
if (!expr->is_op_expr() || expr->get_param_count() < 2) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(expr->get_param_expr(0)) ||
|
|
OB_ISNULL(expr->get_param_expr(1))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("expr's param is null", K(ret));
|
|
} else if (OB_FAIL(ObRawExprUtils::extract_column_exprs(expr, column_exprs))) {
|
|
LOG_WARN("extract column exprs failed", K(ret));
|
|
} else {
|
|
bool found = true;
|
|
for (int64_t j = 0; OB_SUCC(ret) && found && j < column_exprs.count(); j++) {
|
|
int64_t idx =-1;
|
|
if (static_cast<ObColumnRefRawExpr *>(column_exprs.at(j))->get_table_id() != view_table->table_id_) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::get_expr_idx(intersection_in_parent,
|
|
column_exprs.at(j), idx))) {
|
|
LOG_WARN("get expr idx failed", K(ret));
|
|
} else if (idx == -1) {
|
|
found = false;
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret) || !found) {
|
|
//do nothing
|
|
} else if (OB_FAIL(cond_to_push_down.push_back(expr))) {
|
|
LOG_WARN("failed to push expr into array",K(ret));
|
|
} else if (T_OP_EQ == expr->get_expr_type() &&
|
|
expr->get_param_expr(0)->get_relation_ids().num_members() == 1 &&
|
|
expr->get_param_expr(1)->get_relation_ids().num_members() == 1) {
|
|
if (expr->get_param_expr(0)->get_relation_ids().has_member(push_down_table_idx)) {
|
|
// expr0 is from view , expr1 is from push down table.
|
|
std::swap(expr->get_param_expr(0), expr->get_param_expr(1));
|
|
}
|
|
|
|
if (OB_FAIL(exprs_unqiue_check.push_back(expr->get_param_expr(1)))) {
|
|
LOG_WARN("failed to push back expr into array", K(ret));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret) || !is_valid) {
|
|
//do nothing
|
|
} else if (OB_FAIL(ObTransformUtils::check_exprs_unique(*main_stmt, push_down_table, exprs_unqiue_check,
|
|
ctx_->session_info_, ctx_->schema_checker_, is_valid))) {
|
|
LOG_WARN("check exprs unique failed", K(ret));
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//cost based trans, treat as rule trans here
|
|
int ObTransformWinMagic::try_to_push_down_join(ObDMLStmt *&main_stmt)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
if (OB_ISNULL(main_stmt)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < main_stmt->get_from_item_size(); i++) {
|
|
FromItem view_from = main_stmt->get_from_item(i);
|
|
TableItem *view_table = NULL;
|
|
if (view_from.is_joined_) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(view_table = main_stmt->get_table_item_by_id(view_from.table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get table item by id failed", K(ret), K(view_from), K(i));
|
|
} else if (!view_table->is_generated_table()) {
|
|
//do nothing
|
|
} else {
|
|
for (int64_t j = 0; OB_SUCC(ret) && j < main_stmt->get_from_item_size(); j++) {
|
|
bool is_valid = true;
|
|
TableItem *push_down_table = NULL;
|
|
FromItem push_down_from = main_stmt->get_from_item(j);
|
|
ObSEArray<ObRawExpr *, 8> cond_to_push_down;
|
|
if (i == j) {
|
|
//do nothing
|
|
} else if (push_down_from.is_joined_) {
|
|
//do nothing
|
|
} else if (OB_ISNULL(push_down_table = main_stmt->get_table_item_by_id(push_down_from.table_id_))) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("get table item by id failed", K(ret), K(i), K(j));
|
|
//do nothing
|
|
} else if (OB_FAIL(check_join_push_down(main_stmt, view_table,
|
|
push_down_table, cond_to_push_down, is_valid))) {
|
|
LOG_WARN("check join push down failed", K(ret));
|
|
} else if (!is_valid) {
|
|
// do nothing
|
|
} else if (OB_FAIL(push_down_join(main_stmt, view_table, push_down_table, cond_to_push_down))) {
|
|
LOG_WARN("push down join failed", K(ret));
|
|
} else {
|
|
//do nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ObTransformWinMagic::push_down_join(ObDMLStmt *main_stmt,
|
|
TableItem *view_table,
|
|
TableItem *push_down_table,
|
|
ObIArray<ObRawExpr *> &cond_to_push_down)
|
|
{
|
|
int ret = OB_SUCCESS;
|
|
ObSelectStmt *view_stmt = NULL;
|
|
ObSEArray<ObRawExpr *, 4> renamed_cond;
|
|
ObSEArray<ObDMLStmt::PartExprItem,4> part_exprs;
|
|
ObSEArray<ObRawExpr *, 4> view_select_list;
|
|
ObSEArray<ObRawExpr *, 4> view_column_list;
|
|
ObSEArray<ObQueryRefRawExpr *, 4> query_refs;
|
|
ObSEArray<ObRawExpr *, 4> old_column;
|
|
ObSEArray<ObRawExpr *, 4> new_column;
|
|
if (OB_ISNULL(main_stmt) || OB_ISNULL(view_table) || OB_ISNULL(push_down_table) ||
|
|
OB_ISNULL(view_stmt = view_table->ref_query_) || OB_ISNULL(ctx_) || OB_ISNULL(ctx_->expr_factory_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("pointer is null", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::extract_query_ref_expr(cond_to_push_down, query_refs))) {
|
|
LOG_WARN("failed to extract subquery", K(ret));
|
|
} else if (OB_FAIL(main_stmt->get_view_output(*view_table, view_select_list, view_column_list))) {
|
|
LOG_WARN("get view output failed", K(ret));
|
|
} else {
|
|
ObRawExprCopier copier(*ctx_->expr_factory_);
|
|
if (OB_FAIL(copier.add_replaced_expr(view_column_list, view_select_list))) {
|
|
LOG_WARN("failed to add exprs", K(ret));
|
|
} else if (OB_FAIL(copier.add_skipped_expr(query_refs, false))) {
|
|
LOG_WARN("failed to add skipped expr", K(ret));
|
|
} else if (OB_FAIL(copier.copy_on_replace(cond_to_push_down, renamed_cond))) {
|
|
LOG_WARN("failed to copy on replace filters", K(ret));
|
|
}
|
|
}
|
|
if (OB_FAIL(ret)) {
|
|
} else if (OB_FAIL(ObTransformUtils::add_table_item(view_stmt, push_down_table))) {
|
|
LOG_WARN("add table item failed", K(ret));
|
|
} else if (OB_FAIL(append(view_stmt->get_condition_exprs(), renamed_cond))) {
|
|
LOG_WARN("append cond to push down after condition exprs failed", K(ret));
|
|
} else if (OB_FAIL(ObOptimizerUtil::remove_item(main_stmt->get_condition_exprs(), cond_to_push_down))) {
|
|
LOG_WARN("remove item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_table_item(push_down_table))) {
|
|
LOG_WARN("remove tbale item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_from_item(push_down_table->table_id_))) {
|
|
LOG_WARN("remove from item faield", K(ret));
|
|
} else if (OB_FAIL(view_stmt->add_from_item(push_down_table->table_id_))) {
|
|
LOG_WARN("add from item failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->get_part_expr_items(push_down_table->table_id_, part_exprs))) {
|
|
LOG_WARN("failed to get part expr items", K(ret));
|
|
} else if (!part_exprs.empty() && OB_FAIL(view_stmt->set_part_expr_items(part_exprs))) {
|
|
LOG_WARN("failed to set part expr item", K(ret));
|
|
} else if (OB_FAIL(main_stmt->remove_part_expr_items(push_down_table->table_id_))) {
|
|
LOG_WARN("failed to remove part epxr", K(ret));
|
|
}
|
|
for (int64_t i = 0; OB_SUCC(ret) && i < main_stmt->get_column_size(); i++) {
|
|
if (OB_ISNULL(main_stmt->get_column_item(i)) || OB_ISNULL(main_stmt->get_column_item(i)->expr_)) {
|
|
ret = OB_ERR_UNEXPECTED;
|
|
LOG_WARN("column is null", K(ret));
|
|
} else if (main_stmt->get_column_item(i)->expr_->get_table_id() == push_down_table->table_id_) {
|
|
if (OB_FAIL(view_stmt->add_column_item(*main_stmt->get_column_item(i)))) {
|
|
LOG_WARN("add column item failed", K(ret));
|
|
} else if (OB_FAIL(old_column.push_back(main_stmt->get_column_item(i)->expr_))) {
|
|
LOG_WARN("push back expr into array failed", K(ret));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OB_FAIL(ret)) {
|
|
//do nothing
|
|
} else if (OB_FAIL(main_stmt->remove_column_item(push_down_table->table_id_))) {
|
|
LOG_WARN("remove column item failed", K(ret));
|
|
} else if (OB_FAIL(ObTransformUtils::create_columns_for_view(
|
|
ctx_, *view_table, main_stmt, old_column, new_column))) {
|
|
LOG_WARN("create columns for view failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->replace_relation_exprs(old_column, new_column))) {
|
|
LOG_WARN("replace inner stmt expr failed", K(ret));
|
|
} else if (OB_FAIL(main_stmt->rebuild_tables_hash())) {
|
|
LOG_WARN("failed to rebuild table hash", K(ret));
|
|
} else if (OB_FAIL(main_stmt->update_column_item_rel_id())) {
|
|
LOG_WARN("failed to update column item relation id", K(ret));
|
|
} else if (OB_FAIL(view_stmt->rebuild_tables_hash())) {
|
|
LOG_WARN("failed to rebuild table hash", K(ret));
|
|
} else if (OB_FAIL(view_stmt->update_column_item_rel_id())) {
|
|
LOG_WARN("failed to update column item relation id", K(ret));
|
|
} else if (OB_FAIL(main_stmt->formalize_stmt(ctx_->session_info_))) {
|
|
LOG_WARN("failed to formalize stmt info", K(ret));
|
|
} else {
|
|
LOG_DEBUG("push down join", K(*main_stmt));
|
|
}
|
|
return ret;
|
|
}
|