Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add convert case when predicate for simplify exprs #1469

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

fhkong
Copy link
Contributor

@fhkong fhkong commented Jul 14, 2023

Task Description

When users write SQL, there maybe some redundant case when expressions. We can rewrite these redundant case when expressions according to rewrite rules. Such that, we can use an index on the column of the case when expression other than full table scan.

Solution Description

According to rewrite rules, the case when expressions that can be rewritten in following condition.
case when exp1 then exp2 else exp3 end ~ exp4
When exp3 ~ exp4 is static const expr, we can remove case when expression.
Such as case when c1 > 0 then c2 else 3 end > 4; ==> c1 > 0 and c2 > 4

Passed Regressions

  • tools/deploy/record/transformer/r/mysql/transformer_convert_case_when_predicate.record

Upgrade Compatibility

Other Information

Release Note

LOG_WARN("unexpected param null", K(parent_expr), K(case_expr), K(sibling_expr), K(ret));
} else {
case_expr = static_cast<ObCaseOpRawExpr*>(case_when_expr);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里还是应该检查一下 case_when_expr 的类型。不能假设上面的传参是对的。

或者就是函数直接传一个 ObCaseOpRawExpr 的指针下来

}
if (OB_SUCC(ret)) {
if (case_expr->get_when_expr_size() != case_expr->get_then_expr_size()) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OB_UNLIKELY

int ObTransformSimplifyExpr::convert_case_when_predicate(
ObRawExpr *&expr,
const bool &is_scala_group_by,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不需要传 const bool 的引用吧

ObOpRawExpr *op_expr = NULL;
ObRawExpr *child_0 = NULL, *child_1 = NULL;
if (OB_FALSE_IT(op_expr = static_cast<ObOpRawExpr *>(expr))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OB_ISNULL(

} else if (OB_FALSE_IT(case_expr = static_cast<ObCaseOpRawExpr *>(expr))) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("failed convert to case when expr");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是恒false的,只是为了代码逻辑这样写,不可能抛出错误

} else if (is_uncalculable) {
case_expr->get_when_param_exprs().assign(when_exprs);
case_expr->get_then_param_exprs().assign(then_exprs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

吞错误码

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

替换完要formalize

}
has_trans = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这一部分逻辑写的太分散了

// IF case_at_left is false, parent_expr := sibling_expr op case_when_expr
int ObTransformSimplifyExpr::do_convert_case_when_predicate(
ObRawExpr *&parent_expr,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ObTransformSimplifyExpr::inner_convert_case_when_predicate 和 ObTransformSimplifyExpr::do_convert_case_when_predicate 不看注释不知道在干什么,还以为是调用关系

/* do nothing */
} else if (OB_FALSE_IT(case_expr = static_cast<ObCaseOpRawExpr *>(case_when_expr))) {
ret = OB_ERR_UNEXPECTED;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

错误码走不到

// LOG_WARN("failed to add cast for replace", K(ret));
} else if (OB_FAIL(ObRawExprUtils::create_double_op_expr(*(ctx_->expr_factory_),
ctx_->session_info_,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在可改写条件判断时就创建大量表达式?

if (OB_ISNULL(stmt)) {
ret = OB_ERR_UNEXPECTED;
LOG_WARN("stmt is NULL", K(*stmt));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print *stmt after check stmt is null? just print ret

and ({expr1 to exprn} \ exprk) op const_value is all false, then
expr can be rewritten to lnnvl(cond1) and .. and lnnvl(condk-1) and condk and (exprk op const_value).
(If k == n, condk is ignored.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too long comments, just show a simple transformation example

ObRawExpr *child_0 = NULL;
ObRawExpr *child_1 = NULL;
if (OB_FALSE_IT(op_expr = static_cast<ObOpRawExpr *>(expr))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (OB_FALSE_IT(op_expr = static_cast<ObOpRawExpr *>(expr))) { is unnecessary. just set op_expr when define it.

} else { /* do nothing */}

for (int64_t i = 0; OB_SUCC(ret) && i < expr->get_param_count(); ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if trans_happened is true, is it necessary to try transform recursively?

ret = OB_ERR_UNEXPECTED;
LOG_WARN("default expr is NULL", K(ret));
} else if (OB_UNLIKELY(false_null_cnt != when_cnt)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

convert_case_when_predicate_by_when_exprs is too complex.
try convert the first when directly, and do this convert for the same expr repeatedly if transform happened.

ObRawExpr *default_expr = NULL;
if (OB_ISNULL(default_expr = case_expr->get_default_param_expr())) {
ret = OB_ERR_UNEXPECTED;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just enum_exprs.assign(get_then_param_exprs()) and enum_exprs.push_back() ?
enum_exprs is uncessary, just use get_then_param_exprs() and get_default_param_expr() in the loop.

} else {
// rewrite case_when_expr with lnnvl(...)
if (true_null_uncalc_idx > case_expr->get_then_expr_size() ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OB_UNLIKELY

}
// push back condk, if k != n
if (OB_SUCC(ret) && true_null_uncalc_idx < case_expr->get_then_expr_size()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use if else for all the operators below

LOG_WARN("failed to add false constraint", K(ret));
} else if (OB_FAIL(add_true_and_null_constraint(
is_uncalculable,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge add_false_or_false_constraint and add_true_and_null_constraint as one function


for (int64_t i = 0; OB_SUCC(ret) && -1 == first_non_false_idx && i < when_cnt; ++i) {
ObRawExpr *when_expr = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put this for loop into check_when_exprs_validity?

} else if (0 == first_non_false_idx && is_uncalc) {
} else if (OB_FAIL(add_false_or_null_constraint(false_null_exprs,
has_null))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adjust function name for assign_case_when_expr.


int ObTransformSimplifyExpr::add_true_and_true_constraint(
const ObIArray<ObRawExpr *> &true_exprs) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this unused function: ObTransformSimplifyExpr::add_true_and_true_constraint

}

int ObTransformSimplifyExpr::add_true_constraint(ObRawExpr *true_expr) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ObTransformSimplifyExpr::add_true_constraint,need create this function?

if (OB_FAIL(ret)) {
} else if (OB_UNLIKELY(idx < when_cnt && !is_true && !is_uncalc)) {
} else if (OB_FAIL(add_false_or_false_constraint(false_exprs))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add and use add_constraint_for_convert_case_when to add constraint for convert_case_when_predicate_by_when_exprs and convert_case_when_predicate_by_then_exprs ?

ObRawExpr *child_0 = NULL;
ObRawExpr *child_1 = NULL;
if (OB_FALSE_IT(op_expr = static_cast<ObOpRawExpr *>(expr))) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里套 if OB_FAISE_IT 做什么?直接做个变量赋值不就好了吗

}
} else if (T_OP_CASE == expr->get_expr_type()) {
if (is_scala_group_by && expr->has_flag(CNT_AGG)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个判断在反复出现?为什么不干脆放到最前面?或者直接放到 2595 行,这样 is_scalar_group_by 也不用传参了

int64_t uncalc_idx = -1;
// do pre check
for (int64_t i = 0; OB_SUCC(ret) && i < enum_value_cnt; ++i) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum_value_cnt 这种变量也没必要定义。

直接诶用 enum_exprs.count() 就好了

* @param[in][out] trans_happened: whether rewrite is happened
*/
int ObTransformSimplifyExpr::convert_case_when_predicate(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个函数里的递归顺序有点奇怪。
逻辑上这里有两个变化。
一个是,直接简化 case when 表达式。
另外一个是简化 case_expr = const 。

逻辑上,递归方式不应该是先递归每个 param expr。

然后检查当前 expr 是否为 case 表达式;

如果是 case 的话,那么尝试用策略1 简化
如果是 case = const 这种形态的,就尝试用策略 2 简化。

ObSEArray<ObRawExpr*, 2> false_exprs; // used to add constraints. nvl(expr, false)

for (idx = 0; OB_SUCC(ret) && idx < when_cnt; ++idx) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的逻辑是不是搞得太复杂了。

直接 for loop case when 的每一个分支。

如果 when 是 false,就构造约束并继续 loop;
如果 when 是 true,那么保留 then expr;并构造约束;跳出循环;
如果 when 是 unkown,那么保留后续的 when/then 分支;跳出循环。

循环外,构造最终的改写表达式

@@ -5296,6 +5296,37 @@ int ObRawExprUtils::build_nvl_expr(ObRawExprFactory &expr_factory, const ColumnI
return ret;
}

int ObRawExprUtils::build_nvl_expr(ObRawExprFactory &expr_factory,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为啥 transform_simpilfy_expr 里加了一个 build_nvl

这里又加了一个

const bool case_at_left,
bool &trans_happened)
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

假如,最终 case_expr op const 这个谓词的结果是 true。我理解现在的变化是没问题的。

但假如,判定结果为 false。有没有可能 transform 之后得到的结果为 NULL ?目前从逻辑上,看是没有去区分 NULL 和 false 的。

同理,如果判定结果为 NULL,会不会混淆成 false

@CLAassistant
Copy link

CLAassistant commented Aug 15, 2023

CLA assistant check
All committers have signed the CLA.

@fhkong fhkong force-pushed the feat branch 3 times, most recently from 9f01e13 to 612316d Compare August 15, 2023 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants