diff --git a/src/sql/optimizer/ob_optimizer_util.cpp b/src/sql/optimizer/ob_optimizer_util.cpp index 4d56dfb94..8c061715d 100644 --- a/src/sql/optimizer/ob_optimizer_util.cpp +++ b/src/sql/optimizer/ob_optimizer_util.cpp @@ -6802,7 +6802,8 @@ int ObOptimizerUtil::check_pushdown_filter_for_set(const ObSelectStmt &parent_st LOG_WARN("predicate is null", K(ret)); } else if (OB_FAIL(ObRawExprUtils::extract_set_op_exprs(pred, set_op_exprs))) { LOG_WARN("failed to extract set op exprs", K(ret)); - } else if (OB_FAIL(ObTransformUtils::check_pushdown_into_set_valid(pred, + } else if (OB_FAIL(ObTransformUtils::check_pushdown_into_set_valid(&subquery, + pred, set_op_exprs, is_simple_expr))) { LOG_WARN("failed to check pushdown into set", K(ret)); diff --git a/src/sql/rewrite/ob_transform_predicate_move_around.cpp b/src/sql/rewrite/ob_transform_predicate_move_around.cpp index fd61b3415..c50593d15 100644 --- a/src/sql/rewrite/ob_transform_predicate_move_around.cpp +++ b/src/sql/rewrite/ob_transform_predicate_move_around.cpp @@ -1670,8 +1670,8 @@ int ObTransformPredicateMoveAround::pushdown_into_set_stmt(ObSelectStmt *stmt, ObSEArray invalid_pushdown_preds; ObSEArray invalid_pullup_preds; const int64_t pushdown_preds_cnt = pushdown_preds.count(); - if (OB_FAIL(extract_valid_preds(parent_stmt, pushdown_preds, valid_preds, invalid_pushdown_preds)) - || OB_FAIL(extract_valid_preds(parent_stmt, pullup_preds, valid_preds, invalid_pullup_preds))) { + if (OB_FAIL(extract_valid_preds(parent_stmt, stmt, pushdown_preds, valid_preds, invalid_pushdown_preds)) + || OB_FAIL(extract_valid_preds(parent_stmt, stmt, pullup_preds, valid_preds, invalid_pullup_preds))) { LOG_WARN("failed to check push down", K(ret)); } else if (OB_FAIL(rename_preds.assign(valid_preds))) { LOG_WARN("failed to assign rename preds", K(ret)); @@ -1722,6 +1722,7 @@ int ObTransformPredicateMoveAround::pushdown_into_set_stmt(ObSelectStmt *stmt, * @return int */ int ObTransformPredicateMoveAround::extract_valid_preds(ObSelectStmt *stmt, + ObSelectStmt *child_stmt, ObIArray &all_preds, ObIArray &valid_preds, ObIArray &invalid_preds) @@ -1745,7 +1746,8 @@ int ObTransformPredicateMoveAround::extract_valid_preds(ObSelectStmt *stmt, is_valid = false; } if (OB_SUCC(ret) && is_valid) { - if (OB_FAIL(ObTransformUtils::check_pushdown_into_set_valid(expr, + if (OB_FAIL(ObTransformUtils::check_pushdown_into_set_valid(child_stmt, + expr, parent_set_exprs, is_valid))) { LOG_WARN("failed to check expr pushdown validity", K(ret)); diff --git a/src/sql/rewrite/ob_transform_predicate_move_around.h b/src/sql/rewrite/ob_transform_predicate_move_around.h index fce33a573..64e174778 100644 --- a/src/sql/rewrite/ob_transform_predicate_move_around.h +++ b/src/sql/rewrite/ob_transform_predicate_move_around.h @@ -179,6 +179,7 @@ private: ObIArray &output_pushdown_preds); int extract_valid_preds(ObSelectStmt *stmt, + ObSelectStmt *child_stmt, ObIArray &all_preds, ObIArray &valid_exprs, ObIArray &invalid_exprs); diff --git a/src/sql/rewrite/ob_transform_query_push_down.cpp b/src/sql/rewrite/ob_transform_query_push_down.cpp index 974b54e0b..a4cafa814 100644 --- a/src/sql/rewrite/ob_transform_query_push_down.cpp +++ b/src/sql/rewrite/ob_transform_query_push_down.cpp @@ -620,6 +620,7 @@ int ObTransformQueryPushDown::check_select_item_subquery(ObSelectStmt &select_st int ret = OB_SUCCESS; can_be = true; ObSEArray column_exprs_from_subquery; + ObSEArray query_ref_exprs; ObRawExpr *expr = NULL; TableItem *table = NULL; ObSqlBitSet<> table_set; @@ -628,12 +629,25 @@ int ObTransformQueryPushDown::check_select_item_subquery(ObSelectStmt &select_st ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect select stmt", K(ret), K(select_stmt.get_from_item_size()), K(table)); } - for (int64_t i = 0; OB_SUCC(ret) && i < view.get_select_item_size(); ++i) { + for (int64_t i = 0; OB_SUCC(ret) && can_be && i < view.get_select_item_size(); ++i) { if (OB_ISNULL(expr = view.get_select_item(i).expr_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("unexpect null expr", K(ret)); } else if (!expr->has_flag(CNT_SUB_QUERY)) { /* do nothing */ + } else if (expr->has_flag(IS_WITH_ANY) || expr->has_flag(IS_WITH_ALL) || + expr->get_expr_type() == T_OP_EXISTS || expr->get_expr_type() == T_OP_NOT_EXISTS) { + /* + * Disable query pushdown when a select item of view is `any/all/exists subquery` form. + * As for the query below, after pushing down, v.c2 will be used for both projection and filtering, + * which may result in an incorrect stmt status in the subsequent rewrite loop. + * e.g. select * from + * (select t1.c1, (exists(select 1 from t2 where (t1.c1 = t2.c1))) as c2 from t1) v + * where v.c2 is true; + */ + can_be = false; + } else if (OB_FAIL(ObTransformUtils::extract_query_ref_expr(expr, query_ref_exprs, true))) { + LOG_WARN("failed to extract query ref exprs", K(ret)); } else if (OB_ISNULL(expr = select_stmt.get_column_expr_by_id(table->table_id_, i + OB_APP_MIN_COLUMN_ID))) { /* do nothing */ @@ -641,7 +655,24 @@ int ObTransformQueryPushDown::check_select_item_subquery(ObSelectStmt &select_st LOG_WARN("failed to push back column expr", K(ret)); } } - if (OB_FAIL(ret)) { + + // check query ref exprs of view's select items + if (OB_FAIL(ret) || !can_be) { + /* do nothing */ + } else if (query_ref_exprs.count() > 0) { + for (int64_t i = 0; OB_SUCC(ret) && can_be && i < query_ref_exprs.count(); i++) { + ObQueryRefRawExpr* query_ref = NULL; + if (OB_ISNULL(query_ref = query_ref_exprs.at(i))) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected null pointer", K(ret)); + } else if (query_ref->is_set() || query_ref->get_output_column() != 1) { + can_be = false; + } + } + } + + // check query ref exprs of upper select stmt + if (OB_FAIL(ret) || !can_be) { } else if (column_exprs_from_subquery.empty()) { /* do nothing */ } else if (OB_FAIL(select_stmt.get_table_rel_ids(*table, table_set))) { diff --git a/src/sql/rewrite/ob_transform_utils.cpp b/src/sql/rewrite/ob_transform_utils.cpp index da13792d8..7b35cf157 100644 --- a/src/sql/rewrite/ob_transform_utils.cpp +++ b/src/sql/rewrite/ob_transform_utils.cpp @@ -13838,13 +13838,15 @@ int ObTransformUtils::check_convert_string_safely(const ObRawExpr *expr, // Q2: select * from (select * from t1 intersect select * from t2) v where concat(c1,'a') = 'aa'; // only predicate in Q1 can be pushdown into set stmt // TODO: sean.yyj, concat(c1,'a') = 'aa' can be pushdown into UNION after solved collation level bug -int ObTransformUtils::check_pushdown_into_set_valid(ObRawExpr *expr, +int ObTransformUtils::check_pushdown_into_set_valid(const ObSelectStmt* child_stmt, + ObRawExpr *expr, const ObIArray &set_op_exprs, bool &is_valid) { int ret = OB_SUCCESS; ObSEArray parent_exprs; - if (OB_FAIL(recursive_check_pushdown_into_set_valid(expr, + if (OB_FAIL(recursive_check_pushdown_into_set_valid(child_stmt, + expr, set_op_exprs, parent_exprs, is_valid))) { @@ -13854,6 +13856,7 @@ int ObTransformUtils::check_pushdown_into_set_valid(ObRawExpr *expr, } int ObTransformUtils::recursive_check_pushdown_into_set_valid( + const ObSelectStmt* child_stmt, ObRawExpr *expr, const ObIArray &set_op_exprs, ObIArray &parent_exprs, @@ -13869,6 +13872,8 @@ int ObTransformUtils::recursive_check_pushdown_into_set_valid( } else if (ObOptimizerUtil::find_item(set_op_exprs, expr)) { if (OB_FAIL(ObTransformUtils::check_can_replace(expr, parent_exprs, false, is_valid))) { LOG_WARN("failed to check can replace expr", K(ret)); + } else if (is_valid && check_child_projection_validity(child_stmt, expr, is_valid)) { + LOG_WARN("failed to check push to set child validity", K(ret)); } } else if (OB_UNLIKELY(expr->is_set_op_expr())) { ret = OB_ERR_UNEXPECTED; @@ -13878,7 +13883,8 @@ int ObTransformUtils::recursive_check_pushdown_into_set_valid( if (OB_FAIL(parent_exprs.push_back(expr))) { LOG_WARN("failed to push back", K(ret)); } - if (OB_FAIL(SMART_CALL(recursive_check_pushdown_into_set_valid(expr->get_param_expr(i), + if (OB_FAIL(SMART_CALL(recursive_check_pushdown_into_set_valid(child_stmt, + expr->get_param_expr(i), set_op_exprs, parent_exprs, is_valid)))) { @@ -13892,5 +13898,40 @@ int ObTransformUtils::recursive_check_pushdown_into_set_valid( return ret; } +int ObTransformUtils::check_child_projection_validity(const ObSelectStmt *child_stmt, + ObRawExpr *expr, + bool &is_valid) +{ + int ret = OB_SUCCESS; + is_valid = true; + ObSetOpRawExpr* set_op_expr = NULL; + if (OB_ISNULL(child_stmt) || OB_ISNULL(expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected null pointer", K(ret), K(child_stmt), K(expr)); + } else if (OB_UNLIKELY(!expr->is_set_op_expr())) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected expr type", K(ret), K(expr->get_expr_type())); + } else if (OB_FALSE_IT(set_op_expr = static_cast(expr))) { + } else { + int64_t proj_idx = set_op_expr->get_idx(); + ObRawExpr *proj_expr = NULL; + if (proj_idx < 0 || proj_idx >= child_stmt->get_select_item_size()) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected index of select items", K(ret), K(proj_idx), K(child_stmt->get_select_item_size())); + } else if (OB_ISNULL(proj_expr = child_stmt->get_select_item(proj_idx).expr_)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected null pointer", K(ret)); + } else if (proj_expr->has_flag(CNT_SUB_QUERY)) { + /* + * disable pushdown predicates that reference a select item containing subquery in set query. + * e.g. select * from (select 1 a, exists(select 1 from t2 where t1.c1=t2.c1) b from t1 + * union all select c1,c2 from t1) v where b is true; + */ + is_valid = false; + } + } + return ret; +} + } // namespace sql } // namespace oceanbase diff --git a/src/sql/rewrite/ob_transform_utils.h b/src/sql/rewrite/ob_transform_utils.h index 2b6e5f379..9ad418136 100644 --- a/src/sql/rewrite/ob_transform_utils.h +++ b/src/sql/rewrite/ob_transform_utils.h @@ -1824,17 +1824,22 @@ public: bool used_in_compare, bool &can_replace); - static int check_pushdown_into_set_valid(ObRawExpr *expr, + static int check_pushdown_into_set_valid(const ObSelectStmt* child_stmt, + ObRawExpr *expr, const ObIArray &set_op_exprs, bool &is_valid); - static int recursive_check_pushdown_into_set_valid(ObRawExpr *expr, + static int recursive_check_pushdown_into_set_valid(const ObSelectStmt* child_stmt, + ObRawExpr *expr, const ObIArray &set_op_exprs, ObIArray &parent_exprs, bool &is_valid); static int get_explicated_ref_columns(const uint64_t table_id, ObDMLStmt *stmt, ObIArray &table_cols); + static int check_child_projection_validity(const ObSelectStmt *child_stmt, + ObRawExpr *expr, + bool &is_valid); private: static int inner_get_lazy_left_join(ObDMLStmt *stmt, TableItem *table,