[ 
https://issues.apache.org/jira/browse/CALCITE-4414?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Ruben Q L updated CALCITE-4414:
-------------------------------
    Description: 
{{RelMdSelectivity#getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
predicate)}} method:
{code}
  public Double getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
predicate) {
    final RexProgram rexProgram = rel.getProgram();
    final RexLocalRef programCondition = rexProgram.getCondition();
    if (programCondition == null) {
      return getSelectivity(rel.getInput(), mq, predicate); // [2]
    } else {
      // [1]
      return mq.getSelectivity(rel.getInput(),
          RelMdUtil.minusPreds(
              rel.getCluster().getRexBuilder(),
              predicate,
              rexProgram.expandLocalRef(programCondition)));
    }
  }
{code}
currently passes down the predicate to its input [1] without considering any 
possible translation, since the predicate might include expressions generated 
by the Calc's projections; hence when the Calc's input analyzes the predicate, 
it can end up trying to access fields that do not exist on its rowType.

This can lead to unforeseeable consequences, like the test attached to the 
first comment, where after {{RelMdSelectivity#getSelectivity(Calc)}} we reach 
{{RelMdSelectivity#getSelectivity(Union)}} and this method ends up in an 
{{ArrayIndexOutOfBoundsException}} because it tries to access a field ($1) that 
does not exists on its rowType (which only has $0). This $1 is actually 
projected by the Calc which is on top of the Union.

Note I: in a similar situation, RelM uses{{ RelOptUtil.pushPastProject}} (which 
"Converts an expression that is based on the output fields of a Project to an 
equivalent expression on the Project's input fields.") to convert the predicate 
before passing it to the Project's input.

Note II: in the code snipped above that in our test example the issue only 
happens in line [1], and not in [2] because the "if" block calls 
{{getSelectivity}} instead of {{mq.getSelectivity}}, although I find this a bit 
questionable and maybe {{mq.getSelectivity}} should be called here as well.


  was:
{{RelMdSelectivity#getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
predicate)}} method:
{code}
  public Double getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
predicate) {
    final RexProgram rexProgram = rel.getProgram();
    final RexLocalRef programCondition = rexProgram.getCondition();
    if (programCondition == null) {
      return getSelectivity(rel.getInput(), mq, predicate); // [2]
    } else {
      // [1]
      return mq.getSelectivity(rel.getInput(),
          RelMdUtil.minusPreds(
              rel.getCluster().getRexBuilder(),
              predicate,
              rexProgram.expandLocalRef(programCondition)));
    }
  }
{code}
currently passes down the predicate to its input [1] without considering any 
possible translation, since the predicate might include expressions generated 
by the Calc's projections; hence when the Calc's input analyzes the predicate, 
it can end up trying to access fields that do not exist on its rowType.

This can lead to unforeseeable consequences, like the test attached to the 
first comment, where after {{RelMdSelectivity#getSelectivity(Calc)}} we reach 
{{RelMdSelectivity#getSelectivity(Union)}} and this method ends up in an 
{{ArrayIndexOutOfBoundsException}} because it tries to access a field ($1) that 
does not exists on its rowType (which only has $0). This $1 is actually 
projected by the Calc which is on top of the Union.

Notice in the code snipped above that in our test example the issue only 
happens in line [1], and not in [2] because the "if" block calls 
{{getSelectivity}} instead of {{mq.getSelectivity}}, although I find this a bit 
questionable and maybe {{mq.getSelectivity}} should be called here as well.



> RelMdSelectivity#getSelectivity for Calc can propagate a predicate with wrong 
> references
> ----------------------------------------------------------------------------------------
>
>                 Key: CALCITE-4414
>                 URL: https://issues.apache.org/jira/browse/CALCITE-4414
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>            Reporter: Ruben Q L
>            Assignee: Ruben Q L
>            Priority: Major
>             Fix For: 1.27.0
>
>
> {{RelMdSelectivity#getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
> predicate)}} method:
> {code}
>   public Double getSelectivity(Calc rel, RelMetadataQuery mq, RexNode 
> predicate) {
>     final RexProgram rexProgram = rel.getProgram();
>     final RexLocalRef programCondition = rexProgram.getCondition();
>     if (programCondition == null) {
>       return getSelectivity(rel.getInput(), mq, predicate); // [2]
>     } else {
>       // [1]
>       return mq.getSelectivity(rel.getInput(),
>           RelMdUtil.minusPreds(
>               rel.getCluster().getRexBuilder(),
>               predicate,
>               rexProgram.expandLocalRef(programCondition)));
>     }
>   }
> {code}
> currently passes down the predicate to its input [1] without considering any 
> possible translation, since the predicate might include expressions generated 
> by the Calc's projections; hence when the Calc's input analyzes the 
> predicate, it can end up trying to access fields that do not exist on its 
> rowType.
> This can lead to unforeseeable consequences, like the test attached to the 
> first comment, where after {{RelMdSelectivity#getSelectivity(Calc)}} we reach 
> {{RelMdSelectivity#getSelectivity(Union)}} and this method ends up in an 
> {{ArrayIndexOutOfBoundsException}} because it tries to access a field ($1) 
> that does not exists on its rowType (which only has $0). This $1 is actually 
> projected by the Calc which is on top of the Union.
> Note I: in a similar situation, RelM uses{{ RelOptUtil.pushPastProject}} 
> (which "Converts an expression that is based on the output fields of a 
> Project to an equivalent expression on the Project's input fields.") to 
> convert the predicate before passing it to the Project's input.
> Note II: in the code snipped above that in our test example the issue only 
> happens in line [1], and not in [2] because the "if" block calls 
> {{getSelectivity}} instead of {{mq.getSelectivity}}, although I find this a 
> bit questionable and maybe {{mq.getSelectivity}} should be called here as 
> well.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to