Uh oh!
There was an error while loading. Please reload this page.
feat: support inlist in LiteralGurantee for pruning - #8654
Conversation
| binary_expr.right().as_any(), | ||
| ); | ||
| let guarantee = match op { |
There was a problem hiding this comment.
Since operator except = and != would be evaluate in predicate_expr, I put the conversion process here.
| if terms.iter().all(|term| { | ||
| term.col.name() == first_term.col.name() | ||
| && term.op == first_term.op | ||
| && term.guarantee == Guarantee::In |
There was a problem hiding this comment.
a != foo OR a != bar would be filtered out here, So we don't need to check new_values.len() == 1 in aggregate_multi_conjunct. If not, new_values.len() == 1 will confilct with the LiteralGuarantee result from InList who may have multi literals. And I think refactored code is easier to follow for me.
There was a problem hiding this comment.
I also double checked that this will verify that first_term.guarantee needs to be In as well ✅
Thank you @my-vegetable-has-exploded -- I plan to review this tomorrow. cc @waynexia and @haohuaijin |
haohuaijin
left a comment
There was a problem hiding this comment.
Thank you @my-vegetable-has-exploded , LGTM!
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Huaijin <haohuaijin@gmail.com>
9baab8d to
e18ebbaCompare| ]); | ||
| let sql = | ||
| "SELECT * FROM tbl WHERE \"String\" IN ('Hello_Not_Exists', 'Hello_Not_Exists2')"; | ||
| let expr = sql_to_physical_plan(sql).unwrap(); |
There was a problem hiding this comment.
This previous test relied on the optimizer who will convert in_list expr with no more 3 elements to and expr. Now we don't need this. logical2physical would convert logical_expr to physical_expr without optimizer.
There was a problem hiding this comment.
I agree -- this change makes sense
BTW I tried to improve these tests to avoid duplication in #8435 (not yet merged). I'll try and consolidate this test too when I get a chance
alamb
left a comment
There was a problem hiding this comment.
I reviewed this PR carefully -- thank you @my-vegetable-has-exploded (and @haohuaijin for the review). This PR looks very nice 👌
I left some small suggestions but nothing I think is required -- we could make them as a follow on PR as well
| ]); | ||
| let sql = | ||
| "SELECT * FROM tbl WHERE \"String\" IN ('Hello_Not_Exists', 'Hello_Not_Exists2')"; | ||
| let expr = sql_to_physical_plan(sql).unwrap(); |
There was a problem hiding this comment.
I agree -- this change makes sense
BTW I tried to improve these tests to avoid duplication in #8435 (not yet merged). I'll try and consolidate this test too when I get a chance
| .as_any() | ||
| .downcast_ref::<crate::expressions::InListExpr>() | ||
| { | ||
| //Only support single-column inlist currently, multi-column inlist is not supported |
Uh oh!
There was an error while loading. Please reload this page.
| if terms.iter().all(|term| { | ||
| term.col.name() == first_term.col.name() | ||
| && term.op == first_term.op | ||
| && term.guarantee == Guarantee::In |
There was a problem hiding this comment.
I also double checked that this will verify that first_term.guarantee needs to be In as well ✅
| binary_expr.right().as_any(), | ||
| ); | ||
| let guarantee = match op { |
Uh oh!
There was an error while loading. Please reload this page.
| col("b") | ||
| .in_list(vec![lit(1), lit(2), lit(3)], false) | ||
| .and(col("b").in_list(vec![lit(2), lit(3), lit(4)], false)), | ||
| vec![in_guarantee("b", [2, 3])], |
There was a problem hiding this comment.
This type of simplification might also be valuable to do in the Expr simplifier code too (so that other optimizer passes could see simpler predicates).
However, I am not sure how common such predicates are 🤔
| } | ||
| #[test] | ||
| fn test_inlist_with_disjunction() { |
There was a problem hiding this comment.
Could you also please add (negative) tests for the inlist directly used with OR too ?
For example:
b IN (1, 2, 3) OR b = 2
b IN (1, 2, 3) OR b != 3
I think the code above will work correctly, but it would be nice to verify
| col("b") | ||
| .in_list(vec![lit(1), lit(2), lit(3)], false) | ||
| .and(col("b").eq(lit(4)).or(col("b").eq(lit(5)))), | ||
| vec![], |
There was a problem hiding this comment.
I theory this could be in_guarantee(1,2,3) right? As it can only be true when b is 1,2,3.
However in this case this expression can never be true so maybe it doesn't really matter 🤔
There was a problem hiding this comment.
This would be invalid since intersection between [1,2,3] and [4,5] is empty.
let intersection = new_values
.into_iter().filter(|new_value| existing.literals.contains(*new_value)).collect::<Vec<_>>();// for an In guarantee, if the intersection is not empty, we can extend the guarantee// e.g. `a IN (1,2,3) AND a IN (2,3,4)` is `a IN (2,3)`// otherwise, we invalidate the guarantee// e.g. `a IN (1,2,3) AND a IN (4,5,6)` is `a IN ()`, which is invalidif !intersection.is_empty(){
existing.literals = intersection.into_iter().cloned().collect();}else{// at least one was not, so invalidate the guarantee*entry = None;}BTW, I left a comment in #8437. Please cc when you are free.
There was a problem hiding this comment.
This would be invalid since intersection between [1,2,3] and [4,5] is empty.
Right -- I wonder if in general for cases where the intersection is empty, can we infer that the expression can not be true ever 🤔 (there is no way to represent this today in the guarantee framework, maybe something like
enum Guarantee {
/// This predicate can not be true
CanNotBeTrue,
/// This predicate can only be true if all the `LiteralGurantee`s are valie
List(Vec<LiteralGurantee>)
}
And then return Gurantee::CanNotBeTrue if we discover that the expression can not be true 🤔
BTW, I left a comment in #8437. Please cc when you are free.
I am not quite sure what you are referring to. #8437 doesn't seem to have had any recent activity that I can see.
There was a problem hiding this comment.
Maybe we can enhance eliminate_filter optimizer rule to eliminate the filters(in where clause) that always not be true and replace by a EmptyRelation.
like we do for where false in
https://github.com/apache/arrow-datafusion/blob/8284371cb5dbeb5d0b1d50c420affb9be86b1599/datafusion/optimizer/src/eliminate_filter.rs#L55-L62
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| /// | ||
| /// # Notes: | ||
| /// 1. `expr` must be a boolean expression. | ||
| /// 1. `expr` must be a boolean expression or inlist expression. |
There was a problem hiding this comment.
More specifically, the boolean expr can only be in a form that can be transformed into a ColOpLit
Co-authored-by: Ruihang Xia <waynestxia@gmail.com> Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
my-vegetable-has-exploded
commented
Dec 28, 2023
Thanks all❤️. |
| vec![not_in_guarantee("b", [1, 2, 3]), in_guarantee("b", [3, 4])], | ||
| ); | ||
| // b IN (1, 2, 3) OR b = 2 | ||
| // TODO this should be in_guarantee("b", [1, 2, 3]) but currently we don't support to anylize this kind of disjunction. Only `ColOpLit OR ColOpLit` is supported. |
alamb
commented
Dec 28, 2023
Thanks again @my-vegetable-has-exploded |
domyway
commented
Dec 28, 2023
if items more than 10, it still cant use bloom filter |
alamb
commented
Dec 28, 2023
In case anyone is following along, the discussion about this is at #8436 (comment) |
Which issue does this PR close?
Closes#8436
Rationale for this change
What changes are included in this PR?
InListsupport in LiteralGurantee coderow_groups.rsAre these changes tested?
Are there any user-facing changes?