You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by tl...@apache.org on 2021/10/20 15:43:20 UTC

[ignite-3] branch main updated: IGNITE-15600 Cache for Calcite SQL plans in 3.0 (#403)

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

tledkov 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 4760f67  IGNITE-15600 Cache for Calcite SQL plans in 3.0 (#403)
4760f67 is described below

commit 4760f677f8c2eeaeb34321216ef8bf4a796898de
Author: korlov42 <ko...@gridgain.com>
AuthorDate: Wed Oct 20 18:43:15 2021 +0300

    IGNITE-15600 Cache for Calcite SQL plans in 3.0 (#403)
---
 modules/calcite/pom.xml                            |  5 ++
 .../query/calcite/SqlQueryProcessor.java           | 19 +++++--
 .../calcite/exec/exp/ExpressionFactoryImpl.java    | 14 +++--
 .../calcite/exec/exp/agg/AccumulatorsFactory.java  |  9 ++--
 .../query/calcite/externalize/RelJson.java         |  8 +--
 ...DummyPlanCache.java => QueryPlanCacheImpl.java} | 27 ++++++++--
 .../query/calcite/schema/SchemaHolderImpl.java     | 11 +++-
 .../processors/query/calcite/util/LocalCache.java  | 59 ----------------------
 .../ignite/internal/calcite/ITAggregatesTest.java  |  8 +--
 .../ignite/internal/calcite/ITDataTypesTest.java   |  1 -
 .../ignite/internal/calcite/ITFunctionsTest.java   | 16 ++++--
 .../ignite/internal/calcite/ITMetadataTest.java    | 25 +++++++++
 .../calcite/ITProjectScanMergeRuleTest.java        |  8 +--
 parent/pom.xml                                     |  7 +++
 14 files changed, 126 insertions(+), 91 deletions(-)

diff --git a/modules/calcite/pom.xml b/modules/calcite/pom.xml
index 1251065..a6b2597 100644
--- a/modules/calcite/pom.xml
+++ b/modules/calcite/pom.xml
@@ -53,6 +53,11 @@
         </dependency>
 
         <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.calcite</groupId>
             <artifactId>calcite-core</artifactId>
         </dependency>
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/SqlQueryProcessor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/SqlQueryProcessor.java
index bacd5bb..769ce75 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/SqlQueryProcessor.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/SqlQueryProcessor.java
@@ -30,7 +30,8 @@ import org.apache.ignite.internal.processors.query.calcite.exec.QueryTaskExecuto
 import org.apache.ignite.internal.processors.query.calcite.exec.QueryTaskExecutorImpl;
 import org.apache.ignite.internal.processors.query.calcite.message.MessageService;
 import org.apache.ignite.internal.processors.query.calcite.message.MessageServiceImpl;
-import org.apache.ignite.internal.processors.query.calcite.prepare.DummyPlanCache;
+import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCache;
+import org.apache.ignite.internal.processors.query.calcite.prepare.QueryPlanCacheImpl;
 import org.apache.ignite.internal.processors.query.calcite.schema.SchemaHolderImpl;
 import org.apache.ignite.internal.table.distributed.TableManager;
 import org.apache.ignite.internal.table.event.TableEvent;
@@ -44,6 +45,9 @@ import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 public class SqlQueryProcessor implements QueryProcessor {
+    /** Size of the cache for query plans. */
+    public static final int PLAN_CACHE_SIZE = 1024;
+
     private volatile ExecutionService executionSrvc;
 
     private volatile MessageService msgSrvc;
@@ -57,6 +61,9 @@ public class SqlQueryProcessor implements QueryProcessor {
     /** Busy lock for stop synchronisation. */
     private final IgniteSpinBusyLock busyLock = new IgniteSpinBusyLock();
 
+    /** Keeps queries plans to avoid expensive planning of the same queries. */
+    private final QueryPlanCache planCache = new QueryPlanCacheImpl(PLAN_CACHE_SIZE);
+
     /** Event listeners to close. */
     private final List<Pair<TableEvent, EventListener>> evtLsnrs = new ArrayList<>();
 
@@ -78,12 +85,12 @@ public class SqlQueryProcessor implements QueryProcessor {
             taskExecutor
         );
 
-        SchemaHolderImpl schemaHolder = new SchemaHolderImpl();
+        SchemaHolderImpl schemaHolder = new SchemaHolderImpl(planCache::clear);
 
         executionSrvc = new ExecutionServiceImpl<>(
             clusterSrvc.topologyService(),
             msgSrvc,
-            new DummyPlanCache(),
+            planCache,
             schemaHolder,
             taskExecutor,
             ArrayRowHandler.INSTANCE
@@ -96,6 +103,7 @@ public class SqlQueryProcessor implements QueryProcessor {
         taskExecutor.start();
         msgSrvc.start();
         executionSrvc.start();
+        planCache.start();
     }
 
     /** */
@@ -110,10 +118,11 @@ public class SqlQueryProcessor implements QueryProcessor {
     @Override public void stop() throws Exception {
         busyLock.block();
 
-        List<AutoCloseable> toClose = new ArrayList<AutoCloseable>(Arrays.asList(
+        List<AutoCloseable> toClose = new ArrayList<>(Arrays.asList(
             executionSrvc::stop,
             msgSrvc::stop,
-            taskExecutor::stop
+            taskExecutor::stop,
+            planCache::stop
         ));
 
         toClose.addAll(evtLsnrs.stream()
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java
index adbffaf..8235b2f 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java
@@ -24,11 +24,13 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
+import com.github.benmanes.caffeine.cache.Caffeine;
 import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.enumerable.EnumUtils;
 import org.apache.calcite.linq4j.function.Function1;
@@ -71,8 +73,14 @@ import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
  * Each expression compiles into a class and a wrapper over it is returned.
  */
 public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
-//    /** */
-//    private static final Map<String, Scalar> SCALAR_CACHE = new GridBoundedConcurrentLinkedHashMap<>(1024);
+    /** */
+    private static final int CACHE_SIZE = 1024;
+
+    /** */
+    private static final ConcurrentMap<String, Scalar> SCALAR_CACHE = Caffeine.newBuilder()
+        .maximumSize(CACHE_SIZE)
+        .<String, Scalar>build()
+        .asMap();
 
     /** */
     private final IgniteTypeFactory typeFactory;
@@ -240,7 +248,7 @@ public class ExpressionFactoryImpl<Row> implements ExpressionFactory<Row> {
 
     /** {@inheritDoc} */
     @Override public Scalar scalar(List<RexNode> nodes, RelDataType type) {
-        return compile(nodes, type);
+        return SCALAR_CACHE.computeIfAbsent(digest(nodes, type), k -> compile(nodes, type));
     }
 
     /** */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/agg/AccumulatorsFactory.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/agg/AccumulatorsFactory.java
index f39b07d..5f9e572 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/agg/AccumulatorsFactory.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/agg/AccumulatorsFactory.java
@@ -22,6 +22,8 @@ import java.util.List;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
 import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.enumerable.EnumUtils;
 import org.apache.calcite.adapter.enumerable.JavaRowFormat;
@@ -46,7 +48,6 @@ import org.apache.ignite.internal.processors.query.calcite.exec.RowHandler;
 import org.apache.ignite.internal.processors.query.calcite.prepare.PlanningContext;
 import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
 import org.apache.ignite.internal.processors.query.calcite.util.Commons;
-import org.apache.ignite.internal.processors.query.calcite.util.LocalCache;
 import org.apache.ignite.internal.processors.query.calcite.util.Primitives;
 import org.jetbrains.annotations.NotNull;
 
@@ -56,11 +57,11 @@ import static org.apache.ignite.internal.util.CollectionUtils.nullOrEmpty;
 /** */
 public class AccumulatorsFactory<Row> implements Supplier<List<AccumulatorWrapper<Row>>> {
     /** */
-    private static final LocalCache<Pair<RelDataType, RelDataType>, Function<Object, Object>> CACHE =
-        new LocalCache<>(AccumulatorsFactory::cast0);
+    private static final LoadingCache<Pair<RelDataType, RelDataType>, Function<Object, Object>> CACHE =
+        Caffeine.newBuilder().build(AccumulatorsFactory::cast0);
 
     /** */
-    public static interface CastFunction extends Function<Object, Object> {
+    public interface CastFunction extends Function<Object, Object> {
         @Override Object apply(Object o);
     }
 
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJson.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJson.java
index b099a30..8f1bdb2 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJson.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJson.java
@@ -30,6 +30,8 @@ import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
 import com.google.common.collect.ImmutableList;
 import org.apache.calcite.avatica.AvaticaUtils;
 import org.apache.calcite.avatica.util.TimeUnit;
@@ -100,7 +102,6 @@ import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribut
 import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
 import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
 import org.apache.ignite.internal.processors.query.calcite.util.Commons;
-import org.apache.ignite.internal.processors.query.calcite.util.LocalCache;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.lang.IgniteException;
 
@@ -118,13 +119,14 @@ class RelJson {
 
     /** */
     @SuppressWarnings("PublicInnerClass") @FunctionalInterface
-    public static interface RelFactory extends Function<RelInput, RelNode> {
+    public interface RelFactory extends Function<RelInput, RelNode> {
         /** {@inheritDoc} */
         @Override RelNode apply(RelInput input);
     }
 
     /** */
-    private static final LocalCache<String, RelFactory> FACTORIES_CACHE = new LocalCache<>(RelJson::relFactory);
+    private static final LoadingCache<String, RelFactory> FACTORIES_CACHE = Caffeine.newBuilder()
+        .build(RelJson::relFactory);
 
     /** */
     private static RelFactory relFactory(String typeName) {
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DummyPlanCache.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java
similarity index 63%
rename from modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DummyPlanCache.java
rename to modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java
index 4d0c4c7..68c1ac6 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/DummyPlanCache.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/QueryPlanCacheImpl.java
@@ -17,16 +17,37 @@
 package org.apache.ignite.internal.processors.query.calcite.prepare;
 
 import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+/**
+ * Implementation of {@link QueryPlanCache} that simply wraps
+ * a {@link Caffeine} cache.
+ */
+public class QueryPlanCacheImpl implements QueryPlanCache {
+    private final ConcurrentMap<CacheKey, List<QueryPlan>> cache;
+
+    /**
+     * Creates a plan cache of provided size.
+     *
+     * @param cacheSize Desired cache size.
+     */
+    public QueryPlanCacheImpl(int cacheSize) {
+        cache = Caffeine.newBuilder()
+            .maximumSize(cacheSize)
+            .<CacheKey, List<QueryPlan>>build()
+            .asMap();
+    }
 
-public class DummyPlanCache implements QueryPlanCache {
     /** {@inheritDoc} */
     @Override public List<QueryPlan> queryPlan(PlanningContext ctx, CacheKey key, QueryPlanFactory factory) {
-        return factory.create(ctx);
+        return cache.computeIfAbsent(key, k -> factory.create(ctx));
     }
 
     /** {@inheritDoc} */
     @Override public void clear() {
-        // No-op.
+        cache.clear();
     }
 
     /** {@inheritDoc} */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SchemaHolderImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SchemaHolderImpl.java
index d905303..f928e57 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SchemaHolderImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SchemaHolderImpl.java
@@ -37,9 +37,14 @@ public class SchemaHolderImpl implements SchemaHolder {
     private final Map<String, IgniteSchema> igniteSchemas = new HashMap<>();
 
     /** */
+    private final Runnable onSchemaUpdatedCallback;
+
+    /** */
     private volatile SchemaPlus calciteSchema;
 
-    public SchemaHolderImpl() {
+    public SchemaHolderImpl(Runnable onSchemaUpdatedCallback) {
+        this.onSchemaUpdatedCallback = onSchemaUpdatedCallback;
+
         SchemaPlus newCalciteSchema = Frameworks.createRootSchema(false);
         newCalciteSchema.add("PUBLIC", new IgniteSchema("PUBLIC"));
         calciteSchema = newCalciteSchema;
@@ -70,7 +75,7 @@ public class SchemaHolderImpl implements SchemaHolder {
 
         List<ColumnDescriptor> colDescriptors = descriptor.columnNames().stream()
             .map(descriptor::column)
-            .sorted(Comparator.comparingInt(Column::schemaIndex))
+            .sorted(Comparator.comparingInt(Column::columnOrder))
             .map(col -> new ColumnDescriptorImpl(
                 col.name(),
                 descriptor.isKeyColumn(col.schemaIndex()),
@@ -110,6 +115,8 @@ public class SchemaHolderImpl implements SchemaHolder {
         newCalciteSchema.add("PUBLIC", new IgniteSchema("PUBLIC"));
         igniteSchemas.forEach(newCalciteSchema::add);
         calciteSchema = newCalciteSchema;
+
+        onSchemaUpdatedCallback.run();
     }
 
     private static String removeSchema(String schemaName, String canonicalName) {
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/LocalCache.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/LocalCache.java
deleted file mode 100644
index 5071ad7..0000000
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/LocalCache.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.query.calcite.util;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.function.Function;
-
-/** */
-public class LocalCache<K, V> {
-    /** */
-    private final ConcurrentMap<K, V> map = new ConcurrentHashMap<>();
-
-    /** */
-    private final Function<K, V> builder;
-
-    /** */
-    public LocalCache() {
-        this(null);
-    }
-
-    /** */
-    public LocalCache(Function<K, V> builder) {
-        this.builder = builder;
-    }
-
-    /** */
-    public V get(K key) {
-        if (builder != null)
-            return map.computeIfAbsent(key, builder);
-        else
-            return map.get(key);
-    }
-
-    /** */
-    public void put(K key, V val) {
-        map.put(key, val);
-    }
-
-    /** */
-    public void clear() {
-        map.clear();
-    }
-}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITAggregatesTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITAggregatesTest.java
index 1b596b7..933d134 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITAggregatesTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITAggregatesTest.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.calcite;
 
 import java.util.List;
 
-import org.apache.ignite.lang.IgniteInternalException;
+import org.apache.ignite.lang.IgniteException;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -113,17 +113,17 @@ public class ITAggregatesTest extends AbstractBasicIntegrationTest {
     @Test
     public void testMultipleRowsFromSingleAggr() {
         assertThrows(
-            IgniteInternalException.class,
+            IgniteException.class,
             () -> assertQuery("SELECT (SELECT name FROM person)").check()
         );
 
         assertThrows(
-            IgniteInternalException.class,
+            IgniteException.class,
             () -> assertQuery("SELECT t.id, (SELECT x FROM TABLE(system_range(1, 5))) FROM person t").check()
         );
 
         assertThrows(
-            IgniteInternalException.class,
+            IgniteException.class,
             () -> assertQuery("SELECT t.id, (SELECT x FROM " +
                 "TABLE(system_range(t.id, t.id + 1))) FROM person t").check()
         );
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITDataTypesTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITDataTypesTest.java
index f4181b1..86d42d5 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITDataTypesTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITDataTypesTest.java
@@ -29,7 +29,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 /**
  * Test SQL data types.
  */
-@Disabled("https://issues.apache.org/jira/browse/IGNITE-15655")
 public class ITDataTypesTest extends AbstractBasicIntegrationTest {
     /** */
     @Disabled("https://issues.apache.org/jira/browse/IGNITE-15107")
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITFunctionsTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITFunctionsTest.java
index b7c7af7..f43ca9c 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITFunctionsTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITFunctionsTest.java
@@ -24,7 +24,8 @@ import java.util.List;
 import java.util.function.LongFunction;
 
 import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
-import org.apache.ignite.lang.IgniteInternalException;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.lang.LoggerMessageHelper;
 import org.apache.ignite.schema.SchemaBuilders;
 import org.apache.ignite.schema.definition.ColumnType;
 import org.apache.ignite.schema.definition.TableDefinition;
@@ -121,10 +122,19 @@ public class ITFunctionsTest extends AbstractBasicIntegrationTest {
 
         assertEquals(0, sql("SELECT * FROM table(system_range(null, 1))").size());
 
-        IgniteInternalException ex = assertThrows(IgniteInternalException.class,
+        IgniteException ex = assertThrows(IgniteException.class,
             () -> sql("SELECT * FROM table(system_range(1, 1, 0))"));
 
-        assertEquals("Increment can't be 0", ex.getMessage());
+        assertTrue(
+            ex.getCause() instanceof IllegalArgumentException,
+            LoggerMessageHelper.format(
+                "Expected cause is {}, but was {}",
+                IllegalArgumentException.class.getSimpleName(),
+                ex.getCause() == null ? null : ex.getCause().getClass().getSimpleName()
+            )
+        );
+
+        assertEquals("Increment can't be 0", ex.getCause().getMessage());
     }
 
     /** */
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITMetadataTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITMetadataTest.java
index d5492bc..34b447e 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITMetadataTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITMetadataTest.java
@@ -18,6 +18,10 @@
 
 package org.apache.ignite.internal.calcite;
 
+import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
+import org.apache.ignite.schema.SchemaBuilders;
+import org.apache.ignite.schema.definition.ColumnType;
+import org.apache.ignite.schema.definition.TableDefinition;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
@@ -78,4 +82,25 @@ public class ITMetadataTest extends AbstractBasicIntegrationTest {
             .columnTypes(Integer.class, Byte.class, Short.class, String.class)
             .check();
     }
+
+    /** */
+    @Test
+    public void columnOrder() {
+        TableDefinition schTbl1 = SchemaBuilders.tableBuilder("PUBLIC", "COLUMN_ORDER").columns(
+            SchemaBuilders.column("DOUBLE_C", ColumnType.DOUBLE).asNullable().build(),
+            SchemaBuilders.column("LONG_C", ColumnType.INT64).asNonNull().build(),
+            SchemaBuilders.column("STRING_C", ColumnType.string()).asNullable().build(),
+            SchemaBuilders.column("INT_C", ColumnType.INT32).asNullable().build()
+        ).withPrimaryKey("LONG_C").build();
+
+        CLUSTER_NODES.get(0).tables().createTable(schTbl1.canonicalName(), tblCh ->
+            SchemaConfigurationConverter.convert(schTbl1, tblCh)
+                .changeReplicas(1)
+                .changePartitions(10)
+        );
+
+        assertQuery("select * from column_order")
+            .columnNames("DOUBLE_C", "LONG_C", "STRING_C", "INT_C")
+            .check();
+    }
 }
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITProjectScanMergeRuleTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITProjectScanMergeRuleTest.java
index 0bd6d0e..96aab11 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITProjectScanMergeRuleTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/calcite/ITProjectScanMergeRuleTest.java
@@ -18,7 +18,7 @@
 package org.apache.ignite.internal.calcite;
 
 import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
-import org.apache.ignite.lang.IgniteInternalException;
+import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.schema.SchemaBuilders;
 import org.apache.ignite.schema.definition.ColumnType;
 import org.apache.ignite.schema.definition.TableDefinition;
@@ -140,17 +140,17 @@ public class ITProjectScanMergeRuleTest extends AbstractBasicIntegrationTest {
             .check();
 
         assertThrows(
-            IgniteInternalException.class,
+            IgniteException.class,
             () -> assertQuery("SELECT NAME FROM products WHERE CAT_ID = (SELECT CAT_ID FROM products WHERE SUBCAT_ID = 11)").check()
         );
 
         assertThrows(
-            IgniteInternalException.class,
+            IgniteException.class,
             () -> assertQuery("SELECT NAME FROM products WHERE CAT_ID = (SELECT 2 UNION ALL SELECT 1)").check()
         );
 
         assertThrows(
-            IgniteInternalException.class,
+            IgniteException.class,
             () -> assertQuery("SELECT NAME FROM products WHERE CAT_ID = (SELECT null UNION ALL SELECT 1)").check()
         );
     }
diff --git a/parent/pom.xml b/parent/pom.xml
index 8a1773b..ef02b7a 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -82,6 +82,7 @@
         <metrics.version>4.0.2</metrics.version>
         <jctools.version>3.3.0</jctools.version>
         <msgpack.version>0.8.21</msgpack.version>
+        <caffeine.version>3.0.4</caffeine.version>
 
         <!-- Plugins versions -->
         <apache.rat.plugin.version>0.13</apache.rat.plugin.version>
@@ -441,6 +442,12 @@
                 <version>${msgpack.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>com.github.ben-manes.caffeine</groupId>
+                <artifactId>caffeine</artifactId>
+                <version>${caffeine.version}</version>
+            </dependency>
+
             <!-- Test dependencies -->
             <dependency>
                 <groupId>org.apache.ignite</groupId>