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

gvvinblade pushed a commit to branch ignite-12248
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/ignite-12248 by this push:
     new 11c1a330 Calcite version updated. This closes #8380
11c1a330 is described below

commit 11c1a3309dade5262b3421c44f8b05b745e2b9da
Author: Igor Seliverstov <[email protected]>
AuthorDate: Wed Oct 28 15:55:36 2020 +0300

    Calcite version updated. This closes #8380
---
 modules/calcite/pom.xml                            |   8 +-
 .../query/calcite/CalciteQueryProcessor.java       |  20 +-
 .../query/calcite/exec/ExecutionServiceImpl.java   |   2 +
 .../processors/query/calcite/exec/IndexScan.java   |   5 +-
 .../processors/query/calcite/exec/TableScan.java   |   5 +-
 .../processors/query/calcite/exec/rel/Inbox.java   |   2 +-
 .../query/calcite/exec/rel/ScanNode.java           |   2 +-
 .../query/calcite/externalize/RelJsonWriter.java   |   8 +-
 .../query/calcite/metadata/IgniteMetadata.java     |  32 +--
 .../query/calcite/prepare/PlannerPhase.java        | 124 ++++++++----
 .../calcite/rel/AbstractIgniteNestedLoopJoin.java  |  71 +++----
 .../query/calcite/rel/IgniteAggregate.java         |  51 ++---
 .../rel/IgniteCorrelatedNestedLoopJoin.java        |  33 +--
 .../query/calcite/rel/IgniteExchange.java          |  39 ----
 .../processors/query/calcite/rel/IgniteFilter.java |   8 +-
 .../query/calcite/rel/IgniteProject.java           |   2 -
 .../processors/query/calcite/rel/IgniteSort.java   |  20 +-
 .../query/calcite/rel/IgniteTableModify.java       |  37 ----
 .../query/calcite/rel/IgniteTrimExchange.java      |  28 ---
 .../rel/ProjectableFilterableTableScan.java        |  17 ++
 ...Rule.java => CorrelatedNestedLoopJoinRule.java} |  68 +++++--
 .../calcite/rule/TableModifyConverterRule.java     |   8 +-
 .../rule/{ => logical}/ExposeIndexRule.java        |   2 +-
 .../query/calcite/rule/logical/FilterJoinRule.java |  80 --------
 .../rule/{ => logical}/FilterScanMergeRule.java    |  36 +---
 .../rule/logical/LogicalFilterMergeRule.java       |  46 -----
 .../logical/LogicalFilterProjectTransposeRule.java |  38 ----
 .../calcite/rule/logical/LogicalOrToUnionRule.java |  40 +++-
 .../rule/logical/LogicalProjectMergeRule.java      |  47 -----
 .../rule/logical/LogicalProjectRemoveRule.java     |  49 -----
 .../rule/{ => logical}/ProjectScanMergeRule.java   |   2 +-
 .../query/calcite/trait/RelRegistrar.java          |  94 +++++++++
 .../processors/query/calcite/trait/TraitUtils.java | 222 ++++++++++++++-------
 .../query/calcite/trait/TraitsAwareIgniteRel.java  |  40 +---
 .../calcite/trait/TraitsPropagationContext.java    |  98 ---------
 .../query/calcite/trait/TraitsPropagator.java      |  35 ----
 .../processors/query/calcite/util/RexUtils.java    |   3 +-
 .../processors/query/calcite/PlannerTest.java      |   2 +-
 .../query/calcite/exec/rel/ExecutionTest.java      |   1 -
 .../query/calcite/rules/OrToUnionRuleTest.java     |   2 +-
 .../calcite/rules/ProjectScanMergeRuleTest.java    |   6 +-
 41 files changed, 553 insertions(+), 880 deletions(-)

diff --git a/modules/calcite/pom.xml b/modules/calcite/pom.xml
index c3452e5..095449d 100644
--- a/modules/calcite/pom.xml
+++ b/modules/calcite/pom.xml
@@ -25,7 +25,7 @@
 
     <!-- Module specific package versions -->
     <properties>
-        <calcite.version>1.23.0</calcite.version>
+        <calcite.version>1.26.0</calcite.version>
         <guava.version>19.0</guava.version>
         <janino.version>3.0.11</janino.version>
         <avatica.version>1.16.0</avatica.version>
@@ -69,6 +69,12 @@
 
         <dependency>
             <groupId>org.apache.calcite</groupId>
+            <artifactId>calcite-babel</artifactId>
+            <version>${calcite.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.calcite</groupId>
             <artifactId>calcite-linq4j</artifactId>
             <version>${calcite.version}</version>
         </dependency>
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
index f59b50f..a54f94e 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java
@@ -18,13 +18,13 @@
 package org.apache.ignite.internal.processors.query.calcite;
 
 import java.util.List;
-
 import org.apache.calcite.config.Lex;
 import org.apache.calcite.plan.Contexts;
 import org.apache.calcite.plan.RelOptCostImpl;
 import org.apache.calcite.sql.fun.SqlLibrary;
 import org.apache.calcite.sql.fun.SqlLibraryOperatorTableFactory;
 import org.apache.calcite.sql.parser.SqlParser;
+import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
 import org.apache.calcite.sql.validate.SqlConformanceEnum;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql2rel.SqlToRelConverter;
@@ -68,17 +68,14 @@ public class CalciteQueryProcessor extends 
GridProcessorAdapter implements Query
     /** */
     public static final FrameworkConfig FRAMEWORK_CONFIG = 
Frameworks.newConfigBuilder()
         .executor(EXECUTOR)
-        .sqlToRelConverterConfig(SqlToRelConverter.configBuilder()
+        .sqlToRelConverterConfig(SqlToRelConverter.config()
             .withTrimUnusedFields(true)
-            .withDecorrelationEnabled(true)
-            .build())
-        .parserConfig(SqlParser.configBuilder()
-            // Lexical configuration defines how identifiers are quoted, 
whether they are converted to upper or lower
-            // case when they are read, and whether identifiers are matched 
case-sensitively.
-            .setLex(Lex.ORACLE)
-//                .setParserFactory(SqlDdlParserImpl.FACTORY) // Enables DDL 
support
-            .setConformance(SqlConformanceEnum.DEFAULT)
-            .build())
+            .withDecorrelationEnabled(true))
+        .parserConfig(
+            SqlParser.config()
+                .withParserFactory(SqlBabelParserImpl.FACTORY)
+                .withLex(Lex.ORACLE)
+                .withConformance(SqlConformanceEnum.DEFAULT))
         .sqlValidatorConfig(SqlValidator.Config.DEFAULT
             .withIdentifierExpansion(true)
             .withSqlConformance(SqlConformanceEnum.DEFAULT))
@@ -86,6 +83,7 @@ public class CalciteQueryProcessor extends 
GridProcessorAdapter implements Query
         .operatorTable(SqlLibraryOperatorTableFactory.INSTANCE
             .getOperatorTable(
                 SqlLibrary.STANDARD,
+                SqlLibrary.ORACLE,
                 SqlLibrary.MYSQL))
         // Context provides a way to store data within the planner session 
that can be accessed in planner rules.
         .context(Contexts.empty())
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
index ba9e1c2..2a3065b 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java
@@ -531,6 +531,8 @@ public class ExecutionServiceImpl<Row> extends 
AbstractService implements Execut
         switch (sqlNode.getKind()) {
             case SELECT:
             case ORDER_BY:
+            case WITH:
+            case VALUES:
             case UNION:
                 return prepareQuery(sqlNode, ctx);
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
index dcc178e..e2af26a 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
@@ -41,7 +41,6 @@ import 
org.apache.ignite.internal.processors.cache.distributed.dht.topology.Grid
 import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
 import org.apache.ignite.internal.processors.query.GridIndex;
-import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import 
org.apache.ignite.internal.processors.query.calcite.exec.RowHandler.RowFactory;
 import 
org.apache.ignite.internal.processors.query.calcite.schema.TableDescriptor;
 import org.apache.ignite.internal.processors.query.h2.H2Utils;
@@ -214,11 +213,11 @@ public class IndexScan<Row> implements Iterable<Row>, 
AutoCloseable {
         try {
             for (GridDhtLocalPartition part : toReserve) {
                 if (part == null || !part.reserve())
-                    throw new IgniteSQLException("Failed to reserve partition 
for query execution. Retry on stable topology.");
+                    throw new ClusterTopologyException("Failed to reserve 
partition for query execution. Retry on stable topology.");
                 else if (part.state() != GridDhtPartitionState.OWNING) {
                     part.release();
 
-                    throw new IgniteSQLException("Failed to reserve partition 
for query execution. Retry on stable topology.");
+                    throw new ClusterTopologyException("Failed to reserve 
partition for query execution. Retry on stable topology.");
                 }
 
                 reserved.add(part);
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
index b4690ef..abdbffb 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/TableScan.java
@@ -38,7 +38,6 @@ import 
org.apache.ignite.internal.processors.cache.distributed.dht.topology.Grid
 import 
org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionTopology;
 import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
 import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
-import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import 
org.apache.ignite.internal.processors.query.calcite.exec.RowHandler.RowFactory;
 import 
org.apache.ignite.internal.processors.query.calcite.schema.TableDescriptor;
 import org.apache.ignite.internal.util.lang.GridCursor;
@@ -166,11 +165,11 @@ public class TableScan<Row> implements Iterable<Row>, 
AutoCloseable {
         try {
             for (GridDhtLocalPartition part : toReserve) {
                 if (part == null || !part.reserve())
-                    throw new IgniteSQLException("Failed to reserve partition 
for query execution. Retry on stable topology.");
+                    throw new ClusterTopologyException("Failed to reserve 
partition for query execution. Retry on stable topology.");
                 else if (part.state() != GridDhtPartitionState.OWNING) {
                     part.release();
 
-                    throw new IgniteSQLException("Failed to reserve partition 
for query execution. Retry on stable topology.");
+                    throw new ClusterTopologyException("Failed to reserve 
partition for query execution. Retry on stable topology.");
                 }
 
                 reserved.add(part);
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/Inbox.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/Inbox.java
index 9c0c918..c261b79 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/Inbox.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/Inbox.java
@@ -216,7 +216,7 @@ public class Inbox<Row> extends AbstractNode<Row> 
implements Mailbox<Row>, Singl
     /** */
     private void pushOrdered() throws IgniteCheckedException {
          PriorityQueue<Pair<Row, Buffer>> heap =
-            new PriorityQueue<>(buffers.size(), 
Map.Entry.comparingByKey(comp));
+            new PriorityQueue<>(Math.max(buffers.size(), 1), 
Map.Entry.comparingByKey(comp));
 
         Iterator<Buffer> it = buffers.iterator();
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ScanNode.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ScanNode.java
index 739c773..2006666 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ScanNode.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ScanNode.java
@@ -75,7 +75,7 @@ public class ScanNode<Row> extends AbstractNode<Row> 
implements SingleNode<Row>
             push();
         }
         catch (Exception e) {
-            e.printStackTrace();
+            onError(e);
         }
     }
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJsonWriter.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJsonWriter.java
index 25e8d44..aa35b27 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJsonWriter.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/externalize/RelJsonWriter.java
@@ -22,13 +22,11 @@ import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
-
 import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectWriter;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
-import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlExplainLevel;
 import org.apache.calcite.util.Pair;
 import org.apache.ignite.IgniteException;
@@ -76,9 +74,7 @@ public class RelJsonWriter implements RelWriter {
 
     /** {@inheritDoc} */
     @Override public final void explain(RelNode rel, List<Pair<String, 
Object>> valueList) {
-        try (RexNode.Closeable ignored = withRexNormalize()) {
-            explain_(rel, valueList);
-        }
+        explain_(rel, valueList);
     }
 
     /** {@inheritDoc} */
@@ -107,7 +103,7 @@ public class RelJsonWriter implements RelWriter {
 
     /** */
     public String asString() {
-        try (RexNode.Closeable ignored = withRexNormalize()) {
+        try {
             StringWriter writer = new StringWriter();
             ObjectMapper mapper = new ObjectMapper();
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMetadata.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMetadata.java
index def30fe..a370180 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMetadata.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMetadata.java
@@ -20,24 +20,10 @@ package 
org.apache.ignite.internal.processors.query.calcite.metadata;
 import com.google.common.collect.ImmutableList;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
+import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
 import org.apache.calcite.rel.metadata.Metadata;
 import org.apache.calcite.rel.metadata.MetadataDef;
 import org.apache.calcite.rel.metadata.MetadataHandler;
-import org.apache.calcite.rel.metadata.RelMdAllPredicates;
-import org.apache.calcite.rel.metadata.RelMdColumnOrigins;
-import org.apache.calcite.rel.metadata.RelMdColumnUniqueness;
-import org.apache.calcite.rel.metadata.RelMdDistinctRowCount;
-import org.apache.calcite.rel.metadata.RelMdExplainVisibility;
-import org.apache.calcite.rel.metadata.RelMdExpressionLineage;
-import org.apache.calcite.rel.metadata.RelMdMaxRowCount;
-import org.apache.calcite.rel.metadata.RelMdMemory;
-import org.apache.calcite.rel.metadata.RelMdMinRowCount;
-import org.apache.calcite.rel.metadata.RelMdNodeTypes;
-import org.apache.calcite.rel.metadata.RelMdParallelism;
-import org.apache.calcite.rel.metadata.RelMdPopulationSize;
-import org.apache.calcite.rel.metadata.RelMdSize;
-import org.apache.calcite.rel.metadata.RelMdTableReferences;
-import org.apache.calcite.rel.metadata.RelMdUniqueKeys;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.ignite.internal.processors.query.calcite.util.IgniteMethod;
@@ -63,21 +49,7 @@ public class IgniteMetadata {
                 IgniteMdSelectivity.SOURCE,
 
                 // Basic providers
-                RelMdColumnOrigins.SOURCE,
-                RelMdExpressionLineage.SOURCE,
-                RelMdTableReferences.SOURCE,
-                RelMdNodeTypes.SOURCE,
-                RelMdMaxRowCount.SOURCE,
-                RelMdMinRowCount.SOURCE,
-                RelMdUniqueKeys.SOURCE,
-                RelMdColumnUniqueness.SOURCE,
-                RelMdPopulationSize.SOURCE,
-                RelMdSize.SOURCE,
-                RelMdParallelism.SOURCE,
-                RelMdMemory.SOURCE,
-                RelMdDistinctRowCount.SOURCE,
-                RelMdExplainVisibility.SOURCE,
-                RelMdAllPredicates.SOURCE));
+                DefaultRelMetadataProvider.INSTANCE));
 
     /** */
     public interface NodesMappingMetadata extends Metadata {
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
index 0547393..521d55c 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerPhase.java
@@ -17,35 +17,41 @@
 
 package org.apache.ignite.internal.processors.query.calcite.prepare;
 
-import org.apache.calcite.rel.rules.AggregateReduceFunctionsRule;
-import org.apache.calcite.rel.rules.JoinCommuteRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalJoin;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalSort;
+import org.apache.calcite.rel.rules.AggregateMergeRule;
+import org.apache.calcite.rel.rules.CoreRules;
+import org.apache.calcite.rel.rules.FilterJoinRule.FilterIntoJoinRule;
+import org.apache.calcite.rel.rules.FilterJoinRule.JoinConditionPushRule;
+import org.apache.calcite.rel.rules.FilterMergeRule;
+import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
+import org.apache.calcite.rel.rules.JoinPushExpressionsRule;
 import org.apache.calcite.rel.rules.JoinPushThroughJoinRule;
 import org.apache.calcite.rel.rules.ProjectFilterTransposeRule;
+import org.apache.calcite.rel.rules.ProjectMergeRule;
+import org.apache.calcite.rel.rules.ProjectRemoveRule;
 import org.apache.calcite.rel.rules.SortRemoveRule;
-import org.apache.calcite.rel.rules.SubQueryRemoveRule;
-import org.apache.calcite.rel.rules.UnionMergeRule;
 import org.apache.calcite.tools.Program;
 import org.apache.calcite.tools.RuleSet;
 import org.apache.calcite.tools.RuleSets;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.AggregateConverterRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.CorrelatedNestedLoopJoinConverterRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.ExposeIndexRule;
+import 
org.apache.ignite.internal.processors.query.calcite.rule.CorrelatedNestedLoopJoinRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.FilterConverterRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.FilterScanMergeRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.LogicalScanConverterRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.NestedLoopJoinConverterRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.ProjectConverterRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.ProjectScanMergeRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.SortConverterRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.TableModifyConverterRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.UnionConverterRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.ValuesConverterRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.FilterJoinRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalFilterMergeRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalFilterProjectTransposeRule;
+import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.ExposeIndexRule;
+import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.FilterScanMergeRule;
 import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalOrToUnionRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalProjectMergeRule;
-import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.LogicalProjectRemoveRule;
+import 
org.apache.ignite.internal.processors.query.calcite.rule.logical.ProjectScanMergeRule;
 
 import static 
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePrograms.cbo;
 import static 
org.apache.ignite.internal.processors.query.calcite.prepare.IgnitePrograms.hep;
@@ -59,9 +65,9 @@ public enum PlannerPhase {
         /** {@inheritDoc} */
         @Override public RuleSet getRules(PlanningContext ctx) {
             return RuleSets.ofList(
-                SubQueryRemoveRule.FILTER,
-                SubQueryRemoveRule.PROJECT,
-                SubQueryRemoveRule.JOIN);
+                CoreRules.FILTER_SUB_QUERY_TO_CORRELATE,
+                CoreRules.PROJECT_SUB_QUERY_TO_CORRELATE,
+                CoreRules.JOIN_SUB_QUERY_TO_CORRELATE);
         }
 
         /** {@inheritDoc} */
@@ -75,36 +81,80 @@ public enum PlannerPhase {
         /** {@inheritDoc} */
         @Override public RuleSet getRules(PlanningContext ctx) {
             return RuleSets.ofList(
+                FilterMergeRule.Config.DEFAULT
+                    .withOperandFor(LogicalFilter.class).toRule(),
+
+                JoinPushThroughJoinRule.Config.LEFT
+                    .withOperandFor(LogicalJoin.class).toRule(),
+
+                JoinPushThroughJoinRule.Config.RIGHT
+                    .withOperandFor(LogicalJoin.class).toRule(),
+
+                JoinPushExpressionsRule.Config.DEFAULT
+                    .withOperandFor(LogicalJoin.class).toRule(),
+
+                JoinConditionPushRule.Config.DEFAULT
+                    .withOperandSupplier(b -> b.operand(LogicalJoin.class)
+                        .anyInputs()).toRule(),
+
+                FilterIntoJoinRule.Config.DEFAULT
+                    .withOperandSupplier(b0 ->
+                        b0.operand(LogicalFilter.class).oneInput(b1 ->
+                            
b1.operand(LogicalJoin.class).anyInputs())).toRule(),
+
+                FilterProjectTransposeRule.Config.DEFAULT
+                    .withOperandFor(LogicalFilter.class, f -> true, 
LogicalProject.class, p -> true).toRule(),
+
+                ProjectFilterTransposeRule.Config.DEFAULT
+                    .withOperandFor(LogicalProject.class, 
LogicalFilter.class).toRule(),
+
+                ProjectMergeRule.Config.DEFAULT
+                    .withOperandFor(LogicalProject.class).toRule(),
+
+                ProjectRemoveRule.Config.DEFAULT
+                    .withOperandSupplier(b ->
+                        b.operand(LogicalProject.class)
+                            .predicate(ProjectRemoveRule::isTrivial)
+                            .anyInputs()).toRule(),
+
+                AggregateMergeRule.Config.DEFAULT
+                    .withOperandSupplier(b0 ->
+                        b0.operand(LogicalAggregate.class)
+                            .oneInput(b1 ->
+                                b1.operand(LogicalAggregate.class)
+                                    .predicate(Aggregate::isSimple)
+                                    .anyInputs())).toRule(),
+
+                SortRemoveRule.Config.DEFAULT
+                    .withOperandSupplier(b ->
+                        b.operand(LogicalSort.class)
+                            .anyInputs()).toRule(),
+
+                CoreRules.UNION_MERGE,
+                CoreRules.UNION_REMOVE,
+                CoreRules.JOIN_COMMUTE,
+                CoreRules.AGGREGATE_REMOVE,
+                CoreRules.AGGREGATE_REDUCE_FUNCTIONS,
+
+                ExposeIndexRule.INSTANCE,
+                ProjectScanMergeRule.TABLE_SCAN,
+                ProjectScanMergeRule.INDEX_SCAN,
+                FilterScanMergeRule.TABLE_SCAN,
+                FilterScanMergeRule.INDEX_SCAN,
+
+                LogicalOrToUnionRule.INSTANCE,
+                CorrelatedNestedLoopJoinRule.INSTANCE,
+
                 ValuesConverterRule.INSTANCE,
                 LogicalScanConverterRule.INDEX_SCAN,
                 LogicalScanConverterRule.TABLE_SCAN,
-                ExposeIndexRule.INSTANCE,
                 AggregateConverterRule.INSTANCE,
                 NestedLoopJoinConverterRule.INSTANCE,
-                CorrelatedNestedLoopJoinConverterRule.INSTANCE,
-                FilterJoinRule.PUSH_JOIN_CONDITION,
-                FilterJoinRule.FILTER_ON_JOIN,
                 ProjectConverterRule.INSTANCE,
-                LogicalProjectMergeRule.INSTANCE,
-                LogicalProjectRemoveRule.INSTANCE,
-                ProjectScanMergeRule.TABLE_SCAN,
-                ProjectScanMergeRule.INDEX_SCAN,
                 FilterConverterRule.INSTANCE,
-                LogicalFilterMergeRule.INSTANCE,
-                LogicalFilterProjectTransposeRule.INSTANCE,
-                FilterScanMergeRule.TABLE_SCAN,
-                FilterScanMergeRule.INDEX_SCAN,
                 TableModifyConverterRule.INSTANCE,
-                ProjectFilterTransposeRule.INSTANCE,
-                LogicalOrToUnionRule.INSTANCE,
-                UnionMergeRule.INSTANCE,
                 UnionConverterRule.INSTANCE,
-                SortConverterRule.INSTANCE,
-                JoinCommuteRule.INSTANCE,
-                JoinPushThroughJoinRule.LEFT,
-                JoinPushThroughJoinRule.RIGHT,
-                AggregateReduceFunctionsRule.INSTANCE,
-                SortRemoveRule.INSTANCE);
+                SortConverterRule.INSTANCE);
         }
 
         /** {@inheritDoc} */
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIgniteNestedLoopJoin.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIgniteNestedLoopJoin.java
index 65d1092..3b05e73 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIgniteNestedLoopJoin.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIgniteNestedLoopJoin.java
@@ -118,28 +118,23 @@ public abstract class AbstractIgniteNestedLoopJoin 
extends Join implements Trait
 
         RelTraitSet left = inputTraits.get(0), right = inputTraits.get(1);
 
-        ImmutableList.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableList.builder();
-
         RewindabilityTrait leftRewindability = TraitUtils.rewindability(left);
         RewindabilityTrait rightRewindability = 
TraitUtils.rewindability(right);
 
         RelTraitSet outTraits, leftTraits, rightTraits;
 
-        outTraits = nodeTraits.replace(RewindabilityTrait.ONE_WAY);
-        leftTraits = left.replace(RewindabilityTrait.ONE_WAY);
-        rightTraits = right.replace(RewindabilityTrait.ONE_WAY);
-
-        b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, rightTraits)));
-
         if (leftRewindability.rewindable() && rightRewindability.rewindable()) 
{
             outTraits = nodeTraits.replace(RewindabilityTrait.REWINDABLE);
             leftTraits = left.replace(RewindabilityTrait.REWINDABLE);
             rightTraits = right.replace(RewindabilityTrait.REWINDABLE);
-
-            b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+        }
+        else {
+            outTraits = nodeTraits.replace(RewindabilityTrait.ONE_WAY);
+            leftTraits = left.replace(RewindabilityTrait.ONE_WAY);
+            rightTraits = right.replace(RewindabilityTrait.ONE_WAY);
         }
 
-        return b.build();
+        return ImmutableList.of(Pair.of(outTraits, 
ImmutableList.of(leftTraits, rightTraits)));
     }
 
     /** {@inheritDoc} */
@@ -155,24 +150,28 @@ public abstract class AbstractIgniteNestedLoopJoin 
extends Join implements Trait
 
         RelTraitSet left = inputTraits.get(0), right = inputTraits.get(1);
 
-        ImmutableList.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableList.builder();
+        List<Pair<RelTraitSet, List<RelTraitSet>>> res = new ArrayList<>();
 
         IgniteDistribution leftDistr = TraitUtils.distribution(left);
         IgniteDistribution rightDistr = TraitUtils.distribution(right);
 
         RelTraitSet outTraits, leftTraits, rightTraits;
 
-        outTraits = nodeTraits.replace(single());
-        leftTraits = left.replace(single());
-        rightTraits = right.replace(single());
+        if (leftDistr == broadcast() || rightDistr == broadcast()) {
+            outTraits = nodeTraits.replace(broadcast());
+            leftTraits = left.replace(broadcast());
+            rightTraits = right.replace(broadcast());
 
-        b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, rightTraits)));
+            res.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+        }
 
-        outTraits = nodeTraits.replace(broadcast());
-        leftTraits = left.replace(broadcast());
-        rightTraits = right.replace(broadcast());
+        if (leftDistr == single() || rightDistr == single()) {
+            outTraits = nodeTraits.replace(single());
+            leftTraits = left.replace(single());
+            rightTraits = right.replace(single());
 
-        b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, rightTraits)));
+            res.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+        }
 
         if (!F.isEmpty(joinInfo.pairs())) {
             Set<DistributionFunction> functions = new HashSet<>();
@@ -193,17 +192,17 @@ public abstract class AbstractIgniteNestedLoopJoin 
extends Join implements Trait
 
                 // TODO distribution multitrait support
                 outTraits = nodeTraits.replace(hash(joinInfo.leftKeys, 
function));
-                b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+                res.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
 
                 outTraits = nodeTraits.replace(hash(joinInfo.rightKeys, 
function));
-                b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+                res.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
 
                 if (joinType == INNER || joinType == LEFT) {
                     outTraits = nodeTraits.replace(hash(joinInfo.leftKeys, 
function));
                     leftTraits = left.replace(hash(joinInfo.leftKeys, 
function));
                     rightTraits = right.replace(broadcast());
 
-                    b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+                    res.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
                 }
 
                 if (joinType == INNER || joinType == RIGHT) {
@@ -211,12 +210,16 @@ public abstract class AbstractIgniteNestedLoopJoin 
extends Join implements Trait
                     leftTraits = left.replace(broadcast());
                     rightTraits = right.replace(hash(joinInfo.rightKeys, 
function));
 
-                    b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
+                    res.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
                 }
             }
         }
 
-        return b.build();
+        if (!res.isEmpty())
+            return res;
+
+        return ImmutableList.of(Pair.of(nodeTraits.replace(single()),
+            ImmutableList.of(left.replace(single()), 
right.replace(single()))));
     }
 
     /** {@inheritDoc} */
@@ -233,8 +236,6 @@ public abstract class AbstractIgniteNestedLoopJoin extends 
Join implements Trait
             return ImmutableList.of(Pair.of(nodeTraits,
                 ImmutableList.of(left.replace(RelCollations.EMPTY), 
right.replace(RelCollations.EMPTY))));
 
-        RelTraitSet outTraits, leftTraits, rightTraits;
-
         if (!projectsLeft(collation))
             collation = RelCollations.EMPTY;
         else if (joinType == RIGHT || joinType == JoinRelType.FULL) {
@@ -246,11 +247,8 @@ public abstract class AbstractIgniteNestedLoopJoin extends 
Join implements Trait
             }
         }
 
-        outTraits = nodeTraits.replace(collation);
-        leftTraits = left.replace(collation);
-        rightTraits = right.replace(RelCollations.EMPTY);
-
-        return ImmutableList.of(Pair.of(outTraits, 
ImmutableList.of(leftTraits, rightTraits)));
+        return ImmutableList.of(Pair.of(nodeTraits.replace(collation),
+            ImmutableList.of(left.replace(collation), 
right.replace(RelCollations.EMPTY))));
     }
 
     /** {@inheritDoc} */
@@ -259,15 +257,10 @@ public abstract class AbstractIgniteNestedLoopJoin 
extends Join implements Trait
 
         RelTraitSet left = inputTraits.get(0), right = inputTraits.get(1);
 
-        RelTraitSet outTraits, leftTraits, rightTraits;
-
         RewindabilityTrait rewindability = 
TraitUtils.rewindability(nodeTraits);
 
-        outTraits = nodeTraits.replace(rewindability);
-        leftTraits = left.replace(rewindability);
-        rightTraits = right.replace(rewindability);
-
-        return ImmutableList.of(Pair.of(outTraits, 
ImmutableList.of(leftTraits, rightTraits)));
+        return ImmutableList.of(Pair.of(nodeTraits.replace(rewindability),
+            ImmutableList.of(left.replace(rewindability), 
right.replace(rewindability))));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteAggregate.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteAggregate.java
index bced726..190789b 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteAggregate.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteAggregate.java
@@ -94,7 +94,7 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
 
         RelTraitSet in = inputTraits.get(0);
 
-        ImmutableList.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableList.builder();
+        List<Pair<RelTraitSet, List<RelTraitSet>>> res = new ArrayList<>();
 
         IgniteDistribution distribution = TraitUtils.distribution(nodeTraits);
 
@@ -103,10 +103,10 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
         switch (distrType) {
             case SINGLETON:
             case BROADCAST_DISTRIBUTED:
-                b.add(Pair.of(nodeTraits, 
ImmutableList.of(in.replace(distribution))));
+                res.add(Pair.of(nodeTraits, 
ImmutableList.of(in.replace(distribution))));
 
                 if (isSimple(this))
-                    b.add(Pair.of(nodeTraits, 
ImmutableList.of(in.replace(random())))); // Map-reduce aggregate
+                    res.add(Pair.of(nodeTraits, 
ImmutableList.of(in.replace(random())))); // Map-reduce aggregate
 
                 break;
 
@@ -115,13 +115,9 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
                     IgniteDistribution outDistr = hash(range(0, 
groupSet.cardinality()));
                     IgniteDistribution inDistr = hash(groupSet.asList());
 
-                    b.add(Pair.of(nodeTraits.replace(outDistr), 
ImmutableList.of(in.replace(inDistr))));
-
-                    break;
+                    res.add(Pair.of(nodeTraits.replace(outDistr), 
ImmutableList.of(in.replace(inDistr))));
                 }
 
-                b.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(single()))));
-
                 break;
 
             case HASH_DISTRIBUTED:
@@ -143,21 +139,22 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
                     }
 
                     if (srcKeys.size() == keys.size()) {
-                        b.add(Pair.of(nodeTraits, 
ImmutableList.of(in.replace(hash(srcKeys, distribution.function())))));
+                        res.add(Pair.of(nodeTraits, 
ImmutableList.of(in.replace(hash(srcKeys, distribution.function())))));
 
                         break;
                     }
                 }
 
-                b.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(single()))));
-
                 break;
 
             default:
                 break;
         }
 
-        return b.build();
+        if (!res.isEmpty())
+            return res;
+
+        return ImmutableList.of(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(single()))));
     }
 
     /** {@inheritDoc} */
@@ -191,7 +188,7 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
 
         RelTraitSet in = inputTraits.get(0);
 
-        ImmutableList.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableList.builder();
+        List<Pair<RelTraitSet, List<RelTraitSet>>> res = new ArrayList<>();
 
         IgniteDistribution distribution = TraitUtils.distribution(in);
 
@@ -200,7 +197,7 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
         switch (distrType) {
             case SINGLETON:
             case BROADCAST_DISTRIBUTED:
-                b.add(Pair.of(nodeTraits.replace(distribution), 
ImmutableList.of(in)));
+                res.add(Pair.of(nodeTraits.replace(distribution), 
ImmutableList.of(in)));
 
                 break;
 
@@ -214,40 +211,30 @@ public class IgniteAggregate extends Aggregate implements 
TraitsAwareIgniteRel {
 
                         IgniteDistribution outDistr = 
distribution.apply(mapping);
 
-                        if (outDistr.getType() == HASH_DISTRIBUTED) {
-                            b.add(Pair.of(nodeTraits.replace(outDistr), 
ImmutableList.of(in)));
-
-                            break;
-                        }
+                        if (outDistr.getType() == HASH_DISTRIBUTED)
+                            res.add(Pair.of(nodeTraits.replace(outDistr), 
ImmutableList.of(in)));
                     }
-
-                    b.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(random()))));
-
-                    break;
                 }
 
-                b.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(single()))));
-
                 break;
 
             case RANDOM_DISTRIBUTED:
                 // Map-reduce aggregates
                 if (isSimple(this)) {
-                    b.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(random()))));
-                    b.add(Pair.of(nodeTraits.replace(broadcast()), 
ImmutableList.of(in.replace(random()))));
-
-                    break;
+                    res.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(random()))));
+                    res.add(Pair.of(nodeTraits.replace(broadcast()), 
ImmutableList.of(in.replace(random()))));
                 }
 
-                b.add(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(single()))));
-
                 break;
 
             default:
                 break;
         }
 
-        return b.build();
+        if (!res.isEmpty())
+            return res;
+
+        return ImmutableList.of(Pair.of(nodeTraits.replace(single()), 
ImmutableList.of(in.replace(single()))));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteCorrelatedNestedLoopJoin.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteCorrelatedNestedLoopJoin.java
index 7769a58..1e78883 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteCorrelatedNestedLoopJoin.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteCorrelatedNestedLoopJoin.java
@@ -19,7 +19,6 @@ package 
org.apache.ignite.internal.processors.query.calcite.rel;
 
 import java.util.List;
 import java.util.Set;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import org.apache.calcite.plan.RelOptCluster;
@@ -103,27 +102,10 @@ public class IgniteCorrelatedNestedLoopJoin extends 
AbstractIgniteNestedLoopJoin
 
         RelTraitSet left = inputTraits.get(0), right = inputTraits.get(1);
 
-        ImmutableList.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableList.builder();
-
-        RewindabilityTrait leftRewindability = TraitUtils.rewindability(left);
-
-        RelTraitSet outTraits, leftTraits, rightTraits;
-
-        outTraits = nodeTraits.replace(RewindabilityTrait.ONE_WAY);
-        leftTraits = left.replace(RewindabilityTrait.ONE_WAY);
-        rightTraits = right.replace(RewindabilityTrait.REWINDABLE);
-
-        b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, rightTraits)));
+        RewindabilityTrait rewindability = TraitUtils.rewindability(left);
 
-        if (leftRewindability.rewindable()) {
-            outTraits = nodeTraits.replace(RewindabilityTrait.REWINDABLE);
-            leftTraits = left.replace(RewindabilityTrait.REWINDABLE);
-            rightTraits = right.replace(RewindabilityTrait.REWINDABLE);
-
-            b.add(Pair.of(outTraits, ImmutableList.of(leftTraits, 
rightTraits)));
-        }
-
-        return b.build();
+        return ImmutableList.of(Pair.of(nodeTraits.replace(rewindability),
+            ImmutableList.of(left, 
right.replace(RewindabilityTrait.REWINDABLE))));
     }
 
     @Override public List<Pair<RelTraitSet, List<RelTraitSet>>> 
passThroughCollation(RelTraitSet nodeTraits, List<RelTraitSet> inputTraits) {
@@ -143,15 +125,10 @@ public class IgniteCorrelatedNestedLoopJoin extends 
AbstractIgniteNestedLoopJoin
 
         RelTraitSet left = inputTraits.get(0), right = inputTraits.get(1);
 
-        RelTraitSet outTraits, leftTraits, rightTraits;
-
         RewindabilityTrait rewindability = 
TraitUtils.rewindability(nodeTraits);
 
-        outTraits = nodeTraits.replace(rewindability);
-        leftTraits = left.replace(rewindability);
-        rightTraits = right.replace(RewindabilityTrait.REWINDABLE);
-
-        return ImmutableList.of(Pair.of(outTraits, 
ImmutableList.of(leftTraits, rightTraits)));
+        return ImmutableList.of(Pair.of(nodeTraits.replace(rewindability),
+            ImmutableList.of(left.replace(rewindability), 
right.replace(RewindabilityTrait.REWINDABLE))));
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteExchange.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteExchange.java
index de080cd..636b361 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteExchange.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteExchange.java
@@ -17,9 +17,6 @@
 
 package org.apache.ignite.internal.processors.query.calcite.rel;
 
-import java.util.List;
-
-import com.google.common.collect.ImmutableList;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptPlanner;
@@ -29,14 +26,9 @@ import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Exchange;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.util.Pair;
 import 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
-import 
org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTrait;
-import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils;
 
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.any;
 import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits;
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
 
 /**
  * Relational expression that imposes a particular distribution on its input
@@ -82,37 +74,6 @@ public class IgniteExchange extends Exchange implements 
IgniteRel {
     }
 
     /** {@inheritDoc} */
-    @Override public Pair<RelTraitSet, List<RelTraitSet>> 
passThroughTraits(RelTraitSet required) {
-        required = fixTraits(required);
-
-        IgniteDistribution distribution = TraitUtils.distribution(required);
-
-        if (!distribution().satisfies(distribution))
-            return null;
-
-        RelTraitSet outTraits = required.replace(distribution())
-            .replace(RewindabilityTrait.ONE_WAY);
-        RelTraitSet inTraits = required.replace(any())
-            .replace(RewindabilityTrait.ONE_WAY);
-
-        return Pair.of(outTraits, ImmutableList.of(inTraits));
-    }
-
-    /** {@inheritDoc} */
-    @Override public Pair<RelTraitSet, List<RelTraitSet>> 
deriveTraits(RelTraitSet childTraits, int childId) {
-        assert childId == 0;
-
-        childTraits = fixTraits(childTraits);
-
-        RelTraitSet outTraits = childTraits.replace(distribution())
-            .replace(RewindabilityTrait.ONE_WAY);
-        RelTraitSet inTraits = childTraits.replace(any())
-            .replace(RewindabilityTrait.ONE_WAY);
-
-        return Pair.of(outTraits, ImmutableList.of(inTraits));
-    }
-
-    /** {@inheritDoc} */
     @Override public boolean isEnforcer() {
         return true;
     }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteFilter.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteFilter.java
index d88282e..e0b2887 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteFilter.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteFilter.java
@@ -18,7 +18,6 @@
 package org.apache.ignite.internal.processors.query.calcite.rel;
 
 import java.util.List;
-
 import com.google.common.collect.ImmutableList;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptCost;
@@ -33,7 +32,6 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.Pair;
 
 import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits;
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
 
 /**
  * Relational expression that iterates over its input
@@ -73,7 +71,8 @@ public class IgniteFilter extends Filter implements IgniteRel 
{
 
     /** {@inheritDoc} */
     @Override public Pair<RelTraitSet, List<RelTraitSet>> 
passThroughTraits(RelTraitSet required) {
-        required = fixTraits(required);
+        if (required.getConvention() != IgniteConvention.INSTANCE)
+            return null;
 
         return Pair.of(required, ImmutableList.of(required));
     }
@@ -82,7 +81,8 @@ public class IgniteFilter extends Filter implements IgniteRel 
{
     @Override public Pair<RelTraitSet, List<RelTraitSet>> 
deriveTraits(RelTraitSet childTraits, int childId) {
         assert childId == 0;
 
-        childTraits = fixTraits(childTraits);
+        if (childTraits.getConvention() != IgniteConvention.INSTANCE)
+            return null;
 
         return Pair.of(childTraits, ImmutableList.of(childTraits));
     }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
index 45d897a..8cd85f8 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteProject.java
@@ -113,8 +113,6 @@ public class IgniteProject extends Project implements 
TraitsAwareIgniteRel {
         ImmutableIntList keys = distribution.getKeys();
         List<Integer> srcKeys = new ArrayList<>(keys.size());
 
-        distribution = distribution.apply(mapping);
-
         for (int key : keys) {
             int src = mapping.getSourceOpt(key);
 
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteSort.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteSort.java
index fa941ad..a1faaf2 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteSort.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteSort.java
@@ -17,7 +17,6 @@
 package org.apache.ignite.internal.processors.query.calcite.rel;
 
 import java.util.List;
-
 import com.google.common.collect.ImmutableList;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
@@ -31,7 +30,6 @@ import org.apache.calcite.util.Pair;
 import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils;
 
 import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits;
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
 
 /**
  * Ignite sort operator.
@@ -77,23 +75,27 @@ public class IgniteSort extends Sort implements IgniteRel {
     }
 
     /** {@inheritDoc} */
+    @Override public RelCollation collation() {
+        return collation;
+    }
+
+    /** {@inheritDoc} */
     @Override public Pair<RelTraitSet, List<RelTraitSet>> 
passThroughTraits(RelTraitSet required) {
-        required = fixTraits(required);
+        if (isEnforcer() || required.getConvention() != 
IgniteConvention.INSTANCE)
+            return null;
 
         RelCollation collation = TraitUtils.collation(required);
 
-        if (!collation().satisfies(collation))
-            return null;
-
-        return Pair.of(required.replace(collation()), 
ImmutableList.of(required.replace(RelCollations.EMPTY)));
+        return Pair.of(required.replace(collation), 
ImmutableList.of(required.replace(RelCollations.EMPTY)));
     }
 
     /** {@inheritDoc} */
     @Override public Pair<RelTraitSet, List<RelTraitSet>> 
deriveTraits(RelTraitSet childTraits, int childId) {
         assert childId == 0;
 
-        childTraits = fixTraits(childTraits);
+        if (isEnforcer() || childTraits.getConvention() != 
IgniteConvention.INSTANCE)
+            return null;
 
-        return Pair.of(childTraits.replace(collation()), 
ImmutableList.of(childTraits.replace(RelCollations.EMPTY)));
+        return Pair.of(childTraits.replace(collation()), 
ImmutableList.of(childTraits));
     }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableModify.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableModify.java
index 68e3b4d..2242164 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableModify.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTableModify.java
@@ -18,22 +18,15 @@
 package org.apache.ignite.internal.processors.query.calcite.rel;
 
 import java.util.List;
-import com.google.common.collect.ImmutableList;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.TableModify;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.util.Pair;
-import 
org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTrait;
 import org.apache.ignite.internal.processors.query.calcite.util.Commons;
 
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions.single;
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
-
 /**
  *
  */
@@ -94,36 +87,6 @@ public class IgniteTableModify extends TableModify 
implements IgniteRel {
     }
 
     /** {@inheritDoc} */
-    @Override public Pair<RelTraitSet, List<RelTraitSet>> 
passThroughTraits(RelTraitSet required) {
-        required = fixTraits(required);
-
-        RelTraitSet outTraits = required.replace(single())
-            .replace(RewindabilityTrait.ONE_WAY)
-            .replace(RelCollations.EMPTY);
-        RelTraitSet inTraits = required.replace(single())
-            .replace(RewindabilityTrait.ONE_WAY)
-            .replace(RelCollations.EMPTY);
-
-        return Pair.of(outTraits, ImmutableList.of(inTraits));
-    }
-
-    /** {@inheritDoc} */
-    @Override public Pair<RelTraitSet, List<RelTraitSet>> 
deriveTraits(RelTraitSet childTraits, int childId) {
-        assert childId == 0;
-
-        childTraits = fixTraits(childTraits);
-
-        RelTraitSet outTraits = childTraits.replace(single())
-            .replace(RelCollations.EMPTY)
-            .replace(RewindabilityTrait.ONE_WAY);
-        RelTraitSet inTraits = childTraits.replace(single())
-            .replace(RelCollations.EMPTY)
-            .replace(RewindabilityTrait.ONE_WAY);
-
-        return Pair.of(outTraits, ImmutableList.of(inTraits));
-    }
-
-    /** {@inheritDoc} */
     @Override public <T> T accept(IgniteRelVisitor<T> visitor) {
         return visitor.visit(this);
     }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTrimExchange.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTrimExchange.java
index 47a9867..0fef9d8 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTrimExchange.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteTrimExchange.java
@@ -17,9 +17,6 @@
 
 package org.apache.ignite.internal.processors.query.calcite.rel;
 
-import java.util.List;
-
-import com.google.common.collect.ImmutableList;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptPlanner;
@@ -29,16 +26,12 @@ import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Exchange;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.util.Pair;
 import 
org.apache.ignite.internal.processors.query.calcite.trait.DistributionTraitDef;
 import 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
-import 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
-import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils;
 
 import static 
org.apache.calcite.rel.RelDistribution.Type.BROADCAST_DISTRIBUTED;
 import static org.apache.calcite.rel.RelDistribution.Type.HASH_DISTRIBUTED;
 import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.changeTraits;
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
 
 /**
  *
@@ -77,27 +70,6 @@ public class IgniteTrimExchange extends Exchange implements 
IgniteRel {
     }
 
     /** {@inheritDoc} */
-    @Override public Pair<RelTraitSet, List<RelTraitSet>> 
passThroughTraits(RelTraitSet required) {
-        required = fixTraits(required);
-
-        IgniteDistribution distribution = TraitUtils.distribution(required);
-
-        if (!distribution().satisfies(distribution))
-            return null;
-
-        return Pair.of(required.replace(distribution()), 
ImmutableList.of(required.replace(IgniteDistributions.broadcast())));
-    }
-
-    /** {@inheritDoc} */
-    @Override public Pair<RelTraitSet, List<RelTraitSet>> 
deriveTraits(RelTraitSet childTraits, int childId) {
-        assert childId == 0;
-
-        childTraits = fixTraits(childTraits);
-
-        return Pair.of(childTraits.replace(distribution()), 
ImmutableList.of(childTraits.replace(IgniteDistributions.broadcast())));
-    }
-
-    /** {@inheritDoc} */
     @Override public RelOptCost computeSelfCost(RelOptPlanner planner, 
RelMetadataQuery mq) {
         double rowCount = mq.getRowCount(this);
         return planner.getCostFactory().makeCost(rowCount, rowCount, 0);
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
index 93f4c04..1948c52 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/ProjectableFilterableTableScan.java
@@ -1,3 +1,20 @@
+/*
+ * 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.rel;
 
 import java.util.ArrayList;
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/CorrelatedNestedLoopJoinConverterRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/CorrelatedNestedLoopJoinRule.java
similarity index 68%
rename from 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/CorrelatedNestedLoopJoinConverterRule.java
rename to 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/CorrelatedNestedLoopJoinRule.java
index 1a5cdf0..7e39ad7 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/CorrelatedNestedLoopJoinConverterRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/CorrelatedNestedLoopJoinRule.java
@@ -21,57 +21,51 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-
 import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptPlanner;
 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.PhysicalNode;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.CorrelationId;
+import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalJoin;
-import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCorrelVariable;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.ImmutableBeans;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteConvention;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteCorrelatedNestedLoopJoin;
 import 
org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTrait;
 
 /** */
-public class CorrelatedNestedLoopJoinConverterRule extends 
AbstractIgniteConverterRule<LogicalJoin> {
+public class CorrelatedNestedLoopJoinRule extends 
RelRule<CorrelatedNestedLoopJoinRule.Config> {
     /** */
-    public static final RelOptRule INSTANCE = new 
CorrelatedNestedLoopJoinConverterRule(1);
+    public static final RelOptRule INSTANCE = Config.DEFAULT.toRule();
 
     /** */
-    public static final RelOptRule INSTANCE_BATCHED = new 
CorrelatedNestedLoopJoinConverterRule(100);
+    public static final RelOptRule INSTANCE_BATCHED = 
Config.DEFAULT.withBatchSize(100).toRule();
 
     /** */
     private final int batchSize;
 
     /** */
-    public CorrelatedNestedLoopJoinConverterRule(int batchSize) {
-        super(LogicalJoin.class, "CorrelatedNestedLoopConverter");
+    public CorrelatedNestedLoopJoinRule(Config cfg) {
+        super(cfg);
 
+        int batchSize = cfg.batchSize();
         assert batchSize >= 0;
 
         this.batchSize = batchSize;
     }
 
-    /** {@inheritDoc} */
-    @Override public boolean matches(RelOptRuleCall call) {
-        LogicalJoin join = call.rel(0);
-        JoinRelType joinType = join.getJoinType();
-        return joinType == JoinRelType.INNER; // TODO LEFT, SEMI, ANTI
-    }
-
-    /** {@inheritDoc} */
-    @Override protected PhysicalNode convert(RelOptPlanner planner, 
RelMetadataQuery mq, LogicalJoin rel) {
+    @Override public void onMatch(RelOptRuleCall call) {
+        Join rel = call.rel(0);
         final int leftFieldCount = rel.getLeft().getRowType().getFieldCount();
         final RelOptCluster cluster = rel.getCluster();
         final RexBuilder rexBuilder = cluster.getRexBuilder();
@@ -125,6 +119,42 @@ public class CorrelatedNestedLoopJoinConverterRule extends 
AbstractIgniteConvert
         RelNode left = convert(rel.getLeft(), leftInTraits);
         right = convert(right, rightInTraits);
 
-        return new IgniteCorrelatedNestedLoopJoin(cluster, outTraits, left, 
right, rel.getCondition(), correlationIds, joinType);
+        call.transformTo(new IgniteCorrelatedNestedLoopJoin(cluster, 
outTraits, left, right, rel.getCondition(), correlationIds, joinType));
+    }
+
+    /** */
+    @SuppressWarnings("ClassNameSameAsAncestorName")
+    public interface Config extends RelRule.Config {
+        /** */
+        Config DEFAULT = RelRule.Config.EMPTY
+            .withRelBuilderFactory(RelFactories.LOGICAL_BUILDER)
+            .withDescription("CorrelatedNestedLoopJoinRule")
+            .as(Config.class)
+            .withOperandFor(LogicalJoin.class)
+            .withBatchSize(1);
+
+        /** Description of the rule instance. */
+        @ImmutableBeans.Property
+        int batchSize();
+
+        /** Sets {@link #description()}. */
+        Config withBatchSize(int batchSize);
+
+        /** Defines an operand tree for the given classes. */
+        default Config withOperandFor(Class<? extends Join> joinClass) {
+            return withOperandSupplier(o -> o.operand(joinClass)
+                .predicate(CorrelatedNestedLoopJoinRule::preMatch).anyInputs())
+                .as(Config.class);
+        }
+
+        /** {@inheritDoc} */
+        @Override default CorrelatedNestedLoopJoinRule toRule() {
+            return new CorrelatedNestedLoopJoinRule(this);
+        }
+    }
+
+    /** */
+    private static boolean preMatch(Join join) {
+        return join.getJoinType() == JoinRelType.INNER; // TODO LEFT, SEMI, 
ANTI
     }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/TableModifyConverterRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/TableModifyConverterRule.java
index 581e715..5ef0d55 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/TableModifyConverterRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/TableModifyConverterRule.java
@@ -22,11 +22,14 @@ import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.PhysicalNode;
+import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteConvention;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableModify;
+import 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
+import 
org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTrait;
 
 /**
  *
@@ -45,7 +48,10 @@ public class TableModifyConverterRule extends 
AbstractIgniteConverterRule<Logica
     /** {@inheritDoc} */
     @Override protected PhysicalNode convert(RelOptPlanner planner, 
RelMetadataQuery mq, LogicalTableModify rel) {
         RelOptCluster cluster = rel.getCluster();
-        RelTraitSet traits = cluster.traitSetOf(IgniteConvention.INSTANCE);
+        RelTraitSet traits = cluster.traitSetOf(IgniteConvention.INSTANCE)
+            .replace(IgniteDistributions.single())
+            .replace(RewindabilityTrait.ONE_WAY)
+            .replace(RelCollations.EMPTY);
         RelNode input = convert(rel.getInput(), traits);
 
         return new IgniteTableModify(cluster, traits, rel.getTable(), input,
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/ExposeIndexRule.java
similarity index 99%
rename from 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
rename to 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/ExposeIndexRule.java
index a5071ca..f4ad024 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ExposeIndexRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/ExposeIndexRule.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.query.calcite.rule;
+package org.apache.ignite.internal.processors.query.calcite.rule.logical;
 
 import java.util.HashMap;
 import java.util.List;
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterJoinRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterJoinRule.java
deleted file mode 100644
index 23e3440..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterJoinRule.java
+++ /dev/null
@@ -1,80 +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.rule.logical;
-
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.core.Filter;
-import org.apache.calcite.rel.core.Join;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalJoin;
-import org.apache.calcite.rel.rules.JoinPushExpressionsRule;
-
-public class FilterJoinRule {
-    /** Rule that pushes predicates from a Filter into the Join below them. */
-    public static final FilterIntoJoinRule FILTER_ON_JOIN = new 
FilterIntoJoinRule();
-
-    /** Rule that pushes predicates in a Join into the inputs to the join. */
-    public static final JoinConditionPushRule JOIN = new 
JoinConditionPushRule();
-
-    /** Rule that pushes down expressions in "equal" join condition. */
-    public static final JoinPushExpressionsRule PUSH_JOIN_CONDITION =
-        new JoinPushExpressionsRule(LogicalJoin.class, 
RelFactories.LOGICAL_BUILDER);
-
-    /**
-     * Rule that tries to push filter expressions into a join condition and 
into the inputs of the join.
-     */
-    public static class FilterIntoJoinRule extends 
org.apache.calcite.rel.rules.FilterJoinRule {
-        /**
-         * Creates a converter.
-         */
-        public FilterIntoJoinRule() {
-            super(
-                operand(LogicalFilter.class,
-                    operand(LogicalJoin.class, RelOptRule.any())),
-                "FilterJoinRule:filter", true, RelFactories.LOGICAL_BUILDER,
-                org.apache.calcite.rel.rules.FilterJoinRule.TRUE_PREDICATE);
-        }
-
-        /** {@inheritDoc} */
-        @Override public void onMatch(RelOptRuleCall call) {
-            Filter filter = call.rel(0);
-            Join join = call.rel(1);
-            perform(call, filter, join);
-        }
-    }
-
-    /** Rule that pushes parts of the join condition to its inputs. */
-    public static class JoinConditionPushRule extends 
org.apache.calcite.rel.rules.FilterJoinRule {
-        /**
-         * Creates a converter.
-         */
-        public JoinConditionPushRule() {
-            super(RelOptRule.operand(LogicalJoin.class, RelOptRule.any()),
-                "FilterJoinRule:no-filter", true, RelFactories.LOGICAL_BUILDER,
-                org.apache.calcite.rel.rules.FilterJoinRule.TRUE_PREDICATE);
-        }
-
-        /** {@inheritDoc} */
-        @Override public void onMatch(RelOptRuleCall call) {
-            Join join = call.rel(0);
-            perform(call, null, join);
-        }
-    }
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/FilterScanMergeRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java
similarity index 84%
rename from 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/FilterScanMergeRule.java
rename to 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java
index ddab945..c2a6149 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/FilterScanMergeRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/FilterScanMergeRule.java
@@ -14,12 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.internal.processors.query.calcite.rule;
+package org.apache.ignite.internal.processors.query.calcite.rule.logical;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
@@ -33,7 +31,6 @@ import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexShuttle;
-import org.apache.calcite.rex.RexSimplify;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.util.ControlFlowException;
 import org.apache.calcite.util.mapping.Mappings;
@@ -44,7 +41,6 @@ import 
org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
 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.RexUtils;
-import org.apache.ignite.internal.util.typedef.F;
 
 /**
  * Rule that pushes filter into the scan. This might be useful for index range 
scans.
@@ -84,6 +80,12 @@ public abstract class FilterScanMergeRule<T extends 
ProjectableFilterableTableSc
     }
 
     /** {@inheritDoc} */
+    @Override public boolean matches(RelOptRuleCall call) {
+        T rel = call.rel(1);
+        return rel.condition() == null;
+    }
+
+    /** {@inheritDoc} */
     @Override public void onMatch(RelOptRuleCall call) {
         LogicalFilter filter = call.rel(0);
         T scan = call.rel(1);
@@ -131,32 +133,12 @@ public abstract class FilterScanMergeRule<T extends 
ProjectableFilterableTableSc
             remaining = RexUtil.composeConjunction(builder, remaining0, true);
         }
 
-        RexSimplify simplifier = RexUtils.simplifier(cluster);
-
-        // Let's remove from the condition common with the scan filter parts.
-        condition = simplifier
-            .withPredicates(mq.getPulledUpPredicates(scan))
-            .simplifyUnknownAsFalse(condition);
-
         // We need to replace RexInputRef with RexLocalRef because TableScan 
doesn't have inputs.
-        condition = RexUtils.replaceInputRefs(condition);
-
-        // Combine the condition with the scan filter.
-        condition = RexUtil.composeConjunction(builder, F.asList(condition, 
scan.condition()));
-
-        // Final simplification. We need several phases because simplifier 
sometimes
-        // (see RexSimplify.simplifyGenericNode) leaves UNKNOWN nodes that can 
be
-        // eliminated on next simplify attempt. We limit attempts count not to 
break
-        // planning performance on complex condition.
-        Set<RexNode> nodes = new HashSet<>();
-        while (nodes.add(condition) && nodes.size() < 3)
-            condition = simplifier.simplifyUnknownAsFalse(condition);
+        // TODO SEARCH support
+        condition = RexUtils.replaceInputRefs(RexUtil.expandSearch(builder, 
null, condition));
 
         RelNode res = createNode(cluster, scan, condition);
 
-        if (res == null)
-            return;
-
         if (remaining != null) {
             res = relBuilderFactory.create(cluster, null)
                 .push(res)
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalFilterMergeRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalFilterMergeRule.java
deleted file mode 100644
index c7eaf6a..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalFilterMergeRule.java
+++ /dev/null
@@ -1,46 +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.rule.logical;
-
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.rules.FilterMergeRule;
-
-/**
- *
- */
-public class LogicalFilterMergeRule extends RelOptRule {
-    public static final RelOptRule INSTANCE = new LogicalFilterMergeRule();
-
-    /**
-     *
-     */
-    public LogicalFilterMergeRule() {
-        super(
-            operand(LogicalFilter.class,
-                operand(LogicalFilter.class, any())),
-            RelFactories.LOGICAL_BUILDER, null);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onMatch(RelOptRuleCall call) {
-        FilterMergeRule.INSTANCE.onMatch(call);
-    }
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalFilterProjectTransposeRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalFilterProjectTransposeRule.java
deleted file mode 100644
index 9415185..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalFilterProjectTransposeRule.java
+++ /dev/null
@@ -1,38 +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.rule.logical;
-
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.rel.logical.LogicalFilter;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
-
-/**
- *
- */
-public class LogicalFilterProjectTransposeRule extends 
FilterProjectTransposeRule {
-    /** */
-    public static final RelOptRule INSTANCE = new 
LogicalFilterProjectTransposeRule();
-
-    /** */
-    public LogicalFilterProjectTransposeRule() {
-        super(LogicalFilter.class, filter -> true, LogicalProject.class, 
project -> true, true, true,
-            RelFactories.LOGICAL_BUILDER);
-    }
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalOrToUnionRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalOrToUnionRule.java
index 40b2bb9..fd62804 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalOrToUnionRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalOrToUnionRule.java
@@ -18,13 +18,14 @@
 package org.apache.ignite.internal.processors.query.calcite.rule.logical;
 
 import java.util.List;
-
 import com.google.common.collect.ImmutableMap;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelRule;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rex.RexNode;
@@ -35,20 +36,17 @@ import org.apache.calcite.tools.RelBuilder;
 /**
  * Converts OR to UNION ALL.
  */
-public class LogicalOrToUnionRule extends RelOptRule {
+public class LogicalOrToUnionRule extends RelRule<LogicalOrToUnionRule.Config> 
{
     /** Instance. */
-    public static final RelOptRule INSTANCE = new 
LogicalOrToUnionRule(LogicalFilter.class, "LogicalOrToUnionRule");
+    public static final RelOptRule INSTANCE = Config.DEFAULT.toRule();
 
     /**
      * Constructor.
      *
-     * @param clazz Class of relational expression to match.
-     * @param desc  Description, or null to guess description
+     * @param config Rule configuration.
      */
-    private LogicalOrToUnionRule(Class<LogicalFilter> clazz, String desc) {
-        super(
-            operand(clazz, any()),
-            RelFactories.LOGICAL_BUILDER, desc);
+    private LogicalOrToUnionRule(Config config) {
+        super(config);
     }
 
     /** {@inheritDoc} */
@@ -63,7 +61,7 @@ public class LogicalOrToUnionRule extends RelOptRule {
 
         List<RexNode> operands = RelOptUtil.disjunctions(dnf);
 
-        if (operands.size() != 2)
+        if (operands.size() != 2 || 
RexUtil.find(SqlKind.IS_NULL).anyContain(operands))
             return;
 
         RelNode input = rel.getInput(0);
@@ -94,4 +92,26 @@ public class LogicalOrToUnionRule extends RelOptRule {
             .union(true)
             .build();
     }
+
+    /** */
+    @SuppressWarnings("ClassNameSameAsAncestorName")
+    public interface Config extends RelRule.Config {
+        /** */
+        Config DEFAULT = RelRule.Config.EMPTY
+            .withRelBuilderFactory(RelFactories.LOGICAL_BUILDER)
+            .withDescription("LogicalOrToUnionRule")
+            .as(Config.class)
+            .withOperandFor(LogicalFilter.class);
+
+        /** Defines an operand tree for the given classes. */
+        default Config withOperandFor(Class<? extends Filter> filterClass) {
+            return withOperandSupplier(o -> o.operand(filterClass).anyInputs())
+                .as(Config.class);
+        }
+
+        /** {@inheritDoc} */
+        @Override default LogicalOrToUnionRule toRule() {
+            return new LogicalOrToUnionRule(this);
+        }
+    }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectMergeRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectMergeRule.java
deleted file mode 100644
index be69085..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectMergeRule.java
+++ /dev/null
@@ -1,47 +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.rule.logical;
-
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.rules.ProjectMergeRule;
-import org.apache.calcite.rel.rules.TransformationRule;
-
-/**
- * LogicalProjectMergeRule merges a {@link LogicalProject} into another {@link 
LogicalProject},
- * provided the projects aren't projecting identical sets of input references.
- */
-public class LogicalProjectMergeRule extends RelOptRule implements 
TransformationRule {
-    /** */
-    public static final RelOptRule INSTANCE = new LogicalProjectMergeRule();
-
-    /** */
-    private LogicalProjectMergeRule() {
-        super(
-            operand(LogicalProject.class,
-                operand(LogicalProject.class, any())),
-            RelFactories.LOGICAL_BUILDER, null);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onMatch(RelOptRuleCall call) {
-        ProjectMergeRule.INSTANCE.onMatch(call);
-    }
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectRemoveRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectRemoveRule.java
deleted file mode 100644
index aacb848..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/LogicalProjectRemoveRule.java
+++ /dev/null
@@ -1,49 +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.rule.logical;
-
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.core.RelFactories;
-import org.apache.calcite.rel.logical.LogicalProject;
-import org.apache.calcite.rel.rules.ProjectRemoveRule;
-import org.apache.calcite.rel.rules.SubstitutionRule;
-
-/**
- * Rule that, given a {@link LogicalProject} node that
-*  merely returns its input, converts the node into its child.
-*/
-public class LogicalProjectRemoveRule extends RelOptRule implements 
SubstitutionRule {
-    /** */
-    public static final RelOptRule INSTANCE = new LogicalProjectRemoveRule();
-
-    /** Constructor. */
-    public LogicalProjectRemoveRule() {
-        super(operandJ(LogicalProject.class, null, 
ProjectRemoveRule::isTrivial, any()), RelFactories.LOGICAL_BUILDER, null);
-    }
-
-    /** {@inheritDoc} */
-    @Override public void onMatch(RelOptRuleCall call) {
-        ProjectRemoveRule.INSTANCE.onMatch(call);
-    }
-
-    /** {@inheritDoc} */
-    @Override public boolean autoPruneOld() {
-        return true;
-    }
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ProjectScanMergeRule.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/ProjectScanMergeRule.java
similarity index 99%
rename from 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ProjectScanMergeRule.java
rename to 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/ProjectScanMergeRule.java
index ac8ca97..8beda54 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/ProjectScanMergeRule.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rule/logical/ProjectScanMergeRule.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.processors.query.calcite.rule;
+package org.apache.ignite.internal.processors.query.calcite.rule.logical;
 
 import java.util.List;
 import org.apache.calcite.plan.RelOptCluster;
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/RelRegistrar.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/RelRegistrar.java
new file mode 100644
index 0000000..8af9983
--- /dev/null
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/RelRegistrar.java
@@ -0,0 +1,94 @@
+/*
+ * 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.trait;
+
+import java.util.List;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.AbstractRelNode;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.util.Litmus;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
+import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteRelVisitor;
+
+/** */
+public class RelRegistrar extends AbstractRelNode implements IgniteRel {
+    /** */
+    private final List<RelNode> rels;
+
+    /** */
+    private final RelNode orig;
+
+    /** */
+    public RelRegistrar(RelOptCluster cluster, RelTraitSet traitSet, RelNode 
orig, List<RelNode> rels) {
+        super(cluster, traitSet);
+
+        this.rels = rels;
+        this.orig = orig;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected RelDataType deriveRowType() {
+        return orig.getRowType();
+    }
+
+    /** {@inheritDoc} */
+    @Override public RelWriter explainTerms(RelWriter pw) {
+        return pw.item("orig", orig).item("requiredTraits", getTraitSet());
+    }
+
+    /** {@inheritDoc} */
+    @Override public RelNode onRegister(RelOptPlanner planner) {
+        RelNode r = null;
+        for (RelNode rel : rels) {
+            r = planner.ensureRegistered(rel, orig);
+            assert r == rel || RelOptUtil.equal("rowtype of rel before 
registration",
+                rel.getRowType(),
+                "rowtype of rel after registration",
+                r.getRowType(),
+                Litmus.THROW);
+            if (!r.getTraitSet().satisfies(getTraitSet()))
+                r = RelOptRule.convert(r, getTraitSet());
+        }
+        assert r != null && r.isValid(Litmus.THROW, null);
+        return r;
+    }
+
+    /** {@inheritDoc} */
+    @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        assert inputs.isEmpty();
+        return new RelRegistrar(getCluster(), traitSet, orig, rels);
+    }
+
+    /** {@inheritDoc} */
+    @Override public RelOptCost computeSelfCost(RelOptPlanner planner, 
RelMetadataQuery mq) {
+        return planner.getCostFactory().makeInfiniteCost();
+    }
+
+    /** {@inheritDoc} */
+    @Override public <T> T accept(IgniteRelVisitor<T> visitor) {
+        throw new AssertionError();
+    }
+}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
index 7c1e394..04fa1ba 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitUtils.java
@@ -17,9 +17,10 @@
 
 package org.apache.ignite.internal.processors.query.calcite.trait;
 
-import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import org.apache.calcite.plan.RelOptCluster;
@@ -39,8 +40,9 @@ import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.ControlFlowException;
 import org.apache.calcite.util.ImmutableBitSet;
-import org.apache.calcite.util.Util;
+import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.mapping.Mappings;
 import 
org.apache.ignite.internal.processors.query.calcite.rel.IgniteConvention;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteExchange;
@@ -65,8 +67,9 @@ public class TraitUtils {
     @Nullable public static RelNode enforce(RelNode rel, RelTraitSet toTraits) 
{
         RelOptPlanner planner = rel.getCluster().getPlanner();
         RelTraitSet fromTraits = rel.getTraitSet();
+        int size = Math.min(fromTraits.size(), toTraits.size());
         if (!fromTraits.satisfies(toTraits)) {
-            for (int i = 0; (rel != null) && (i < toTraits.size()); i++) {
+            for (int i = 0; rel != null && i < size; i++) {
                 RelTrait fromTrait = rel.getTraitSet().getTrait(i);
                 RelTrait toTrait = toTraits.getTrait(i);
 
@@ -76,9 +79,6 @@ public class TraitUtils {
                 RelNode old = rel;
                 rel = convertTrait(planner, fromTrait, toTrait, old);
 
-                if (rel != null)
-                    rel = planner.register(rel, old);
-
                 assert rel == null || 
rel.getTraitSet().getTrait(i).satisfies(toTrait);
             }
 
@@ -89,7 +89,7 @@ public class TraitUtils {
     }
 
     /** */
-    @SuppressWarnings({"unchecked", "rawtypes"})
+    @SuppressWarnings({"rawtypes"})
     @Nullable private static RelNode convertTrait(RelOptPlanner planner, 
RelTrait fromTrait, RelTrait toTrait, RelNode rel) {
         assert fromTrait.getTraitDef() == toTrait.getTraitDef();
 
@@ -101,52 +101,62 @@ public class TraitUtils {
             return convertDistribution(planner, (IgniteDistribution)toTrait, 
rel);
         else if (converter == RewindabilityTraitDef.INSTANCE)
             return convertRewindability(planner, (RewindabilityTrait)toTrait, 
rel);
-        else if (converter.canConvert(planner, fromTrait, toTrait))
-            return converter.convert(planner, rel, toTrait, true);
-
-        return null;
+        else
+            return convertOther(planner, converter, toTrait, rel);
     }
 
     /** */
     @Nullable public static RelNode convertCollation(RelOptPlanner planner,
         RelCollation toTrait, RelNode rel) {
-        if (toTrait.getFieldCollations().isEmpty())
-            return null;
+        RelCollation fromTrait = collation(rel);
 
-        RelNode result = new IgniteSort(rel.getCluster(), 
rel.getTraitSet().replace(toTrait),
-            rel, toTrait, null, null);
+        if (fromTrait.satisfies(toTrait))
+            return rel;
 
-        return planner.register(result, rel);
+        RelTraitSet traits = rel.getTraitSet().replace(toTrait);
+
+        return new IgniteSort(rel.getCluster(), traits, rel, toTrait, null, 
null);
     }
 
     /** */
     @Nullable public static RelNode convertDistribution(RelOptPlanner planner,
         IgniteDistribution toTrait, RelNode rel) {
+        IgniteDistribution fromTrait = distribution(rel);
 
-        if (toTrait.getType() == RelDistribution.Type.ANY)
-            return null;
+        if (fromTrait.satisfies(toTrait))
+            return rel;
 
-        RelNode result;
-        IgniteDistribution fromTrait = distribution(rel.getTraitSet());
         RelTraitSet traits = rel.getTraitSet().replace(toTrait);
-
         if (fromTrait.getType() == BROADCAST_DISTRIBUTED && toTrait.getType() 
== HASH_DISTRIBUTED)
-            result = new IgniteTrimExchange(rel.getCluster(), traits, rel, 
toTrait);
-        else {
-            traits = traits.replace(RewindabilityTrait.ONE_WAY);
-            result = new IgniteExchange(rel.getCluster(), traits, 
RelOptRule.convert(rel, any()), toTrait);
-        }
-
-        return planner.register(result, rel);
+            return new IgniteTrimExchange(rel.getCluster(), traits, rel, 
toTrait);
+        else
+            return new IgniteExchange(rel.getCluster(),
+                traits.replace(RewindabilityTrait.ONE_WAY), 
RelOptRule.convert(rel, any()), toTrait);
     }
 
     /** */
     @Nullable public static RelNode convertRewindability(RelOptPlanner planner,
         RewindabilityTrait toTrait, RelNode rel) {
-        if (toTrait.rewindable() && 
!rewindability(rel.getTraitSet()).rewindable())
-            return null; // TODO IndexSpoon
+        RewindabilityTrait fromTrait = rewindability(rel);
 
-        return rel;
+        if (fromTrait.satisfies(toTrait))
+            return rel;
+
+        return null; // TODO IndexSpoon
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    @Nullable private static RelNode convertOther(RelOptPlanner planner, 
RelTraitDef converter,
+        RelTrait toTrait, RelNode rel) {
+        RelTrait fromTrait = rel.getTraitSet().getTrait(converter);
+
+        if (fromTrait.satisfies(toTrait))
+            return rel;
+
+        if (!converter.canConvert(planner, fromTrait, toTrait))
+            return null;
+
+        return converter.convert(planner, rel, toTrait, true);
     }
 
     /** Change distribution and Convention. */
@@ -298,44 +308,6 @@ public class TraitUtils {
         };
     }
 
-    /**
-     * Replaces input traits into a set of source traits combinations.
-     */
-    public static Collection<List<RelTraitSet>> 
inputTraits(List<List<RelTraitSet>> inTraits) {
-        assert !F.isEmpty(inTraits);
-
-        try {
-            return fill(inTraits, ImmutableSet.builder(), 
ImmutableList.builder(), 0).build();
-        }
-        catch (Util.FoundOne e) {
-            return Collections.emptySet();
-        }
-    }
-
-    /** */
-    private static ImmutableSet.Builder<List<RelTraitSet>> 
fill(List<List<RelTraitSet>> in,
-        ImmutableSet.Builder<List<RelTraitSet>> out, 
ImmutableList.Builder<RelTraitSet> template, int srcIdx) {
-        if (srcIdx < in.size()) {
-            List<RelTraitSet> sourceTraits = in.get(srcIdx);
-
-            if (F.isEmpty(sourceTraits))
-                // see a special case in OptimizeTask.RelNodeOptTask#execute
-                throw Util.FoundOne.NULL;
-
-            for (RelTraitSet traits : sourceTraits) {
-                ImmutableList.Builder<RelTraitSet> newTemplate = 
ImmutableList.<RelTraitSet>builder()
-                    .addAll(template.build())
-                    .add(traits);
-
-                fill(in, out, newTemplate, srcIdx + 1);
-            }
-        }
-        else
-            out.add(template.build());
-
-        return out;
-    }
-
     /** */
     public static RelCollation projectCollation(RelCollation collation, 
List<RexNode> projects, RelDataType inputRowType) {
         if (collation.getFieldCollations().isEmpty())
@@ -355,4 +327,116 @@ public class TraitUtils {
 
         return distribution.apply(mapping);
     }
+
+    /** */
+    public static RelNode passThrough(TraitsAwareIgniteRel rel, RelTraitSet 
outTraits) {
+        if (outTraits.getConvention() != IgniteConvention.INSTANCE || 
rel.getInputs().isEmpty())
+            return null;
+
+        List<RelTraitSet> inTraits = 
Collections.nCopies(rel.getInputs().size(),
+            rel.getCluster().traitSetOf(IgniteConvention.INSTANCE));
+
+        List<RelNode> nodes = new 
PropagationContext(ImmutableSet.of(Pair.of(outTraits, inTraits)))
+            .propagate(rel::passThroughCollation)
+            .propagate(rel::passThroughDistribution)
+            .propagate(rel::passThroughRewindability)
+            .nodes(rel::createNode);
+
+        if (nodes.isEmpty())
+            return null;
+
+        return new RelRegistrar(rel.getCluster(), outTraits, rel, nodes);
+    }
+
+    /** */
+    public static List<RelNode> derive(TraitsAwareIgniteRel rel, 
List<List<RelTraitSet>> inTraits) {
+        assert !F.isEmpty(inTraits);
+
+        RelTraitSet outTraits = 
rel.getCluster().traitSetOf(IgniteConvention.INSTANCE);
+        Set<Pair<RelTraitSet, List<RelTraitSet>>> combinations = 
combinations(outTraits, inTraits);
+
+        if (combinations.isEmpty())
+            return ImmutableList.of();
+
+        return new PropagationContext(combinations)
+            .propagate(rel::deriveCollation)
+            .propagate(rel::deriveDistribution)
+            .propagate(rel::deriveRewindability)
+            .nodes(rel::createNode);
+    }
+
+    /** */
+    private static Set<Pair<RelTraitSet, List<RelTraitSet>>> 
combinations(RelTraitSet outTraits, List<List<RelTraitSet>> inTraits) {
+        Set<Pair<RelTraitSet, List<RelTraitSet>>> out = new HashSet<>();
+        fillRecursive(outTraits, inTraits, out, new 
RelTraitSet[inTraits.size()],0);
+        return out;
+    }
+
+    /** */
+    private static boolean fillRecursive(RelTraitSet outTraits, 
List<List<RelTraitSet>> inTraits,
+        Set<Pair<RelTraitSet, List<RelTraitSet>>> result, RelTraitSet[] 
combination, int idx) throws ControlFlowException {
+        boolean processed = false, last = idx == inTraits.size() - 1;
+        for (RelTraitSet t : inTraits.get(idx)) {
+            if (t.getConvention() != IgniteConvention.INSTANCE)
+                continue;
+
+            processed = true;
+            combination[idx] = t;
+
+            if (last)
+                result.add(Pair.of(outTraits, 
ImmutableList.copyOf(combination)));
+            else if (!fillRecursive(outTraits, inTraits, result, combination, 
idx + 1))
+                return false;
+        }
+        return processed;
+    }
+
+    /** */
+    private static class PropagationContext {
+        /** */
+        private final Set<Pair<RelTraitSet, List<RelTraitSet>>> combinations;
+
+        /** */
+        private PropagationContext(Set<Pair<RelTraitSet, List<RelTraitSet>>> 
combinations) {
+            this.combinations = combinations;
+        }
+
+        /**
+         * Propagates traits in bottom-up or up-to-bottom manner using given 
traits propagator.
+         */
+        public PropagationContext propagate(TraitsPropagator processor) {
+            if (combinations.isEmpty())
+                return this;
+
+            ImmutableSet.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableSet.builder();
+            for (Pair<RelTraitSet, List<RelTraitSet>> variant : combinations)
+                b.addAll(processor.propagate(variant.left, variant.right));
+            return new PropagationContext(b.build());
+        }
+
+        /**
+         * Creates nodes using given factory.
+         */
+        public List<RelNode> nodes(RelFactory nodesCreator) {
+            if (combinations.isEmpty())
+                return ImmutableList.of();
+
+            ImmutableList.Builder<RelNode> b = ImmutableList.builder();
+            for (Pair<RelTraitSet, List<RelTraitSet>> variant : combinations)
+                b.add(nodesCreator.create(variant.left, variant.right));
+            return b.build();
+        }
+    }
+
+    /** */
+    private interface TraitsPropagator {
+        /**
+         * Propagates traits in bottom-up or up-to-bottom manner.
+         *
+         * @param outTraits Relational node traits.
+         * @param inTraits Relational node input traits.
+         * @return List of possible input-output traits combinations.
+         */
+        List<Pair<RelTraitSet, List<RelTraitSet>>> propagate(RelTraitSet 
outTraits, List<RelTraitSet> inTraits);
+    }
 }
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsAwareIgniteRel.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsAwareIgniteRel.java
index 0392a36..bb8a5f0 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsAwareIgniteRel.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsAwareIgniteRel.java
@@ -20,59 +20,23 @@ package 
org.apache.ignite.internal.processors.query.calcite.trait;
 import java.util.List;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.plan.DeriveMode;
-import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.plan.volcano.VolcanoPlanner;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.util.Pair;
 import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
 import org.apache.ignite.internal.processors.query.calcite.util.Commons;
 
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
-
 /** */
 public interface TraitsAwareIgniteRel extends IgniteRel {
     /** {@inheritDoc} */
     @Override public default RelNode passThrough(RelTraitSet required) {
-        required = fixTraits(required);
-
-        List<RelNode> nodes = TraitsPropagationContext.forPassingThrough(this, 
required)
-            .propagate(this::passThroughCollation)
-            .propagate(this::passThroughDistribution)
-            .propagate(this::passThroughRewindability)
-            .nodes(this::createNode);
-
-        RelOptPlanner planner = getCluster().getPlanner();
-
-        assert planner instanceof VolcanoPlanner;
-
-        for (RelNode node : nodes) {
-            RelTraitSet traits = node.getTraitSet();
-
-            // try to fix traits somehow.
-            if (!traits.satisfies(required))
-                node = TraitUtils.enforce(node, required);
-
-            if (node != null) {
-                boolean satisfies = node.getTraitSet().satisfies(required);
-
-                assert satisfies : "current rel=" + getRelTypeName() + ", 
traits=" + traits + ", required=" + required;
-
-                planner.register(node, this);
-            }
-        }
-
-        return RelOptRule.convert(this, required);
+        return TraitUtils.passThrough(this, required);
     }
 
     /** {@inheritDoc} */
     @Override public default List<RelNode> derive(List<List<RelTraitSet>> 
inTraits) {
-        return TraitsPropagationContext.forDerivation(this, inTraits)
-            .propagate(this::deriveCollation)
-            .propagate(this::deriveDistribution)
-            .propagate(this::deriveRewindability)
-            .nodes(this::createNode);
+        return TraitUtils.derive(this, inTraits);
     }
 
     /** {@inheritDoc} */
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsPropagationContext.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsPropagationContext.java
deleted file mode 100644
index 4c936d4..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsPropagationContext.java
+++ /dev/null
@@ -1,98 +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.trait;
-
-import java.util.List;
-import java.util.Set;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.util.Pair;
-import org.apache.ignite.internal.processors.query.calcite.util.Commons;
-
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.fixTraits;
-import static 
org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils.inputTraits;
-
-/** */
-public class TraitsPropagationContext {
-    /** */
-    private final Set<Pair<RelTraitSet, List<RelTraitSet>>> combinations;
-
-    /**
-     * Creates a context for up-to-bottom traits propagation.
-     *
-     * @param node Target node.
-     * @param outTrait Desired relational node output trait.
-     * @return Newly created context.
-     */
-    public static TraitsPropagationContext forPassingThrough(RelNode node, 
RelTraitSet outTrait) {
-        ImmutableSet<Pair<RelTraitSet, List<RelTraitSet>>> variants = 
ImmutableSet.of(
-            Pair.of(fixTraits(outTrait),
-                Commons.transform(node.getInputs(), i -> 
fixTraits(i.getCluster().traitSet()))));
-
-        return new TraitsPropagationContext(variants);
-    }
-
-    /**
-     * Creates a context for bottom-up traits propagation.
-     *
-     * @param node Target node.
-     * @param inTraits Input traits.
-     * @return Newly created context.
-     */
-    public static TraitsPropagationContext forDerivation(RelNode node, 
List<List<RelTraitSet>> inTraits) {
-        ImmutableSet.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableSet.builder();
-
-        RelTraitSet out = fixTraits(node.getCluster().traitSet());
-
-        for (List<RelTraitSet> srcTraits : inputTraits(inTraits))
-            b.add(Pair.of(out, Commons.transform(srcTraits, 
TraitUtils::fixTraits)));
-
-        return new TraitsPropagationContext(b.build());
-    }
-
-    /** */
-    private TraitsPropagationContext(Set<Pair<RelTraitSet, List<RelTraitSet>>> 
combinations) {
-        this.combinations = combinations;
-    }
-
-    /**
-     * Propagates traits in bottom-up or up-to-bottom manner using given 
traits propagator.
-     */
-    public TraitsPropagationContext propagate(TraitsPropagator processor) {
-        ImmutableSet.Builder<Pair<RelTraitSet, List<RelTraitSet>>> b = 
ImmutableSet.builder();
-
-        for (Pair<RelTraitSet, List<RelTraitSet>> variant : combinations)
-            b.addAll(processor.propagate(variant.left, variant.right));
-
-        return new TraitsPropagationContext(b.build());
-    }
-
-    /**
-     * Creates nodes using given factory.
-     */
-    public List<RelNode> nodes(RelFactory nodesCreator) {
-        ImmutableList.Builder<RelNode> b = ImmutableList.builder();
-
-        for (Pair<RelTraitSet, List<RelTraitSet>> variant : combinations)
-            b.add(nodesCreator.create(variant.left, variant.right));
-
-        return b.build();
-    }
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsPropagator.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsPropagator.java
deleted file mode 100644
index b34a2b7..0000000
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/trait/TraitsPropagator.java
+++ /dev/null
@@ -1,35 +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.trait;
-
-import java.util.List;
-
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.util.Pair;
-
-/** */
-public interface TraitsPropagator {
-    /**
-     * Propagates traits in bottom-up or up-to-bottom manner.
-     *
-     * @param outTraits Relational node traits.
-     * @param inTraits Relational node input traits.
-     * @return List of possible input-output traits combinations.
-     */
-    List<Pair<RelTraitSet, List<RelTraitSet>>> propagate(RelTraitSet 
outTraits, List<RelTraitSet> inTraits);
-}
diff --git 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
index 17d4071..db97e35 100644
--- 
a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
+++ 
b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/RexUtils.java
@@ -176,8 +176,6 @@ public class RexUtils {
             if (F.isEmpty(collFldPreds))
                 break;
 
-            boolean lowerBoundBelow = !fc.getDirection().isDescending();
-
             RexNode bestUpper = null;
             RexNode bestLower = null;
 
@@ -188,6 +186,7 @@ public class RexUtils {
                     assert idxOpSupports(cond) : cond;
                 }
 
+                boolean lowerBoundBelow = !fc.getDirection().isDescending();
                 SqlOperator op = pred.getOperator();
                 switch (op.kind) {
                     case EQUALS:
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
index 4188276..bc01282 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/PlannerTest.java
@@ -2634,7 +2634,7 @@ public class PlannerTest extends GridCommonAbstractTest {
             assertEquals("" +
                     "IgniteCorrelatedNestedLoopJoin(condition=[=(CAST(+($0, 
$1)):INTEGER, 2)], joinType=[inner])\n" +
                     "  IgniteTableScan(table=[[PUBLIC, DEPT]], 
requiredColunms=[{0}])\n" +
-                    "  IgniteTableScan(table=[[PUBLIC, EMP]], 
filters=[=(CAST(+($cor2.DEPTNO, $t0)):INTEGER, 2)], requiredColunms=[{2}])\n",
+                    "  IgniteTableScan(table=[[PUBLIC, EMP]], 
filters=[=(CAST(+($cor3.DEPTNO, $t0)):INTEGER, 2)], requiredColunms=[{2}])\n",
                 RelOptUtil.toString(phys));
         }
     }
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ExecutionTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ExecutionTest.java
index bbbee53..8e26411 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ExecutionTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/rel/ExecutionTest.java
@@ -798,7 +798,6 @@ public class ExecutionTest extends AbstractExecutionTest {
         RootNode<Object[]> root = new RootNode<>(ctx, aggType);
         root.register(agg);
 
-
         assertTrue(root.hasNext());
         assertEquals(1400, root.next()[0]);
         assertFalse(root.hasNext());
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
index 83a7854..bbf85d1 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/OrToUnionRuleTest.java
@@ -223,7 +223,7 @@ public class OrToUnionRuleTest extends 
GridCommonAbstractTest {
      * @throws Exception If failed.
      */
     @Test
-    //@Ignore("https://issues.apache.org/jira/browse/IGNITE-12819";)
+    @Ignore("https://issues.apache.org/jira/browse/IGNITE-12819";)
     public void testAllNonIndexedOrToUnionAllRewrite() throws Exception {
         checkQuery("SELECT * " +
             "FROM products " +
diff --git 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
index 983d82e..783d23e 100644
--- 
a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
+++ 
b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/rules/ProjectScanMergeRuleTest.java
@@ -36,11 +36,10 @@ import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.c
 import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsOneProject;
 import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsProject;
 import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.containsTableScan;
-import static 
org.apache.ignite.internal.processors.query.calcite.QueryChecker.notContainsProject;
 import static 
org.apache.ignite.internal.processors.query.calcite.rules.OrToUnionRuleTest.Product;
 
 /**
- * Tests projection rule {@code 
org.apache.ignite.internal.processors.query.calcite.rule.ProjectScanMergeRule}
+ * Tests projection rule {@code 
org.apache.ignite.internal.processors.query.calcite.rule.logical.ProjectScanMergeRule}
  * This rule have a deal with only useful columns and.
  * For example for tables: T1(f12, f12, f13) and T2(f21, f22, f23)
  * sql execution: SELECT t1.f11, t2.f21 FROM T1 t1 INNER JOIN T2 t2 on t1.f11 
= t2.f22"
@@ -136,14 +135,12 @@ public class ProjectScanMergeRuleTest extends 
GridCommonAbstractTest {
     public void testNestedProjects() {
         checkQuery("SELECT NAME FROM products WHERE CAT_ID IN (SELECT CAT_ID 
FROM products WHERE CAT_ID > 1) and ID > 2;")
             .matches(containsIndexScan("PUBLIC", "PRODUCTS"))
-            .matches(notContainsProject("PUBLIC", "PRODUCTS"))
             .returns("noname3")
             .returns("noname4")
             .check();
 
         checkQuery("SELECT NAME FROM products WHERE CAT_ID IN (SELECT DISTINCT 
CAT_ID FROM products WHERE CAT_ID > 1)")
             .matches(containsIndexScan("PUBLIC", "PRODUCTS"))
-            .matches(notContainsProject("PUBLIC", "PRODUCTS"))
             .returns("noname2")
             .returns("noname3")
             .returns("noname4")
@@ -157,7 +154,6 @@ public class ProjectScanMergeRuleTest extends 
GridCommonAbstractTest {
 
         checkQuery("SELECT NAME FROM products WHERE CAT_ID = (SELECT CAT_ID 
FROM products WHERE SUBCAT_ID = 13)")
             .matches(containsTableScan("PUBLIC", "PRODUCTS"))
-            .matches(containsIndexScan("PUBLIC", "PRODUCTS"))
             .returns("noname4")
             .check();
     }

Reply via email to