diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/MatchUtils.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/MatchUtils.java index 254ffc289aa..d7096f0fb1c 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/MatchUtils.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/MatchUtils.java @@ -27,7 +27,7 @@ private MatchUtils() { } /** - * Returns the row with the highest index whose corresponding symbol matches, null otherwise. + * Returns the highest index whose corresponding symbol matches, -1 otherwise. * * @param symbol Target Symbol * @param rows List of passed rows @@ -44,6 +44,23 @@ public static int lastWithSymbol(String symbol, List rows, List s return -1; } + /** + * Returns the highest index whose corresponding symbol matches, startIndex otherwise. + * + * @param symbol Target Symbol + * @param symbols Corresponding symbols to rows + * @return index or startIndex + */ + public static int lastWithSymbolOrLast(String symbol, List symbols, + int startIndex) { + for (int i = startIndex; i >= 0; i--) { + if (symbol.equals(symbols.get(i))) { + return i; + } + } + return startIndex; + } + public static void print(int s) { System.out.println(s); } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java index 42afb720fce..549bddbf724 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java @@ -22,7 +22,6 @@ import org.apache.calcite.avatica.util.DateTimeUtils; import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.avatica.util.TimeUnitRange; -import org.apache.calcite.linq4j.tree.BinaryExpression; import org.apache.calcite.linq4j.tree.BlockBuilder; import org.apache.calcite.linq4j.tree.BlockStatement; import org.apache.calcite.linq4j.tree.ConstantExpression; @@ -4173,43 +4172,27 @@ private static class LastImplementor implements MatchImplementor { final String alpha = ((RexPatternFieldRef) call.getOperands().get(0)).getAlpha(); - // TODO: verify if the variable is needed - @SuppressWarnings("unused") - final BinaryExpression lastIndex = - Expressions.subtract( - Expressions.call(rows, BuiltInMethod.COLLECTION_SIZE.method), - Expressions.constant(1)); - // Just take the last one, if exists if ("*".equals(alpha)) { setInputGetterIndex(translator, i); - // Important, unbox the node / expression to avoid NullAs.NOT_POSSIBLE - final RexPatternFieldRef ref = (RexPatternFieldRef) node; - final RexPatternFieldRef newRef = - new RexPatternFieldRef(ref.getAlpha(), - ref.getIndex(), - translator.typeFactory.createTypeWithNullability(ref.getType(), - true)); - final Expression expression = translator.translate(newRef, NullAs.NULL); - setInputGetterIndex(translator, null); - return expression; } else { // Alpha != "*" so we have to search for a specific one to find and use that, if found + // otherwise pick the last one setInputGetterIndex(translator, - Expressions.call(BuiltInMethod.MATCH_UTILS_LAST_WITH_SYMBOL.method, - Expressions.constant(alpha), rows, symbols, i)); - - // Important, unbox the node / expression to avoid NullAs.NOT_POSSIBLE - final RexPatternFieldRef ref = (RexPatternFieldRef) node; - final RexPatternFieldRef newRef = - new RexPatternFieldRef(ref.getAlpha(), - ref.getIndex(), - translator.typeFactory.createTypeWithNullability(ref.getType(), - true)); - final Expression expression = translator.translate(newRef, NullAs.NULL); - setInputGetterIndex(translator, null); - return expression; + Expressions.call(BuiltInMethod.MATCH_UTILS_LAST_WITH_SYMBOL_OR_LAST.method, + Expressions.constant(alpha), symbols, i)); } + + // Important, unbox the node / expression to avoid NullAs.NOT_POSSIBLE + final RexPatternFieldRef ref = (RexPatternFieldRef) node; + final RexPatternFieldRef newRef = + new RexPatternFieldRef(ref.getAlpha(), + ref.getIndex(), + translator.typeFactory.createTypeWithNullability(ref.getType(), + true)); + final Expression expression = translator.translate(newRef, NullAs.NULL); + setInputGetterIndex(translator, null); + return expression; } private static void setInputGetterIndex(RexToLixTranslator translator, @Nullable Expression o) { diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java index 61aebe15f51..98b67a02bbf 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -245,6 +245,8 @@ public enum BuiltInMethod { MATCHER_BUILDER_BUILD(Matcher.Builder.class, "build"), MATCH_UTILS_LAST_WITH_SYMBOL(MatchUtils.class, "lastWithSymbol", String.class, List.class, List.class, int.class), + MATCH_UTILS_LAST_WITH_SYMBOL_OR_LAST(MatchUtils.class, "lastWithSymbolOrLast", String.class, + List.class, int.class), EMITTER_EMIT(Enumerables.Emitter.class, "emit", List.class, List.class, List.class, int.class, Consumer.class), MERGE_JOIN(EnumerableDefaults.class, "mergeJoin", Enumerable.class, diff --git a/core/src/test/resources/sql/match.iq b/core/src/test/resources/sql/match.iq index 887c781c15e..8c37204d90b 100644 --- a/core/src/test/resources/sql/match.iq +++ b/core/src/test/resources/sql/match.iq @@ -140,6 +140,22 @@ C EMPID !ok +# Test Simple LAST with expanded column name +# Test case for CALCITE-7474 +select * +from "hr"."emps" match_recognize ( + order by "empid" desc + measures "commission" as c, + LAST("hr"."emps"."empid") as empid + pattern (s up) + define up as up."commission" < prev(up."commission")); +C EMPID +---- ----- +1000 100 + 500 200 + +!ok + # Test LAST with Classifier select * from "hr"."emps" match_recognize (