You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by zs...@apache.org on 2022/03/17 07:18:06 UTC

[ignite-3] branch main updated: IGNITE-16656 Adoption of a bunch of calcite related tickets from Ignite-2 - Fixes #707.

This is an automated email from the ASF dual-hosted git repository.

zstan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 79a4b40  IGNITE-16656 Adoption of a bunch of calcite related tickets from Ignite-2 - Fixes #707.
79a4b40 is described below

commit 79a4b403e32f47c8ccd2d6c32c3d995ffc80f88a
Author: zstan <st...@gmail.com>
AuthorDate: Thu Mar 17 10:10:46 2022 +0300

    IGNITE-16656 Adoption of a bunch of calcite related tickets from Ignite-2 - Fixes #707.
    
    IGNITE-14681 Extend return type of sum() aggregate function
    IGNITE-16414 Sorted index spool produces wrong collation
    IGNITE-14963 Date interval arythmetic returns invalid results
    
    Signed-off-by: zstan <st...@gmail.com>
---
 .../internal/sql/engine/ItAggregatesTest.java      |   4 +-
 .../internal/sql/engine/ItDataTypesTest.java       |  59 ++++++
 .../ignite/internal/sql/engine/ItIntervalTest.java |  50 ++---
 .../internal/sql/engine/ItSortAggregateTest.java   |   6 +-
 .../internal/sql/engine/exec/ExecutionContext.java |   3 -
 .../sql/engine/exec/exp/agg/Accumulators.java      | 215 +++------------------
 .../engine/rel/agg/IgniteSortAggregateBase.java    |  24 ++-
 .../FilterSpoolMergeToSortedIndexSpoolRule.java    |  61 +++++-
 .../engine/rule/SortAggregateConverterRule.java    |   7 +-
 .../internal/sql/engine/trait/TraitUtils.java      |  22 ++-
 .../sql/engine/type/IgniteTypeFactory.java         |  14 +-
 .../internal/sql/engine/type/IgniteTypeSystem.java |  64 ++++++
 .../ignite/internal/sql/engine/util/RexUtils.java  |  19 +-
 .../ignite/internal/sql/engine/util/TypeUtils.java |  31 +--
 .../sql/engine/exec/rel/BaseAggregateTest.java     |  96 +++++++++
 .../sql/engine/planner/AbstractPlannerTest.java    |  32 +++
 .../planner/AggregateDistinctPlannerTest.java      |   4 +-
 .../sql/engine/planner/AggregatePlannerTest.java   |  86 ++++++++-
 .../CorrelatedNestedLoopJoinPlannerTest.java       |   8 +-
 .../engine/planner/HashAggregatePlannerTest.java   |   4 +-
 .../engine/planner/HashIndexSpoolPlannerTest.java  |   8 +-
 .../sql/engine/planner/LimitOffsetPlannerTest.java |  20 +-
 .../engine/planner/SortAggregatePlannerTest.java   |   7 +-
 .../planner/SortedIndexSpoolPlannerTest.java       |   8 +-
 24 files changed, 538 insertions(+), 314 deletions(-)

diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java
index ea370e2..e383d01 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItAggregatesTest.java
@@ -78,8 +78,8 @@ public class ItAggregatesTest extends AbstractBasicIntegrationTest {
                 .check();
 
         assertQuery("select salary, count(1), sum(1) from person group by salary order by salary")
-                .returns(10d, 3L, 3)
-                .returns(15d, 2L, 2)
+                .returns(10d, 3L, 3L)
+                .returns(15d, 2L, 2L)
                 .check();
 
         assertQuery("select salary, name, count(1), sum(salary) from person group by salary, name order by salary")
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
index d9f8bfe..d79ea91 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItDataTypesTest.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.sql.engine;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -156,4 +158,61 @@ public class ItDataTypesTest extends AbstractBasicIntegrationTest {
             sql("DROP TABLE if exists tbl");
         }
     }
+
+    /**
+     * Test right date/time interpretation.
+     */
+    @Test
+    public void testDateTime() {
+        assertQuery("select date '1992-01-19'").returns(sqlDate("1992-01-19")).check();
+        assertQuery("select date '1992-01-18' + interval (1) days").returns(sqlDate("1992-01-19")).check();
+        assertQuery("select date '1992-01-18' + interval (24) hours").returns(sqlDate("1992-01-19")).check();
+        assertQuery("SELECT timestamp '1992-01-18 02:30:00' + interval (25) hours")
+                .returns(sqlDateTime("1992-01-19T03:30:00")).check();
+        assertQuery("SELECT timestamp '1992-01-18 02:30:00' + interval (23) hours")
+                .returns(sqlDateTime("1992-01-19T01:30:00.000")).check();
+        assertQuery("SELECT timestamp '1992-01-18 02:30:00' + interval (24) hours")
+                .returns(sqlDateTime("1992-01-19T02:30:00.000")).check();
+
+        assertQuery("select date '1992-03-29'").returns(sqlDate("1992-03-29")).check();
+        assertQuery("select date '1992-03-28' + interval (1) days").returns(sqlDate("1992-03-29")).check();
+        assertQuery("select date '1992-03-28' + interval (24) hours").returns(sqlDate("1992-03-29")).check();
+        assertQuery("SELECT timestamp '1992-03-28 02:30:00' + interval (25) hours")
+                .returns(sqlDateTime("1992-03-29T03:30:00.000")).check();
+        assertQuery("SELECT timestamp '1992-03-28 02:30:00' + interval (23) hours")
+                .returns(sqlDateTime("1992-03-29T01:30:00.000")).check();
+        assertQuery("SELECT timestamp '1992-03-28 02:30:00' + interval (24) hours")
+                .returns(sqlDateTime("1992-03-29T02:30:00.000")).check();
+
+        assertQuery("select date '1992-09-27'").returns(sqlDate("1992-09-27")).check();
+        assertQuery("select date '1992-09-26' + interval (1) days").returns(sqlDate("1992-09-27")).check();
+        assertQuery("select date '1992-09-26' + interval (24) hours").returns(sqlDate("1992-09-27")).check();
+        assertQuery("SELECT timestamp '1992-09-26 02:30:00' + interval (25) hours")
+                .returns(sqlDateTime("1992-09-27T03:30:00.000")).check();
+        assertQuery("SELECT timestamp '1992-09-26 02:30:00' + interval (23) hours")
+                .returns(sqlDateTime("1992-09-27T01:30:00.000")).check();
+        assertQuery("SELECT timestamp '1992-09-26 02:30:00' + interval (24) hours")
+                .returns(sqlDateTime("1992-09-27T02:30:00.000")).check();
+
+        assertQuery("select date '2021-11-07'").returns(sqlDate("2021-11-07")).check();
+        assertQuery("select date '2021-11-06' + interval (1) days").returns(sqlDate("2021-11-07")).check();
+        assertQuery("select date '2021-11-06' + interval (24) hours").returns(sqlDate("2021-11-07")).check();
+        assertQuery("SELECT timestamp '2021-11-06 01:30:00' + interval (25) hours")
+                .returns(sqlDateTime("2021-11-07T02:30:00.000")).check();
+        // Check string representation here, since after timestamp calculation we have '2021-11-07T01:30:00.000-0800'
+        // but Timestamp.valueOf method converts '2021-11-07 01:30:00' in 'America/Los_Angeles' time zone to
+        // '2021-11-07T01:30:00.000-0700' (we pass through '2021-11-07 01:30:00' twice after DST ended).
+        assertQuery("SELECT (timestamp '2021-11-06 02:30:00' + interval (23) hours)::varchar")
+                .returns("2021-11-07 01:30:00").check();
+        assertQuery("SELECT (timestamp '2021-11-06 01:30:00' + interval (24) hours)::varchar")
+                .returns("2021-11-07 01:30:00").check();
+    }
+
+    private LocalDate sqlDate(String str) {
+        return LocalDate.parse(str);
+    }
+
+    private LocalDateTime sqlDateTime(String str) {
+        return LocalDateTime.parse(str);
+    }
 }
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java
index 47211c9..ea7bf3c 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java
@@ -22,10 +22,10 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import java.sql.Date;
-import java.sql.Time;
-import java.sql.Timestamp;
 import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.Period;
 import org.apache.ignite.lang.IgniteInternalException;
 import org.junit.jupiter.api.Disabled;
@@ -220,45 +220,45 @@ public class ItIntervalTest extends AbstractBasicIntegrationTest {
     @Test
     public void testIntervalArithmetic() {
         // Date +/- interval.
-        assertEquals(Date.valueOf("2021-01-02"), eval("DATE '2021-01-01' + INTERVAL 1 DAY"));
-        assertEquals(Date.valueOf("2020-12-31"), eval("DATE '2021-01-01' - INTERVAL 1 DAY"));
-        assertEquals(Date.valueOf("2020-12-31"), eval("DATE '2021-01-01' + INTERVAL -1 DAY"));
-        assertEquals(Date.valueOf("2021-02-01"), eval("DATE '2021-01-01' + INTERVAL 1 MONTH"));
-        assertEquals(Date.valueOf("2022-01-01"), eval("DATE '2021-01-01' + INTERVAL 1 YEAR"));
-        assertEquals(Date.valueOf("2022-02-01"), eval("DATE '2021-01-01' + INTERVAL '1-1' YEAR TO MONTH"));
+        assertEquals(LocalDate.parse("2021-01-02"), eval("DATE '2021-01-01' + INTERVAL 1 DAY"));
+        assertEquals(LocalDate.parse("2020-12-31"), eval("DATE '2021-01-01' - INTERVAL 1 DAY"));
+        assertEquals(LocalDate.parse("2020-12-31"), eval("DATE '2021-01-01' + INTERVAL -1 DAY"));
+        assertEquals(LocalDate.parse("2021-02-01"), eval("DATE '2021-01-01' + INTERVAL 1 MONTH"));
+        assertEquals(LocalDate.parse("2022-01-01"), eval("DATE '2021-01-01' + INTERVAL 1 YEAR"));
+        assertEquals(LocalDate.parse("2022-02-01"), eval("DATE '2021-01-01' + INTERVAL '1-1' YEAR TO MONTH"));
 
         // Timestamp +/- interval.
-        assertEquals(Timestamp.valueOf("2021-01-01 00:00:01"),
+        assertEquals(LocalDateTime.parse("2021-01-01T00:00:01"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL 1 SECOND"));
-        assertEquals(Timestamp.valueOf("2021-01-01 00:00:01.123"),
+        assertEquals(LocalDateTime.parse("2021-01-01T00:00:01.123"),
                 eval("TIMESTAMP '2021-01-01 00:00:00.123' + INTERVAL 1 SECOND"));
-        assertEquals(Timestamp.valueOf("2021-01-01 00:00:01.123"),
+        assertEquals(LocalDateTime.parse("2021-01-01T00:00:01.123"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL '1.123' SECOND"));
-        assertEquals(Timestamp.valueOf("2021-01-01 00:00:01.246"),
+        assertEquals(LocalDateTime.parse("2021-01-01T00:00:01.246"),
                 eval("TIMESTAMP '2021-01-01 00:00:00.123' + INTERVAL '1.123' SECOND"));
-        assertEquals(Timestamp.valueOf("2020-12-31 23:59:59"),
+        assertEquals(LocalDateTime.parse("2020-12-31T23:59:59"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' - INTERVAL 1 SECOND"));
-        assertEquals(Timestamp.valueOf("2020-12-31 23:59:59"),
+        assertEquals(LocalDateTime.parse("2020-12-31T23:59:59"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL -1 SECOND"));
-        assertEquals(Timestamp.valueOf("2021-01-01 00:01:00"),
+        assertEquals(LocalDateTime.parse("2021-01-01T00:01:00"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL 1 MINUTE"));
-        assertEquals(Timestamp.valueOf("2021-01-01 01:00:00"),
+        assertEquals(LocalDateTime.parse("2021-01-01T01:00:00"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL 1 HOUR"));
-        assertEquals(Timestamp.valueOf("2021-01-02 00:00:00"),
+        assertEquals(LocalDateTime.parse("2021-01-02T00:00:00"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL 1 DAY"));
-        assertEquals(Timestamp.valueOf("2021-02-01 00:00:00"),
+        assertEquals(LocalDateTime.parse("2021-02-01T00:00:00"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL 1 MONTH"));
-        assertEquals(Timestamp.valueOf("2022-01-01 00:00:00"),
+        assertEquals(LocalDateTime.parse("2022-01-01T00:00:00"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL 1 YEAR"));
-        assertEquals(Timestamp.valueOf("2021-01-02 01:01:01.123"),
+        assertEquals(LocalDateTime.parse("2021-01-02T01:01:01.123"),
                 eval("TIMESTAMP '2021-01-01 00:00:00' + INTERVAL '1 1:1:1.123' DAY TO SECOND"));
-        assertEquals(Timestamp.valueOf("2022-02-01 01:01:01.123"),
+        assertEquals(LocalDateTime.parse("2022-02-01T01:01:01.123"),
                 eval("TIMESTAMP '2021-01-01 01:01:01.123' + INTERVAL '1-1' YEAR TO MONTH"));
 
         // Time +/- interval.
-        assertEquals(Time.valueOf("00:00:01"), eval("TIME '00:00:00' + INTERVAL 1 SECOND"));
-        assertEquals(Time.valueOf("00:01:00"), eval("TIME '00:00:00' + INTERVAL 1 MINUTE"));
-        assertEquals(Time.valueOf("01:00:00"), eval("TIME '00:00:00' + INTERVAL 1 HOUR"));
+        assertEquals(LocalTime.parse("00:00:01"), eval("TIME '00:00:00' + INTERVAL 1 SECOND"));
+        assertEquals(LocalTime.parse("00:01:00"), eval("TIME '00:00:00' + INTERVAL 1 MINUTE"));
+        assertEquals(LocalTime.parse("01:00:00"), eval("TIME '00:00:00' + INTERVAL 1 HOUR"));
 
         // Date - date as interval.
         assertEquals(Duration.ofDays(1), eval("(DATE '2021-01-02' - DATE '2021-01-01') DAYS"));
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java
index 4ac7024..db305c5 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSortAggregateTest.java
@@ -119,10 +119,10 @@ public class ItSortAggregateTest extends AbstractBasicIntegrationTest {
         assertEquals(ROWS / 10, res.size());
 
         res.forEach(r -> {
-            Integer s0 = (Integer) r.get(0);
-            Integer s1 = (Integer) r.get(1);
+            long s0 = (Long) r.get(0);
+            long s1 = (Long) r.get(1);
 
-            assertEquals(s0 * 2, (int) s1);
+            assertEquals(s0 * 2, s1);
         });
     }
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
index 2acf658..0cb7b48 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/ExecutionContext.java
@@ -237,9 +237,6 @@ public class ExecutionContext<RowT> extends AbstractQueryContext implements Data
         if (Variable.CANCEL_FLAG.camelName.equals(name)) {
             return cancelFlag;
         }
-        if (Variable.TIME_ZONE.camelName.equals(name)) {
-            return TIME_ZONE; // TODO DistributedSqlConfiguration#timeZone
-        }
         if (Variable.CURRENT_TIMESTAMP.camelName.equals(name)) {
             return startTs;
         }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
index 1a925c3..ad9a159 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/agg/Accumulators.java
@@ -98,31 +98,37 @@ public class Accumulators {
 
     private static Supplier<Accumulator> sumFactory(AggregateCall call) {
         switch (call.type.getSqlTypeName()) {
+            case BIGINT:
+            case DECIMAL:
+                return () -> new Sum(new DecimalSumEmptyIsZero());
+
             case DOUBLE:
             case REAL:
             case FLOAT:
-                return DoubleSum.FACTORY;
-            case DECIMAL:
-                return DecimalSum.FACTORY;
+                return () -> new Sum(new DoubleSumEmptyIsZero());
+
+            case TINYINT:
+            case SMALLINT:
             case INTEGER:
-                return IntSum.FACTORY;
-            case BIGINT:
             default:
-                return LongSum.FACTORY;
+                return () -> new Sum(new LongSumEmptyIsZero());
         }
     }
 
     private static Supplier<Accumulator> sumEmptyIsZeroFactory(AggregateCall call) {
         switch (call.type.getSqlTypeName()) {
+            case BIGINT:
+            case DECIMAL:
+                return DecimalSumEmptyIsZero.FACTORY;
+
             case DOUBLE:
             case REAL:
             case FLOAT:
                 return DoubleSumEmptyIsZero.FACTORY;
-            case DECIMAL:
-                return DecimalSumEmptyIsZero.FACTORY;
+
+            case TINYINT:
+            case SMALLINT:
             case INTEGER:
-                return IntSumEmptyIsZero.FACTORY;
-            case BIGINT:
             default:
                 return LongSumEmptyIsZero.FACTORY;
         }
@@ -392,207 +398,50 @@ public class Accumulators {
         }
     }
 
-    private static class DoubleSum implements Accumulator {
-        public static final Supplier<Accumulator> FACTORY = DoubleSum::new;
-
-        private double sum;
+    private static class Sum implements Accumulator {
+        private Accumulator acc;
 
         private boolean empty = true;
 
-        /** {@inheritDoc} */
-        @Override
-        public void add(Object... args) {
-            Double in = (Double) args[0];
-
-            if (in == null) {
-                return;
-            }
-
-            empty = false;
-            sum += in;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public void apply(Accumulator other) {
-            DoubleSum other0 = (DoubleSum) other;
-
-            if (other0.empty) {
-                return;
-            }
-
-            empty = false;
-            sum += other0.sum;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public Object end() {
-            return empty ? null : sum;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
-            return List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(DOUBLE), true));
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public RelDataType returnType(IgniteTypeFactory typeFactory) {
-            return typeFactory.createTypeWithNullability(typeFactory.createSqlType(DOUBLE), true);
-        }
-    }
-
-    private static class IntSum implements Accumulator {
-        public static final Supplier<Accumulator> FACTORY = IntSum::new;
-
-        private int sum;
-
-        private boolean empty = true;
-
-        /** {@inheritDoc} */
-        @Override
-        public void add(Object... args) {
-            Integer in = (Integer) args[0];
-
-            if (in == null) {
-                return;
-            }
-
-            empty = false;
-            sum += in;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public void apply(Accumulator other) {
-            IntSum other0 = (IntSum) other;
-
-            if (other0.empty) {
-                return;
-            }
-
-            empty = false;
-            sum += other0.sum;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public Object end() {
-            return empty ? null : sum;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
-            return List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(INTEGER), true));
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public RelDataType returnType(IgniteTypeFactory typeFactory) {
-            return typeFactory.createTypeWithNullability(typeFactory.createSqlType(INTEGER), true);
+        public Sum(Accumulator acc) {
+            this.acc = acc;
         }
-    }
-
-    private static class LongSum implements Accumulator {
-        public static final Supplier<Accumulator> FACTORY = LongSum::new;
-
-        private long sum;
-
-        private boolean empty = true;
 
         /** {@inheritDoc} */
-        @Override
-        public void add(Object... args) {
-            Long in = (Long) args[0];
-
-            if (in == null) {
+        @Override public void add(Object... args) {
+            if (args[0] == null) {
                 return;
             }
 
             empty = false;
-            sum += in;
+            acc.add(args[0]);
         }
 
         /** {@inheritDoc} */
-        @Override
-        public void apply(Accumulator other) {
-            LongSum other0 = (LongSum) other;
+        @Override public void apply(Accumulator other) {
+            Sum other0 = (Sum) other;
 
             if (other0.empty) {
                 return;
             }
 
             empty = false;
-            sum += other0.sum;
+            acc.apply(other0.acc);
         }
 
         /** {@inheritDoc} */
-        @Override
-        public Object end() {
-            return empty ? null : sum;
+        @Override public Object end() {
+            return empty ? null : acc.end();
         }
 
         /** {@inheritDoc} */
-        @Override
-        public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
-            return List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(BIGINT), true));
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public RelDataType returnType(IgniteTypeFactory typeFactory) {
-            return typeFactory.createTypeWithNullability(typeFactory.createSqlType(BIGINT), true);
-        }
-    }
-
-    private static class DecimalSum implements Accumulator {
-        public static final Supplier<Accumulator> FACTORY = DecimalSum::new;
-
-        private BigDecimal sum;
-
-        /** {@inheritDoc} */
-        @Override
-        public void add(Object... args) {
-            BigDecimal in = (BigDecimal) args[0];
-
-            if (in == null) {
-                return;
-            }
-
-            sum = sum == null ? in : sum.add(in);
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public void apply(Accumulator other) {
-            DecimalSum other0 = (DecimalSum) other;
-
-            if (other0.sum == null) {
-                return;
-            }
-
-            sum = sum == null ? other0.sum : sum.add(other0.sum);
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public Object end() {
-            return sum;
-        }
-
-        /** {@inheritDoc} */
-        @Override
-        public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
-            return List.of(typeFactory.createTypeWithNullability(typeFactory.createSqlType(DECIMAL), true));
+        @Override public List<RelDataType> argumentTypes(IgniteTypeFactory typeFactory) {
+            return acc.argumentTypes(typeFactory);
         }
 
         /** {@inheritDoc} */
-        @Override
-        public RelDataType returnType(IgniteTypeFactory typeFactory) {
-            return typeFactory.createTypeWithNullability(typeFactory.createSqlType(DECIMAL), true);
+        @Override public RelDataType returnType(IgniteTypeFactory typeFactory) {
+            return acc.returnType(typeFactory);
         }
     }
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteSortAggregateBase.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteSortAggregateBase.java
index 772cf61..19b227d 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteSortAggregateBase.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rel/agg/IgniteSortAggregateBase.java
@@ -19,8 +19,8 @@ package org.apache.ignite.internal.sql.engine.rel.agg;
 
 import static org.apache.ignite.internal.sql.engine.util.Commons.maxPrefix;
 
-import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.calcite.plan.RelTraitSet;
@@ -28,7 +28,6 @@ import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.util.ImmutableBitSet;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Pair;
 import org.apache.ignite.internal.sql.engine.trait.TraitUtils;
 import org.apache.ignite.internal.sql.engine.trait.TraitsAwareIgniteRel;
@@ -50,7 +49,22 @@ interface IgniteSortAggregateBase extends TraitsAwareIgniteRel {
     default Pair<RelTraitSet, List<RelTraitSet>> passThroughCollation(
             RelTraitSet nodeTraits, List<RelTraitSet> inputTraits
     ) {
-        RelCollation collation = RelCollations.of(ImmutableIntList.copyOf(getGroupSet().asList()));
+        RelCollation required = TraitUtils.collation(nodeTraits);
+        ImmutableBitSet requiredKeys = ImmutableBitSet.of(required.getKeys());
+        RelCollation collation;
+
+        if (getGroupSet().contains(requiredKeys)) {
+            List<RelFieldCollation> newCollationFields = new ArrayList<>(getGroupSet().cardinality());
+            newCollationFields.addAll(required.getFieldCollations());
+
+            ImmutableBitSet keysLeft = getGroupSet().except(requiredKeys);
+
+            keysLeft.forEach(fieldIdx -> newCollationFields.add(TraitUtils.createFieldCollation(fieldIdx)));
+
+            collation = RelCollations.of(newCollationFields);
+        } else {
+            collation = TraitUtils.createCollation(getGroupSet().toList());
+        }
 
         return Pair.of(nodeTraits.replace(collation),
                 List.of(inputTraits.get(0).replace(collation)));
@@ -66,13 +80,13 @@ interface IgniteSortAggregateBase extends TraitsAwareIgniteRel {
         IntList newCollationColls = maxPrefix(inputCollation.getKeys(), getGroupSet().asSet());
 
         if (newCollationColls.size() < getGroupSet().cardinality()) {
-            return ImmutableList.of();
+            return List.of();
         }
 
         List<RelFieldCollation> suitableCollations = inputCollation.getFieldCollations()
                 .stream().filter(k -> newCollationColls.contains(k.getFieldIndex())).collect(Collectors.toList());
 
-        return ImmutableList.of(Pair.of(
+        return List.of(Pair.of(
                 nodeTraits.replace(RelCollations.of(suitableCollations)),
                 inputTraits
         ));
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
index 6339e23..668e984 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/FilterSpoolMergeToSortedIndexSpoolRule.java
@@ -19,15 +19,23 @@ package org.apache.ignite.internal.sql.engine.rule;
 
 import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelRule;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Spool;
+import org.apache.calcite.rex.RexNode;
 import org.apache.ignite.internal.sql.engine.rel.IgniteFilter;
 import org.apache.ignite.internal.sql.engine.rel.IgniteSortedIndexSpool;
 import org.apache.ignite.internal.sql.engine.rel.IgniteTableSpool;
@@ -66,9 +74,11 @@ public class FilterSpoolMergeToSortedIndexSpoolRule extends RelRule<FilterSpoolM
 
         RelNode input = spool.getInput();
 
+        RelCollation inCollation = TraitUtils.collation(input);
+
         IndexConditions idxCond = RexUtils.buildSortedIndexConditions(
                 cluster,
-                TraitUtils.collation(input),
+                inCollation,
                 filter.getCondition(),
                 spool.getRowType(),
                 null
@@ -78,13 +88,54 @@ public class FilterSpoolMergeToSortedIndexSpoolRule extends RelRule<FilterSpoolM
             return;
         }
 
-        RelCollation collation = TraitUtils.createCollation(idxCond.keys());
+        RelCollation traitCollation;
+        RelCollation searchCollation;
+
+        if (inCollation == null || inCollation.isDefault()) {
+            // Create collation by index condition.
+            List<RexNode> lowerBound = idxCond.lowerBound();
+            List<RexNode> upperBound = idxCond.upperBound();
+
+            assert lowerBound == null || upperBound == null || lowerBound.size() == upperBound.size();
+
+            int cardinality = lowerBound != null ? lowerBound.size() : upperBound.size();
+
+            List<Integer> equalsFields = new ArrayList<>(cardinality);
+            List<Integer> otherFields = new ArrayList<>(cardinality);
+
+            // First, add all equality filters to collation, then add other fields.
+            for (int i = 0; i < cardinality; i++) {
+                RexNode lowerNode = lowerBound != null ? lowerBound.get(i) : null;
+                RexNode upperNode = upperBound != null ? upperBound.get(i) : null;
+
+                if (RexUtils.isNotNull(lowerNode) || RexUtils.isNotNull(upperNode)) {
+                    (Objects.equals(lowerNode, upperNode) ? equalsFields : otherFields).add(i);
+                }
+            }
+
+            equalsFields.addAll(otherFields);
+
+            searchCollation = traitCollation = TraitUtils.createCollation(equalsFields);
+        } else {
+            // Create search collation as a prefix of input collation.
+            traitCollation = inCollation;
+
+            Set<Integer> searchKeys = idxCond.keys();
+
+            List<RelFieldCollation> collationFields = inCollation.getFieldCollations().subList(0, searchKeys.size());
+
+            assert searchKeys.containsAll(collationFields.stream().map(RelFieldCollation::getFieldIndex)
+                    .collect(Collectors.toSet())) : "Search condition should be a prefix of collation [searchKeys="
+                    + searchKeys + ", collation=" + inCollation + ']';
+
+            searchCollation = RelCollations.of(collationFields);
+        }
 
         RelNode res = new IgniteSortedIndexSpool(
                 cluster,
-                trait.replace(collation),
-                convert(input, input.getTraitSet().replace(collation)),
-                collation,
+                trait.replace(traitCollation),
+                convert(input, input.getTraitSet().replace(traitCollation)),
+                searchCollation,
                 filter.getCondition(),
                 idxCond
         );
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java
index 31dbe73..33e6bd8 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/rule/SortAggregateConverterRule.java
@@ -25,16 +25,15 @@ import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.PhysicalNode;
 import org.apache.calcite.rel.RelCollation;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.logical.LogicalAggregate;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteConvention;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteMapSortAggregate;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteReduceSortAggregate;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteSingleSortAggregate;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
+import org.apache.ignite.internal.sql.engine.trait.TraitUtils;
 import org.apache.ignite.internal.sql.engine.util.HintUtils;
 
 /**
@@ -71,7 +70,7 @@ public class SortAggregateConverterRule {
             RelOptCluster cluster = agg.getCluster();
             RelNode input = agg.getInput();
 
-            RelCollation collation = RelCollations.of(ImmutableIntList.copyOf(agg.getGroupSet().asList()));
+            RelCollation collation = TraitUtils.createCollation(agg.getGroupSet().asList());
 
             RelTraitSet inTrait = cluster.traitSetOf(IgniteConvention.INSTANCE)
                     .replace(collation)
@@ -113,7 +112,7 @@ public class SortAggregateConverterRule {
             RelOptCluster cluster = agg.getCluster();
             RelNode input = agg.getInput();
 
-            RelCollation collation = RelCollations.of(ImmutableIntList.copyOf(agg.getGroupSet().asList()));
+            RelCollation collation = TraitUtils.createCollation(agg.getGroupSet().asList());
 
             RelTraitSet inTrait = cluster.traitSetOf(IgniteConvention.INSTANCE).replace(collation);
             RelTraitSet outTrait = cluster.traitSetOf(IgniteConvention.INSTANCE).replace(collation);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
index a3d8026..a51fc24 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/trait/TraitUtils.java
@@ -28,9 +28,9 @@ import static org.apache.ignite.internal.util.CollectionUtils.first;
 import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
 
 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
-import it.unimi.dsi.fastutil.ints.IntSet;
 import java.lang.reflect.Proxy;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -471,13 +471,29 @@ public class TraitUtils {
      * @param keys The keys to create collation from.
      * @return New collation.
      */
-    public static RelCollation createCollation(IntSet keys) {
+    public static RelCollation createCollation(Collection<Integer> keys) {
         return RelCollations.of(
-                keys.intStream().mapToObj(RelFieldCollation::new).collect(Collectors.toList())
+                keys.stream().map(TraitUtils::createFieldCollation).collect(Collectors.toList())
         );
     }
 
     /**
+     * Creates field collation with default direction and nulls ordering.
+     */
+    public static RelFieldCollation createFieldCollation(int fieldIdx) {
+        return createFieldCollation(fieldIdx, true);
+    }
+
+    /**
+     * Creates field collation with default nulls ordering.
+     */
+    public static RelFieldCollation createFieldCollation(int fieldIdx, boolean asc) {
+        return asc
+                ? new RelFieldCollation(fieldIdx, RelFieldCollation.Direction.ASCENDING, RelFieldCollation.NullDirection.FIRST)
+                : new RelFieldCollation(fieldIdx, RelFieldCollation.Direction.DESCENDING, RelFieldCollation.NullDirection.LAST);
+    }
+
+    /**
      * Creates mapping from provided projects that maps a source column idx to idx in a row after applying projections.
      *
      * @param inputFieldCount Size of a source row.
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java
index a6df9bc..d9c0da3 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeFactory.java
@@ -25,8 +25,8 @@ import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
-import java.sql.Timestamp;
 import java.time.Duration;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.Period;
@@ -204,15 +204,14 @@ public class IgniteTypeFactory extends JavaTypeFactoryImpl {
                 case CHAR:
                     return String.class;
                 case DATE:
-                    return java.sql.Date.class;
+                    return LocalDate.class;
                 case TIME:
-                    return java.sql.Time.class;
-                case TIMESTAMP:
-                    return Timestamp.class;
-                case TIME_WITH_LOCAL_TIME_ZONE:
                     return LocalTime.class;
+                case TIMESTAMP:
                 case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
                     return LocalDateTime.class;
+                case TIME_WITH_LOCAL_TIME_ZONE:
+                    return LocalTime.class;
                 case INTEGER:
                     return type.isNullable() ? Integer.class : int.class;
                 case INTERVAL_YEAR:
@@ -310,7 +309,8 @@ public class IgniteTypeFactory extends JavaTypeFactoryImpl {
 
     /** {@inheritDoc} */
     @Override public RelDataType createType(Type type) {
-        if (type == Duration.class || type == Period.class) {
+        if (type == Duration.class || type == Period.class || type == LocalDate.class || type == LocalDateTime.class
+                || type == LocalTime.class) {
             return createJavaType((Class<?>) type);
         }
 
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java
index 9a7d619..f65f74a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/type/IgniteTypeSystem.java
@@ -18,8 +18,13 @@
 package org.apache.ignite.internal.sql.engine.type;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
 import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
+import org.apache.calcite.sql.type.BasicSqlType;
+import org.apache.calcite.sql.type.SqlTypeName;
 
 /**
  * Ignite type system.
@@ -38,4 +43,63 @@ public class IgniteTypeSystem extends RelDataTypeSystemImpl implements Serializa
     public int getMaxNumericPrecision() {
         return Short.MAX_VALUE;
     }
+
+    /** {@inheritDoc} */
+    @Override
+    public RelDataType deriveSumType(RelDataTypeFactory typeFactory, RelDataType argumentType) {
+        RelDataType sumType;
+        if (argumentType instanceof BasicSqlType) {
+            switch (argumentType.getSqlTypeName()) {
+                case INTEGER:
+                case TINYINT:
+                case SMALLINT:
+                    sumType = typeFactory.createSqlType(SqlTypeName.BIGINT);
+
+                    break;
+
+                case BIGINT:
+                case DECIMAL:
+                    sumType = typeFactory.createSqlType(SqlTypeName.DECIMAL);
+
+                    break;
+
+                case REAL:
+                case FLOAT:
+                case DOUBLE:
+                    sumType = typeFactory.createSqlType(SqlTypeName.DOUBLE);
+
+                    break;
+
+                default:
+                    return super.deriveSumType(typeFactory, argumentType);
+            }
+        } else {
+            switch (argumentType.getSqlTypeName()) {
+                case INTEGER:
+                case TINYINT:
+                case SMALLINT:
+                    sumType = typeFactory.createJavaType(Long.class);
+
+                    break;
+
+                case BIGINT:
+                case DECIMAL:
+                    sumType = typeFactory.createJavaType(BigDecimal.class);
+
+                    break;
+
+                case REAL:
+                case FLOAT:
+                case DOUBLE:
+                    sumType = typeFactory.createJavaType(Double.class);
+
+                    break;
+
+                default:
+                    return super.deriveSumType(typeFactory, argumentType);
+            }
+        }
+
+        return typeFactory.createTypeWithNullability(sumType, argumentType.isNullable());
+    }
 }
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
index 50e5a54..8fb3d3d 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/RexUtils.java
@@ -41,7 +41,6 @@ import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptPredicateList;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelCollation;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.CorrelationId;
@@ -69,11 +68,11 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.ControlFlowException;
 import org.apache.calcite.util.ImmutableBitSet;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Util;
 import org.apache.calcite.util.mapping.MappingType;
 import org.apache.calcite.util.mapping.Mappings;
+import org.apache.ignite.internal.sql.engine.trait.TraitUtils;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.jetbrains.annotations.Nullable;
 
@@ -214,7 +213,16 @@ public class RexUtils {
 
         // Force collation for all fields of the condition.
         if (collation == null || collation.isDefault()) {
-            collation = RelCollations.of(ImmutableIntList.of(fieldsToPredicates.keySet().toIntArray()));
+            List<Integer> equalsFields = new ArrayList<>(fieldsToPredicates.size());
+            List<Integer> otherFields = new ArrayList<>(fieldsToPredicates.size());
+
+            // It's more effective to put equality conditions in the collation first.
+            fieldsToPredicates.forEach((idx, conds) ->
+                    (conds.stream().allMatch(call -> call.getOperator().getKind() == EQUALS) ? equalsFields : otherFields).add(idx));
+
+            equalsFields.addAll(otherFields);
+
+            collation = TraitUtils.createCollation(equalsFields);
         }
 
         for (int i = 0; i < collation.getFieldCollations().size(); i++) {
@@ -278,11 +286,6 @@ public class RexUtils {
                 break; // No bounds, so break the loop.
             }
 
-            if (i > 0 && bestLower != bestUpper) {
-                // Go behind the first index field only in the case of multiple "=" conditions on index fields.
-                break; // TODO https://issues.apache.org/jira/browse/IGNITE-13568
-            }
-
             if (bestLower != null && bestUpper != null) { // "x>5 AND x<10"
                 upper.add(bestUpper);
                 lower.add(bestLower);
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
index 10b2217..8062e76 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
@@ -25,17 +25,21 @@ import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
 import java.lang.reflect.Type;
 import java.sql.Timestamp;
 import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.Period;
+import java.time.ZoneOffset;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import org.apache.calcite.DataContext;
+import org.apache.calcite.avatica.util.ByteString;
 import org.apache.calcite.avatica.util.DateTimeUtils;
 import org.apache.calcite.plan.RelOptSchema;
 import org.apache.calcite.plan.RelOptTable;
@@ -64,10 +68,10 @@ import org.jetbrains.annotations.Nullable;
  */
 public class TypeUtils {
     private static final Set<Type> CONVERTABLE_TYPES = Set.of(
-            java.util.Date.class,
-            java.sql.Date.class,
-            java.sql.Time.class,
-            java.sql.Timestamp.class,
+            LocalDate.class,
+            LocalDateTime.class,
+            LocalTime.class,
+            Timestamp.class,
             Duration.class,
             Period.class
     );
@@ -337,19 +341,18 @@ public class TypeUtils {
     public static Object fromInternal(ExecutionContext<?> ectx, Object val, Type storageType) {
         if (val == null) {
             return null;
-        } else if (storageType == java.sql.Date.class && val instanceof Integer) {
-            final long t = (Integer) val * DateTimeUtils.MILLIS_PER_DAY;
-            return new java.sql.Date(t - DataContext.Variable.TIME_ZONE.<TimeZone>get(ectx).getOffset(t));
-        } else if (storageType == java.sql.Time.class && val instanceof Integer) {
-            return new java.sql.Time((Integer) val - DataContext.Variable.TIME_ZONE.<TimeZone>get(ectx).getOffset((Integer) val));
-        } else if (storageType == Timestamp.class && val instanceof Long) {
-            return new Timestamp((Long) val - DataContext.Variable.TIME_ZONE.<TimeZone>get(ectx).getOffset((Long) val));
-        } else if (storageType == java.util.Date.class && val instanceof Long) {
-            return new java.util.Date((Long) val - DataContext.Variable.TIME_ZONE.<TimeZone>get(ectx).getOffset((Long) val));
+        } else if (storageType == LocalDate.class && val instanceof Integer) {
+            return LocalDate.ofEpochDay((Integer) val);
+        } else if (storageType == LocalTime.class && val instanceof Integer) {
+            return LocalTime.ofSecondOfDay((Integer) val / 1000);
+        } else if (storageType == LocalDateTime.class && (val instanceof Long)) {
+            return LocalDateTime.ofEpochSecond((Long) val / 1000, (int) ((Long) val % 1000) * 1000 * 1000, ZoneOffset.UTC);
         } else if (storageType == Duration.class && val instanceof Long) {
             return Duration.ofMillis((Long) val);
         } else if (storageType == Period.class && val instanceof Integer) {
             return Period.of((Integer) val / 12, (Integer) val % 12, 0);
+        } else if (storageType == byte[].class && val instanceof ByteString) {
+            return ((ByteString) val).getBytes();
         } else {
             return val;
         }
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java
index 91f339d..b771437 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/rel/BaseAggregateTest.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
 import it.unimi.dsi.fastutil.ints.IntSet;
+import java.math.BigDecimal;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Supplier;
@@ -511,6 +512,101 @@ public abstract class BaseAggregateTest extends AbstractExecutionTest {
         }
     }
 
+    @ParameterizedTest
+    @EnumSource
+    public void sumIntegerOverflow(TestAggregateType testAgg) {
+        ExecutionContext<Object[]> ctx = executionContext();
+        IgniteTypeFactory tf = ctx.getTypeFactory();
+        RelDataType rowType = TypeUtils.createRowType(tf, int.class, int.class);
+        ScanNode<Object[]> scan = new ScanNode<>(ctx, rowType, Arrays.asList(
+                row(0, Integer.MAX_VALUE / 2),
+                row(0, Integer.MAX_VALUE / 2 + 11)
+        ));
+
+        AggregateCall call = AggregateCall.create(
+                SqlStdOperatorTable.SUM,
+                false,
+                false,
+                false,
+                ImmutableIntList.of(1),
+                -1,
+                null,
+                RelCollations.EMPTY,
+                tf.createJavaType(Long.class),
+                null);
+
+        List<ImmutableBitSet> grpSets = List.of(ImmutableBitSet.of(0));
+
+        RelDataType aggRowType = TypeUtils.createRowType(tf, int.class);
+
+        SingleNode<Object[]> aggChain = createAggregateNodesChain(
+                testAgg,
+                ctx,
+                grpSets,
+                call,
+                rowType,
+                aggRowType,
+                rowFactory(),
+                scan
+        );
+
+        RootNode<Object[]> root = new RootNode<>(ctx, aggRowType);
+        root.register(aggChain);
+
+        assertTrue(root.hasNext());
+
+        assertArrayEquals(row(0, (long) Integer.MAX_VALUE / 2 + (long) Integer.MAX_VALUE / 2 + 11L), root.next());
+
+        assertFalse(root.hasNext());
+    }
+
+    @ParameterizedTest
+    @EnumSource
+    public void sumLongOverflow(TestAggregateType testAgg) {
+        ExecutionContext<Object[]> ctx = executionContext();
+        IgniteTypeFactory tf = ctx.getTypeFactory();
+        RelDataType rowType = TypeUtils.createRowType(tf, int.class, long.class);
+        ScanNode<Object[]> scan = new ScanNode<>(ctx, rowType, Arrays.asList(
+                row(0, Long.MAX_VALUE / 2),
+                row(0, Long.MAX_VALUE / 2 + 11)
+        ));
+
+        AggregateCall call = AggregateCall.create(
+                SqlStdOperatorTable.SUM,
+                false,
+                false,
+                false,
+                ImmutableIntList.of(1),
+                -1,
+                RelCollations.EMPTY,
+                tf.createJavaType(BigDecimal.class),
+                null);
+
+        List<ImmutableBitSet> grpSets = List.of(ImmutableBitSet.of(0));
+
+        RelDataType aggRowType = TypeUtils.createRowType(tf, int.class);
+
+        SingleNode<Object[]> aggChain = createAggregateNodesChain(
+                testAgg,
+                ctx,
+                grpSets,
+                call,
+                rowType,
+                aggRowType,
+                rowFactory(),
+                scan
+        );
+
+        RootNode<Object[]> root = new RootNode<>(ctx, aggRowType);
+        root.register(aggChain);
+
+        assertTrue(root.hasNext());
+
+        assertArrayEquals(row(0, new BigDecimal(Long.MAX_VALUE).add(new BigDecimal(10))), root.next());
+
+        assertFalse(root.hasNext());
+    }
+
     protected SingleNode<Object[]> createAggregateNodesChain(
             TestAggregateType testAgg,
             ExecutionContext<Object[]> ctx,
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
index 08da3d0..72228a5 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AbstractPlannerTest.java
@@ -84,6 +84,7 @@ import org.apache.ignite.internal.sql.engine.prepare.MappingQueryContext;
 import org.apache.ignite.internal.sql.engine.prepare.PlannerHelper;
 import org.apache.ignite.internal.sql.engine.prepare.PlanningContext;
 import org.apache.ignite.internal.sql.engine.prepare.Splitter;
+import org.apache.ignite.internal.sql.engine.rel.IgniteIndexScan;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
 import org.apache.ignite.internal.sql.engine.rel.IgniteTableScan;
 import org.apache.ignite.internal.sql.engine.rel.logical.IgniteLogicalIndexScan;
@@ -97,6 +98,7 @@ import org.apache.ignite.internal.sql.engine.schema.ModifyRow;
 import org.apache.ignite.internal.sql.engine.schema.SqlSchemaManager;
 import org.apache.ignite.internal.sql.engine.schema.TableDescriptor;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
+import org.apache.ignite.internal.sql.engine.trait.TraitUtils;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeFactory;
 import org.apache.ignite.internal.sql.engine.type.IgniteTypeSystem;
 import org.apache.ignite.internal.sql.engine.util.BaseQueryContext;
@@ -549,6 +551,30 @@ public abstract class AbstractPlannerTest extends IgniteAbstractTest {
         }
     }
 
+    /**
+     * Predicate builder for "Index scan with given name" condition.
+     */
+    protected <T extends RelNode> Predicate<IgniteIndexScan> isIndexScan(String tableName, String idxName) {
+        return isInstanceOf(IgniteIndexScan.class).and(
+                n -> {
+                    String scanTableName = Util.last(n.getTable().getQualifiedName());
+
+                    if (!tableName.equalsIgnoreCase(scanTableName)) {
+                        lastErrorMsg = "Unexpected table name [exp=" + tableName + ", act=" + scanTableName + ']';
+
+                        return false;
+                    }
+
+                    if (!idxName.equals(n.indexName())) {
+                        lastErrorMsg = "Unexpected index name [exp=" + idxName + ", act=" + n.indexName() + ']';
+
+                        return false;
+                    }
+
+                    return true;
+                });
+    }
+
     protected void clearTraits(RelNode rel) {
         IgniteTestUtils.setFieldValue(rel, AbstractRelNode.class, "traitSet", RelTraitSet.createEmpty());
         rel.getInputs().forEach(this::clearTraits);
@@ -745,6 +771,12 @@ public abstract class AbstractPlannerTest extends IgniteAbstractTest {
             return this;
         }
 
+        public TestTable addIndex(String name, int... keys) {
+            addIndex(TraitUtils.createCollation(Arrays.stream(keys).boxed().collect(Collectors.toList())), name);
+
+            return this;
+        }
+
         /** {@inheritDoc} */
         @Override
         public IgniteIndex getIndex(String idxName) {
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregateDistinctPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregateDistinctPlannerTest.java
index 255d553..b209438 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregateDistinctPlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregateDistinctPlannerTest.java
@@ -22,9 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.sql.SqlExplainLevel;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteAggregate;
 import org.apache.ignite.internal.sql.engine.rel.IgniteIndexScan;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
@@ -55,7 +53,7 @@ public class AggregateDistinctPlannerTest extends AbstractAggregatePlannerTest {
     @ParameterizedTest
     @EnumSource
     public void mapReduceDistinctWithIndex(AggregateAlgorithm algo) throws Exception {
-        TestTable tbl = createAffinityTable().addIndex(RelCollations.of(ImmutableIntList.of(1, 2)), "val0_val1");
+        TestTable tbl = createAffinityTable().addIndex("val0_val1", 1, 2);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java
index 6767d58..617bd77 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/AggregatePlannerTest.java
@@ -25,14 +25,17 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.math.BigDecimal;
+import java.util.stream.Stream;
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.sql.fun.SqlAvgAggFunction;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteAggregate;
 import org.apache.ignite.internal.sql.engine.rel.IgniteIndexScan;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
@@ -47,11 +50,14 @@ import org.apache.ignite.internal.sql.engine.rel.agg.IgniteSingleAggregateBase;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteSingleHashAggregate;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteSingleSortAggregate;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
+import org.apache.ignite.internal.sql.engine.trait.IgniteDistribution;
 import org.apache.ignite.internal.sql.engine.trait.IgniteDistributions;
 import org.apache.ignite.internal.sql.engine.trait.TraitUtils;
 import org.hamcrest.core.IsInstanceOf;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
 
 /**
  * AggregatePlannerTest.
@@ -67,7 +73,7 @@ public class AggregatePlannerTest extends AbstractAggregatePlannerTest {
     @ParameterizedTest
     @EnumSource
     public void singleWithoutIndex(AggregateAlgorithm algo) throws Exception {
-        TestTable tbl = createBroadcastTable().addIndex(RelCollations.of(ImmutableIntList.of(1, 2)), "val0_val1");
+        TestTable tbl = createBroadcastTable().addIndex("val0_val1", 1, 2);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
@@ -104,7 +110,7 @@ public class AggregatePlannerTest extends AbstractAggregatePlannerTest {
     @ParameterizedTest
     @EnumSource
     public void singleWithIndex(AggregateAlgorithm algo) throws Exception {
-        TestTable tbl = createBroadcastTable().addIndex(RelCollations.of(ImmutableIntList.of(3, 4)), "grp0_grp1");
+        TestTable tbl = createBroadcastTable().addIndex("grp0_grp1", 3, 4);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
@@ -184,7 +190,7 @@ public class AggregatePlannerTest extends AbstractAggregatePlannerTest {
     @ParameterizedTest
     @EnumSource
     public void distribution(AggregateAlgorithm algo) throws Exception {
-        TestTable tbl = createAffinityTable().addIndex(RelCollations.of(ImmutableIntList.of(3)), "grp0");
+        TestTable tbl = createAffinityTable().addIndex("grp0", 3);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
@@ -225,8 +231,8 @@ public class AggregatePlannerTest extends AbstractAggregatePlannerTest {
     @EnumSource
     public void expandDistinctAggregates(AggregateAlgorithm algo) throws Exception {
         TestTable tbl = createAffinityTable()
-                .addIndex(RelCollations.of(ImmutableIntList.of(3, 1, 0)), "idx_val0")
-                .addIndex(RelCollations.of(ImmutableIntList.of(3, 2, 0)), "idx_val1");
+                .addIndex("idx_val0", 3, 1, 0)
+                .addIndex("idx_val1", 3, 2, 0);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
@@ -268,7 +274,7 @@ public class AggregatePlannerTest extends AbstractAggregatePlannerTest {
                 "Invalid plan\n" + RelOptUtil.toString(phys, SqlExplainLevel.ALL_ATTRIBUTES)
         );
 
-        // Check the second aggrgation step contains accumulators.
+        // Check the second aggregation step contains accumulators.
         assertTrue(
                 findNodes(phys, byClass(algo.single)).stream()
                         .noneMatch(n -> ((Aggregate) n).getAggCallList().isEmpty()),
@@ -276,6 +282,70 @@ public class AggregatePlannerTest extends AbstractAggregatePlannerTest {
         );
     }
 
+    @ParameterizedTest
+    @MethodSource("provideAlgoAndDistribution")
+    public void singleSumTypes(AggregateAlgorithm algo, IgniteDistribution distr) throws Exception {
+        IgniteSchema schema = createSchema(
+                createTable(
+                        "TEST", distr,
+                        "ID", Integer.class,
+                        "GRP", Integer.class,
+                        "VAL_TINYINT", Byte.class,
+                        "VAL_SMALLINT", Short.class,
+                        "VAL_INT", Integer.class,
+                        "VAL_BIGINT", Long.class,
+                        "VAL_DECIMAL", BigDecimal.class,
+                        "VAL_FLOAT", Float.class,
+                        "VAL_DOUBLE", Double.class
+                )
+        );
+
+        String sql = "SELECT "
+                + "SUM(VAL_TINYINT), "
+                + "SUM(VAL_SMALLINT), "
+                + "SUM(VAL_INT), "
+                + "SUM(VAL_BIGINT), "
+                + "SUM(VAL_DECIMAL), "
+                + "SUM(VAL_FLOAT), "
+                + "SUM(VAL_DOUBLE) "
+                + "FROM test GROUP BY grp";
+
+        IgniteRel phys = physicalPlan(
+                sql,
+                schema,
+                algo.rulesToDisable
+        );
+
+        checkSplitAndSerialization(phys, schema);
+
+        Class<? extends SingleRel> cls = distr == IgniteDistributions.broadcast() ? algo.single : algo.reduce;
+
+        SingleRel agg = findFirstNode(phys, byClass(cls));
+
+        assertNotNull(agg, "Invalid plan\n" + RelOptUtil.toString(phys));
+
+        RelDataType rowTypes = agg.getRowType();
+
+        RelDataTypeFactory tf = phys.getCluster().getTypeFactory();
+
+        assertEquals(tf.createJavaType(Long.class), rowTypes.getFieldList().get(1).getType());
+        assertEquals(tf.createJavaType(Long.class), rowTypes.getFieldList().get(2).getType());
+        assertEquals(tf.createJavaType(Long.class), rowTypes.getFieldList().get(3).getType());
+        assertEquals(tf.createJavaType(BigDecimal.class), rowTypes.getFieldList().get(4).getType());
+        assertEquals(tf.createJavaType(BigDecimal.class), rowTypes.getFieldList().get(5).getType());
+        assertEquals(tf.createJavaType(Double.class), rowTypes.getFieldList().get(6).getType());
+        assertEquals(tf.createJavaType(Double.class), rowTypes.getFieldList().get(7).getType());
+    }
+
+    private static Stream<Arguments> provideAlgoAndDistribution() {
+        return Stream.of(
+                Arguments.of(AggregateAlgorithm.SORT, IgniteDistributions.broadcast()),
+                Arguments.of(AggregateAlgorithm.SORT, IgniteDistributions.random()),
+                Arguments.of(AggregateAlgorithm.HASH, IgniteDistributions.broadcast()),
+                Arguments.of(AggregateAlgorithm.HASH, IgniteDistributions.random())
+        );
+    }
+
     enum AggregateAlgorithm {
         SORT(
                 IgniteSingleSortAggregate.class,
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
index bf27768..4c966e6 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CorrelatedNestedLoopJoinPlannerTest.java
@@ -23,12 +23,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexFieldAccess;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteIndexScan;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
@@ -81,7 +79,7 @@ public class CorrelatedNestedLoopJoinPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.broadcast();
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t1_jid_idx")
+                        .addIndex("t1_jid_idx", 1, 0)
         );
 
         String sql = "select * "
@@ -142,7 +140,7 @@ public class CorrelatedNestedLoopJoinPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.broadcast();
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t0_jid_idx")
+                        .addIndex("t0_jid_idx", 1, 0)
         );
 
         publicSchema.addTable(
@@ -159,7 +157,7 @@ public class CorrelatedNestedLoopJoinPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.broadcast();
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t1_jid_idx")
+                        .addIndex("t1_jid_idx", 1, 0)
         );
 
         String sql = "select * "
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java
index 5a3779c..c2c5b51 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashAggregatePlannerTest.java
@@ -22,11 +22,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.fun.SqlAvgAggFunction;
 import org.apache.calcite.sql.fun.SqlCountAggFunction;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteMapHashAggregate;
 import org.apache.ignite.internal.sql.engine.rel.agg.IgniteReduceHashAggregate;
@@ -102,7 +100,7 @@ public class HashAggregatePlannerTest extends AbstractAggregatePlannerTest {
      */
     @Test
     public void noGroupByAggregate() throws Exception {
-        TestTable tbl = createAffinityTable().addIndex(RelCollations.of(ImmutableIntList.of(1, 2)), "val0_val1");
+        TestTable tbl = createAffinityTable().addIndex("val0_val1", 1, 2);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashIndexSpoolPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashIndexSpoolPlannerTest.java
index 526fef1..68227c7 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashIndexSpoolPlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/HashIndexSpoolPlannerTest.java
@@ -23,12 +23,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexFieldAccess;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteHashIndexSpool;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
@@ -66,7 +64,7 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.affinity(0, "T0", "hash");
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t0_jid_idx")
+                        .addIndex("t0_jid_idx", 1, 0)
         );
 
         publicSchema.addTable(
@@ -83,7 +81,7 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.affinity(0, "T1", "hash");
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t1_jid_idx")
+                        .addIndex("t1_jid_idx", 1, 0)
         );
 
         String sql = "select * "
@@ -147,7 +145,7 @@ public class HashIndexSpoolPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.affinity(0, "T1", "hash");
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t1_jid0_idx")
+                        .addIndex("t1_jid0_idx", 1, 0)
         );
 
         String sql = "select * "
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
index 8113511..68b9b92 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/LimitOffsetPlannerTest.java
@@ -17,11 +17,6 @@
 
 package org.apache.ignite.internal.sql.engine.planner;
 
-import static java.util.stream.Collectors.toList;
-
-import java.util.Arrays;
-import org.apache.calcite.rel.RelCollations;
-import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.util.ImmutableIntList;
@@ -182,20 +177,7 @@ public class LimitOffsetPlannerTest extends AbstractPlannerTest {
         };
 
         if (!ArrayUtils.nullOrEmpty(indexedColumns)) {
-            table.addIndex(
-                    RelCollations.of(
-                            Arrays.stream(indexedColumns)
-                                    .mapToObj(
-                                            idx -> new RelFieldCollation(
-                                                    idx,
-                                                    RelFieldCollation.Direction.ASCENDING,
-                                                    RelFieldCollation.NullDirection.FIRST
-                                            )
-                                    )
-                                    .collect(toList())
-                    ),
-                    "test_idx"
-            );
+            table.addIndex("test_idx", indexedColumns);
         }
 
         return createSchema(table);
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortAggregatePlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortAggregatePlannerTest.java
index ebafa11..cc8f28b 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortAggregatePlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortAggregatePlannerTest.java
@@ -29,7 +29,6 @@ import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteCorrelatedNestedLoopJoin;
 import org.apache.ignite.internal.sql.engine.rel.IgniteLimit;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
@@ -55,7 +54,7 @@ public class SortAggregatePlannerTest extends AbstractAggregatePlannerTest {
      */
     @Test
     public void notApplicableForSortAggregate() {
-        TestTable tbl = createAffinityTable().addIndex(RelCollations.of(ImmutableIntList.of(1, 2)), "val0_val1");
+        TestTable tbl = createAffinityTable().addIndex("val0_val1", 1, 2);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
@@ -129,7 +128,7 @@ public class SortAggregatePlannerTest extends AbstractAggregatePlannerTest {
                 return IgniteDistributions.broadcast();
             }
         }
-                .addIndex(RelCollations.of(ImmutableIntList.of(3, 4)), "grp0_1");
+                .addIndex("grp0_1", 3, 4);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
@@ -177,7 +176,7 @@ public class SortAggregatePlannerTest extends AbstractAggregatePlannerTest {
                 return IgniteDistributions.affinity(0, "test", "hash");
             }
         }
-                .addIndex(RelCollations.of(ImmutableIntList.of(3, 4)), "grp0_1");
+                .addIndex("grp0_1", 3, 4);
 
         IgniteSchema publicSchema = new IgniteSchema("PUBLIC");
 
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
index bbb9e9d..a4d9cd3 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/SortedIndexSpoolPlannerTest.java
@@ -23,12 +23,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexFieldAccess;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.ImmutableIntList;
 import org.apache.ignite.internal.sql.engine.rel.IgniteRel;
 import org.apache.ignite.internal.sql.engine.rel.IgniteSortedIndexSpool;
 import org.apache.ignite.internal.sql.engine.schema.IgniteSchema;
@@ -66,7 +64,7 @@ public class SortedIndexSpoolPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.affinity(0, "T0", "hash");
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t0_jid_idx")
+                        .addIndex("t0_jid_idx", 1, 0)
         );
 
         publicSchema.addTable(
@@ -83,7 +81,7 @@ public class SortedIndexSpoolPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.affinity(0, "T1", "hash");
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t1_jid_idx")
+                        .addIndex("t1_jid_idx", 1, 0)
         );
 
         String sql = "select * "
@@ -158,7 +156,7 @@ public class SortedIndexSpoolPlannerTest extends AbstractPlannerTest {
                         return IgniteDistributions.affinity(0, "T1", "hash");
                     }
                 }
-                        .addIndex(RelCollations.of(ImmutableIntList.of(1, 0)), "t1_jid0_idx")
+                        .addIndex("t1_jid0_idx", 1, 0)
         );
 
         String sql = "select * "