diff --git a/src/sql/optimizer/ob_optimizer_util.cpp b/src/sql/optimizer/ob_optimizer_util.cpp index bf8c270801..7c514b2abc 100644 --- a/src/sql/optimizer/ob_optimizer_util.cpp +++ b/src/sql/optimizer/ob_optimizer_util.cpp @@ -1998,7 +1998,80 @@ int ObOptimizerUtil::get_subquery_id(const ObDMLStmt* upper_stmt, const ObSelect return ret; } -int ObOptimizerUtil::is_table_on_null_side(const ObDMLStmt* stmt, uint64_t table_id, bool& is_on_null_side) +/** + * @brief + * given source and target table id, try to find its lowest common joined table + * then check if the source table is on the null side of the joined table + * @param stmt + * @param source_table_id + * @param target_table_id + * @param is_on_null_side + * @return int + */ +int ObOptimizerUtil::is_table_on_null_side_of_parent( + const ObDMLStmt *stmt, uint64_t source_table_id, uint64_t target_table_id, bool &is_on_null_side) +{ + int ret = OB_SUCCESS; + is_on_null_side = false; + if (OB_ISNULL(stmt)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("upper_stmt is null", K(ret)); + } else { + JoinedTable *common_joined_table = NULL; + for (int64_t i = 0; OB_SUCC(ret) && OB_ISNULL(common_joined_table) && i < stmt->get_joined_tables().count(); ++i) { + JoinedTable *joined_table = stmt->get_joined_tables().at(i); + bool is_source_in_joined_table = false; + if (OB_ISNULL(joined_table)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get unexpected null", K(ret)); + } else if (OB_FAIL( + find_common_joined_table(joined_table, source_table_id, target_table_id, common_joined_table))) { + LOG_WARN("failed to find target joined table", K(ret)); + } else if (common_joined_table != NULL && + OB_FAIL(is_table_on_null_side_recursively( + common_joined_table, source_table_id, is_source_in_joined_table, is_on_null_side))) { + LOG_WARN("Check for generated table on null side recursively fails", K(is_on_null_side), K(i), K(ret)); + } + } + } + return ret; +} + +int ObOptimizerUtil::find_common_joined_table( + JoinedTable *joined_table, uint64_t source_table_id, uint64_t target_table_id, JoinedTable *&target_joined_table) +{ + int ret = OB_SUCCESS; + TableItem *left_table = NULL; + TableItem *right_table = NULL; + if (OB_ISNULL(joined_table)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get unexpected null", K(ret)); + } else if (OB_ISNULL(left_table = joined_table->left_table_) || OB_ISNULL(right_table = joined_table->right_table_)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get unexpected null", K(ret), K(left_table), K(right_table)); + } else if (is_contain(joined_table->single_table_ids_, source_table_id) && + is_contain(joined_table->single_table_ids_, target_table_id)) { + target_joined_table = joined_table; + JoinedTable *left_common_table = NULL; + JoinedTable *right_common_table = NULL; + if (left_table->is_joined_table() && + OB_FAIL(find_common_joined_table( + static_cast(left_table), source_table_id, target_table_id, left_common_table))) { + LOG_WARN("failed to find left common joined table", K(ret)); + } else if (left_common_table != NULL) { + target_joined_table = left_common_table; + } else if (right_table->is_joined_table() && + OB_FAIL(find_common_joined_table( + static_cast(right_table), source_table_id, target_table_id, right_common_table))) { + LOG_WARN("failed to find right common joined table", K(ret)); + } else if (right_common_table != NULL) { + target_joined_table = right_common_table; + } + } + return ret; +} + +int ObOptimizerUtil::is_table_on_null_side(const ObDMLStmt *stmt, uint64_t table_id, bool &is_on_null_side) { int ret = OB_SUCCESS; is_on_null_side = false; diff --git a/src/sql/optimizer/ob_optimizer_util.h b/src/sql/optimizer/ob_optimizer_util.h index 685055f494..5a24db383e 100644 --- a/src/sql/optimizer/ob_optimizer_util.h +++ b/src/sql/optimizer/ob_optimizer_util.h @@ -304,8 +304,13 @@ public: static int is_table_on_null_side_recursively( const TableItem* table_item, uint64_t table_id, bool& found, bool& is_on_null_side); - static int get_referenced_columns(const ObDMLStmt* stmt, const uint64_t table_id, - const common::ObIArray& keys, common::ObIArray& columns); + static int is_table_on_null_side_of_parent( + const ObDMLStmt *stmt, uint64_t source_table_id, uint64_t target_table_id, bool &is_on_null_side); + static int find_common_joined_table( + JoinedTable *joined_table, uint64_t source_table_id, uint64_t target_table_id, JoinedTable *&target_joined_table); + + static int get_referenced_columns(const ObDMLStmt *stmt, const uint64_t table_id, + const common::ObIArray &keys, common::ObIArray &columns); static int get_non_referenced_columns(const ObDMLStmt* stmt, const uint64_t table_id, const common::ObIArray& keys, common::ObIArray& columns); diff --git a/src/sql/rewrite/ob_transform_join_elimination.cpp b/src/sql/rewrite/ob_transform_join_elimination.cpp index 4de788b2fe..8c8eacc5ee 100644 --- a/src/sql/rewrite/ob_transform_join_elimination.cpp +++ b/src/sql/rewrite/ob_transform_join_elimination.cpp @@ -97,6 +97,7 @@ int ObTransformJoinElimination::eliminate_join_self_foreign_key(ObDMLStmt *stmt, LOG_WARN("eliminate join in joined table failed", K(ret)); } else { trans_happened = from_happedend | joined_happedend; + LOG_TRACE("succ to do self foreign key elimination", K(from_happedend), K(joined_happedend)); } return ret; } @@ -115,23 +116,27 @@ int ObTransformJoinElimination::eliminate_join_in_from_base_table(ObDMLStmt* stm LOG_WARN("failed to get equal set conditions", K(ret)); } else if (OB_FAIL(extract_candi_table(stmt, stmt->get_from_items(), candi_tables, child_candi_tables))) { LOG_WARN("failed to extract candi tables", K(ret)); - } else if (OB_FAIL(eliminate_candi_tables(stmt, conds, candi_tables, child_candi_tables, trans_happened))) { + } else if (OB_FAIL(eliminate_candi_tables(stmt, conds, candi_tables, child_candi_tables, true, trans_happened))) { LOG_WARN("failed to eliminate candi tables", K(ret)); } return ret; } -int ObTransformJoinElimination::do_join_elimination_self_key(ObDMLStmt* stmt, TableItem* source_table, - TableItem* target_table, bool& trans_happened, - EqualSets* equal_sets) // default value NULL + +int ObTransformJoinElimination::do_join_elimination_self_key(ObDMLStmt *stmt, TableItem *source_table, + TableItem *target_table, bool is_from_base_table, bool &trans_happened, EqualSets *equal_sets /*= NULL*/) { int ret = OB_SUCCESS; trans_happened = false; ObStmtMapInfo stmt_map_info; bool can_be_eliminated = false; + bool is_on_null_side = false; if (OB_ISNULL(stmt) || OB_ISNULL(ctx_) || OB_ISNULL(source_table) || OB_ISNULL(target_table)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("get unexpected null", K(stmt), K(ctx_), K(ret)); + } else if (OB_FAIL(check_on_null_side( + stmt, source_table->table_id_, target_table->table_id_, is_from_base_table, is_on_null_side))) { + LOG_WARN("failed to check table on null side", K(ret)); } else if (OB_FAIL(ObTransformUtils::check_loseless_join(stmt, ctx_, source_table, @@ -139,6 +144,7 @@ int ObTransformJoinElimination::do_join_elimination_self_key(ObDMLStmt* stmt, Ta ctx_->session_info_, ctx_->schema_checker_, stmt_map_info, + is_on_null_side, can_be_eliminated, equal_sets))) { LOG_WARN("failed to check whether transformation is possible", K(ret)); @@ -157,8 +163,29 @@ int ObTransformJoinElimination::do_join_elimination_self_key(ObDMLStmt* stmt, Ta return ret; } -int ObTransformJoinElimination::do_join_elimination_foreign_key(ObDMLStmt* stmt, const TableItem* child_table, - const TableItem* parent_table, const ObForeignKeyInfo* foreign_key_info) +int ObTransformJoinElimination::check_on_null_side( + ObDMLStmt *stmt, uint64_t source_table_id, uint64_t target_table_id, bool is_from_base_table, bool &is_on_null_side) +{ + int ret = OB_SUCCESS; + is_on_null_side = false; + if (OB_ISNULL(stmt)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get unexpected null", K(ret)); + } else if (is_from_base_table) { + if (OB_FAIL(ObOptimizerUtil::is_table_on_null_side(stmt, source_table_id, is_on_null_side))) { + LOG_WARN("failed to check table is on null side", K(ret)); + } + } else { + if (OB_FAIL(ObOptimizerUtil::is_table_on_null_side_of_parent( + stmt, source_table_id, target_table_id, is_on_null_side))) { + LOG_WARN("failed to check table is on null side", K(ret)); + } + } + return ret; +} + +int ObTransformJoinElimination::do_join_elimination_foreign_key(ObDMLStmt *stmt, const TableItem *child_table, + const TableItem *parent_table, const ObForeignKeyInfo *foreign_key_info) { int ret = OB_SUCCESS; if (OB_ISNULL(stmt)) { @@ -1706,7 +1733,8 @@ int ObTransformJoinElimination::eliminate_join_in_joined_table(ObDMLStmt* stmt, } if (OB_FAIL(ret)) { - } else if (OB_FAIL(eliminate_candi_tables(stmt, inner_join_conds, other_tables, child_candi_tables, is_happened))) { + } else if (OB_FAIL(eliminate_candi_tables( + stmt, inner_join_conds, other_tables, child_candi_tables, false, is_happened))) { LOG_WARN("failed to eliminate candi tables", K(ret)); } else if (OB_FAIL(append(outer_join_tables, other_tables))) { LOG_WARN("failed to append tables", K(ret)); @@ -1724,8 +1752,9 @@ int ObTransformJoinElimination::eliminate_join_in_joined_table(ObDMLStmt* stmt, return ret; } -int ObTransformJoinElimination::eliminate_candi_tables(ObDMLStmt* stmt, ObIArray& conds, - ObIArray& candi_tables, ObIArray& child_candi_tables, bool& trans_happened) +int ObTransformJoinElimination::eliminate_candi_tables(ObDMLStmt *stmt, ObIArray &conds, + ObIArray &candi_tables, ObIArray &child_candi_tables, bool is_from_base_table, + bool &trans_happened) { int ret = OB_SUCCESS; trans_happened = false; @@ -1751,7 +1780,7 @@ int ObTransformJoinElimination::eliminate_candi_tables(ObDMLStmt* stmt, ObIArray } else if (!is_valid) { // do nothing } else if (OB_FAIL(do_join_elimination_self_key( - stmt, candi_tables.at(i), candi_tables.at(j), is_happened, equal_sets))) { + stmt, candi_tables.at(i), candi_tables.at(j), is_from_base_table, is_happened, equal_sets))) { LOG_WARN("failed to eliminate self key join in base table", K(ret)); } else if (!is_happened) { /*do nothing*/ @@ -1768,8 +1797,12 @@ int ObTransformJoinElimination::eliminate_candi_tables(ObDMLStmt* stmt, ObIArray LOG_WARN("check table can be eliminated failed", K(ret)); } else if (!is_valid) { // do nothing - } else if (OB_FAIL(do_join_elimination_self_key( - stmt, child_candi_tables.at(j), candi_tables.at(i), is_happened, equal_sets))) { + } else if (OB_FAIL(do_join_elimination_self_key(stmt, + child_candi_tables.at(j), + candi_tables.at(i), + is_from_base_table, + is_happened, + equal_sets))) { LOG_WARN("failed to do join elimination erlf key", K(ret)); } else if (!is_happened) { /*do nothing*/ diff --git a/src/sql/rewrite/ob_transform_join_elimination.h b/src/sql/rewrite/ob_transform_join_elimination.h index f18b12f0dd..8cd0241b9d 100644 --- a/src/sql/rewrite/ob_transform_join_elimination.h +++ b/src/sql/rewrite/ob_transform_join_elimination.h @@ -69,7 +69,7 @@ private: /** * @brief eliminate_join_self_key - * cases that can be eliminated for self key join: + * cases that can be eliminated for self key join * basic_table or generate_table within STMT FROM ITEMS * basic_table or generate_table within SEMI ITEMS * inner join tables within SEMI ITEMS or STMT ITEMS @@ -98,11 +98,14 @@ private: int eliminate_join_in_joined_table(ObDMLStmt* stmt, TableItem*& table_item, ObIArray& child_candi_tables, ObIArray& trans_conditions, bool& trans_happened); - int eliminate_candi_tables(ObDMLStmt* stmt, ObIArray& conds, ObIArray& candi_tables, - ObIArray& child_candi_tables, bool& trans_happened); + int eliminate_candi_tables(ObDMLStmt *stmt, ObIArray &conds, ObIArray &candi_tables, + ObIArray &child_candi_tables, bool is_from_base_table, bool &trans_happened); - int do_join_elimination_self_key(ObDMLStmt* stmt, TableItem* source_table, TableItem* target_table, - bool& trans_happened, EqualSets* equal_sets = NULL); + int check_on_null_side(ObDMLStmt *stmt, uint64_t source_table_id, uint64_t target_table_id, bool is_from_base_table, + bool &is_on_null_side); + + int do_join_elimination_self_key(ObDMLStmt *stmt, TableItem *source_table, TableItem *target_table, + bool is_from_base_table, bool &trans_happened, EqualSets *equal_sets = NULL); int do_join_elimination_foreign_key(ObDMLStmt* stmt, const TableItem* child_table, const TableItem* parent_table, const share::schema::ObForeignKeyInfo* foreign_key_info); diff --git a/src/sql/rewrite/ob_transform_utils.cpp b/src/sql/rewrite/ob_transform_utils.cpp index fa55123f92..93a5125b42 100644 --- a/src/sql/rewrite/ob_transform_utils.cpp +++ b/src/sql/rewrite/ob_transform_utils.cpp @@ -4726,17 +4726,18 @@ bool ObTransformUtils::is_subarray(const ObRelIds& table_ids, const common::ObIA return bool_ret; } -int ObTransformUtils::check_loseless_join(ObDMLStmt* stmt, ObTransformerCtx* ctx, TableItem* source_table, - TableItem* target_table, ObSQLSessionInfo* session_info, ObSchemaChecker* schema_checker, - ObStmtMapInfo& stmt_map_info, bool& is_loseless, - EqualSets* input_equal_sets) // default value NULL +int ObTransformUtils::check_loseless_join(ObDMLStmt *stmt, ObTransformerCtx *ctx, TableItem *source_table, + TableItem *target_table, ObSQLSessionInfo *session_info, ObSchemaChecker *schema_checker, + ObStmtMapInfo &stmt_map_info, bool is_on_null_side, bool &is_loseless, + EqualSets *input_equal_sets /*= NULL*/) { int ret = OB_SUCCESS; bool is_contain = false; bool source_unique = false; bool target_unique = false; - ObSEArray source_exprs; - ObSEArray target_exprs; + ObSEArray source_exprs; + ObSEArray target_exprs; + ObSEArray target_tab_cols; is_loseless = false; if (OB_ISNULL(stmt) || OB_ISNULL(source_table) || OB_ISNULL(target_table) || OB_ISNULL(schema_checker)) { ret = OB_ERR_UNEXPECTED; @@ -4754,6 +4755,10 @@ int ObTransformUtils::check_loseless_join(ObDMLStmt* stmt, ObTransformerCtx* ctx target_exprs, input_equal_sets))) { LOG_WARN("failed to extract lossless join columns", K(ret)); + } else if (OB_FAIL(stmt->get_column_exprs(target_table->table_id_, target_tab_cols))) { + LOG_WARN("failed to get column exprs", K(ret)); + } else if (is_on_null_side && !target_tab_cols.empty() && target_exprs.empty()) { + is_loseless = false; } else if (OB_FAIL(ObTransformUtils::check_exprs_unique( *stmt, source_table, source_exprs, session_info, schema_checker, source_unique))) { LOG_WARN("failed to check exprs unique", K(ret)); diff --git a/src/sql/rewrite/ob_transform_utils.h b/src/sql/rewrite/ob_transform_utils.h index 0a1d115cbc..ec16ae60da 100644 --- a/src/sql/rewrite/ob_transform_utils.h +++ b/src/sql/rewrite/ob_transform_utils.h @@ -548,9 +548,10 @@ public: static bool is_subarray(const ObRelIds& table_ids, const common::ObIArray& other); - static int check_loseless_join(ObDMLStmt* stmt, ObTransformerCtx* ctx, TableItem* source_table, - TableItem* target_table, ObSQLSessionInfo* session_info, ObSchemaChecker* schema_checker, - ObStmtMapInfo& stmt_map_info, bool& is_loseless, EqualSets* input_equal_sets = NULL); + + static int check_loseless_join(ObDMLStmt *stmt, ObTransformerCtx *ctx, TableItem *source_table, + TableItem *target_table, ObSQLSessionInfo *session_info, ObSchemaChecker *schema_checker, + ObStmtMapInfo &stmt_map_info, bool is_on_null_side, bool &is_loseless, EqualSets *input_equal_sets = NULL); static int check_relations_containment(ObDMLStmt* stmt, const common::ObIArray& source_rels, const common::ObIArray& target_rels, common::ObIArray& stmt_map_infos,