asolimando commented on code in PR #2876: URL: https://github.com/apache/calcite/pull/2876#discussion_r1020901359
########## core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewRule.java: ########## @@ -1252,6 +1285,193 @@ protected RexNode replaceWithOriginalReferences(final RexBuilder rexBuilder, } } + /** + * Used to generate a view predicate that is added to a materialized view that aggregates + * over a FLOOR(datetime) when the query has a range predicate on the same column. + */ + static class ImplicitViewPredicateShuttle extends RexShuttle { + + private final RexBuilder rexBuilder; + private final RexCall floorCall; + private final RexInputRef rexInputRef; + private final boolean generateViewFilter; + private long lowerBound; + private long upperBound; + + ImplicitViewPredicateShuttle(RexBuilder rexBuilder, RexCall floorCall, + RexInputRef rexInputRef, + boolean generateViewFilter) { + this.floorCall = floorCall; + this.rexBuilder = rexBuilder; + this.rexInputRef = rexInputRef; + this.generateViewFilter = generateViewFilter; + } + + private RexNode transformCall(RexCall call, boolean isLowerBound) { + SqlOperator transformedCallOperator = isLowerBound + ? SqlStdOperatorTable.GREATER_THAN_OR_EQUAL : SqlStdOperatorTable.LESS_THAN; + // matches functions of the form x > 5 or 5 > x + RexNode literalOperand = call.operands.get(0); + RexNode tableInputRefOperand = call.operands.get(1); + final int floorIndex = ((RexInputRef) floorCall.getOperands().get(0)).getIndex(); + boolean reverseOperands = false; + if ((literalOperand.getKind() == SqlKind.LITERAL + && tableInputRefOperand.getKind() == SqlKind.TABLE_INPUT_REF) + || (literalOperand.getKind() == SqlKind.TABLE_INPUT_REF + && tableInputRefOperand.getKind() == SqlKind.LITERAL)) { + if (literalOperand.getKind() == SqlKind.TABLE_INPUT_REF + && tableInputRefOperand.getKind() == SqlKind.LITERAL) { + literalOperand = call.operands.get(1); + tableInputRefOperand = call.operands.get(0); + reverseOperands = true; + } + int predicateIndex = ((RexTableInputRef) tableInputRefOperand).getIndex(); + if (floorIndex == predicateIndex) { + // if the query predicate contains a range over the column that is floored in the + // materialized view we can generate a filter on the view + boolean shiftTruncatedVal = call.getOperator() != transformedCallOperator; + long truncatedVal = getModifiedVal(shiftTruncatedVal, isLowerBound, literalOperand); + RexNode truncatedLiteral = + rexBuilder.makeTimestampLiteral(TimestampString.fromMillisSinceEpoch(truncatedVal), + 0); + if (isLowerBound) { + lowerBound = truncatedVal; + } else { + upperBound = truncatedVal; + } + if (generateViewFilter) { + tableInputRefOperand = rexInputRef; + } + RexNode modifiedCall = rexBuilder.makeCall(call.getType(), transformedCallOperator, + reverseOperands ? ImmutableList.of(tableInputRefOperand, truncatedLiteral) + : ImmutableList.of(truncatedLiteral, tableInputRefOperand)); + return modifiedCall; + } else { + // ignore predicates on different columns + return rexBuilder.makeLiteral(true); + } + } + return super.visitCall(call); + } + + private long getModifiedVal(boolean shiftModifiedVal, boolean isLowerBound, + RexNode literalOperand) { + SqlOperator floorOperator = isLowerBound ? SqlStdOperatorTable.CEIL + : SqlStdOperatorTable.FLOOR; + RexNode truncatedLiteral = rexBuilder.makeCall(floorCall.getType(), + floorOperator, ImmutableList.of(literalOperand, floorCall.getOperands().get(1))); + Comparable v0 = RexInterpreter.evaluate(truncatedLiteral, Collections.emptyMap()); + if (v0 == null) { + throw new AssertionError("interpreter returned null for " + v0); + } + Comparable v1 = RexInterpreter.evaluate(literalOperand, Collections.emptyMap()); + if (v1 == null) { + throw new AssertionError("interpreter returned null for " + v1); + } + long modifiedVal = (long) v0; + final long originalVal = (long) v1; + // Since the view contains a FLOOR() if the query does a > or <= and the operand is already + // a FLOORED value we have to shift the value to the next higher or lower floored value + // If the query contains a >= or < we don't need to shift the modified value Review Comment: Can you add an example here? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@calcite.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org