Hi Ian, FilterJoinRule[1] is designed to not push filters to null-generating side of Join, it has nothing to do with your custom UDF.
[1] https://github.com/apache/calcite/blob/2bf1e7b2e8596a61b5ac19aa7bebb9910c4eea94/core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java#L111-L125 Alessandro Solimando <[email protected]> 于2023年8月30日周三 14:13写道: > > Hi Ian, > I did not have time to dig more into your question (not very familiar with > the way you register functions), but for RuleEventLogger you might want to > check these slides (9 to 12 for how to activate it, the rest 15 to 23 on > how to read the output): > https://www.slideshare.net/StamatisZampetakis/debugging-planning-issues-using-calcites-builtin-loggers > . > > Hth, > Alessandro > > > > On Wed, 30 Aug 2023 at 04:14, Ian Bertolacci > <[email protected]> wrote: > > > Hello, > > We have defined some extra comparison functions for our users. > > However, we’ve noticed that filter push-down (using ` > > CoreRules.FILTER_INTO_JOIN`) does not occur when the operands to the > > functions come from the opposite side of an outer join (i.e. from the right > > side of a left outer join, or from the left side of a right outer join). > > > > Here is a demonstration of this with SQL, the logical plan, and the > > physical plan post optimization: https://pastebin.com/raw/KjE40z5X > > In this example, I’ve defined a simple function called “test” which takes > > a BigInt and returns Boolean. > > In the first query, the operand to `test` comes from the left side of the > > left join, and the tree before planning has the filter node above the join, > > and after planning the filter is below the join. > > In the second query, the operand to `test` comes from the left side of the > > right join, and the tree before planning has the filter node above the > > join, but after planning the filter is still above the join. > > (I was hoping to get the output from the RuleEventLogger, but I haven’t > > been able to get it working.) > > > > I figure the push down is not happening because we are not properly > > communicating how these functions handles null (which is to return null, > > which would be false-y, and therefore allow the join to be converted to an > > inner join and the filter pushed down below the join). > > We are not providing these functions as SqlOperator instances in a > > SqlOperatorTable; instead we use `ScalarFunctionImpl.create` on native > > methods, and provide those Function instances through the Schema, via > > `AbstractSchema.getFunctionMultimap` (which eventually make it to the > > CatalogReader) > > I’m guessing that if we were providing these via a SqlOperatorTable (which > > we’re hoping to do sometime soon) that having the return type of the > > operator be nullable, or have the nullability be inferred from the > > operands, would allow the planner to push the filter below the join. > > > > Am I right? > > In the meantime, is there a way to provide this nullability information > > through `ScalarFunctionImpl.create` pathway we’re currently using? > > > > Thanks! > > -Ian J. Bertolacci > > -- Best, Benchao Li
