korlov42 commented on a change in pull request #9143:
URL: https://github.com/apache/ignite/pull/9143#discussion_r695756692



##########
File path: 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/IgniteMergeJoin.java
##########
@@ -162,74 +166,82 @@ else if (isPrefix(rightCollation.getKeys(), 
joinInfo.rightKeys))// preserve righ
 
     /** {@inheritDoc} */
     @Override public Pair<RelTraitSet, List<RelTraitSet>> passThroughCollation(
-        RelTraitSet nodeTraits,
+        RelTraitSet required,
         List<RelTraitSet> inputTraits
     ) {
-        RelCollation collation = TraitUtils.collation(nodeTraits);
-        RelTraitSet left = inputTraits.get(0), right = inputTraits.get(1);
-
-        int rightOff = this.left.getRowType().getFieldCount();
-
-        Map<Integer, Integer> rightToLeft = joinInfo.pairs().stream()
-            .collect(Collectors.toMap(p -> p.target, p -> p.source));
+        RelCollation collation = TraitUtils.collation(required);
+        RelTraitSet left = inputTraits.get(0);
+        RelTraitSet right = inputTraits.get(1);
 
-        List<Integer> collationLeftPrj = new ArrayList<>();
-
-        for (Integer c : collation.getKeys()) {
-            collationLeftPrj.add(
-                c >= rightOff ? rightToLeft.get(c - rightOff) : c
-            );
-        }
+        if (joinType == FULL)
+            return passThroughDefaultCollation(required, left, right);
 
-        boolean preserveNodeCollation = false;
+        int leftInputFieldCount = this.left.getRowType().getFieldCount();
 
-        List<Integer> newLeftCollation, newRightCollation;
+        List<Integer> reqKeys = RelCollations.ordinals(collation);
+        List<Integer> leftKeys = joinInfo.leftKeys.toIntegerList();
+        List<Integer> rightKeys = 
joinInfo.rightKeys.incr(leftInputFieldCount).toIntegerList();
 
-        Map<Integer, Integer> leftToRight = joinInfo.pairs().stream()
-            .collect(Collectors.toMap(p -> p.source, p -> p.target));
+        ImmutableBitSet reqKeySet = ImmutableBitSet.of(reqKeys);
+        ImmutableBitSet leftKeySet = ImmutableBitSet.of(joinInfo.leftKeys);
+        ImmutableBitSet rightKeySet = ImmutableBitSet.of(rightKeys);
 
-        if (isPrefix(collationLeftPrj, joinInfo.leftKeys)) { // preserve 
collation
-            newLeftCollation = new ArrayList<>();
-            newRightCollation = new ArrayList<>();
+        RelCollation nodeCollation;
+        RelCollation leftCollation;
+        RelCollation rightCollation;
 
-            int ind = 0;
-            for (Integer c : collation.getKeys()) {
-                if (c < rightOff) {
-                    newLeftCollation.add(c);
+        if (reqKeySet.equals(leftKeySet)) {
+            if (joinType == RIGHT)
+                return passThroughDefaultCollation(required, left, right);
 
-                    if (ind < joinInfo.leftKeys.size())
-                        newRightCollation.add(leftToRight.get(c));
-                }
-                else {
-                    c -= rightOff;
-                    newRightCollation.add(c);
-
-                    if (ind < joinInfo.leftKeys.size())
-                        newLeftCollation.add(rightToLeft.get(c));
-                }
-
-                ind++;
-            }
-
-            preserveNodeCollation = true;
+            nodeCollation = collation;
+            leftCollation = collation;
+            rightCollation = collation.apply(buildTransposeMapping(true));
         }
-        else { // generate new collations
-            newLeftCollation = maxPrefix(collationLeftPrj, joinInfo.leftKeys);
-
-            Set<Integer> tail = new HashSet<>(joinInfo.leftKeys);
-
-            tail.removeAll(newLeftCollation);
-
-            newLeftCollation.addAll(tail);
+        else if (containsOrderless(leftKeys, collation)) {
+            if (joinType == RIGHT)
+                return passThroughDefaultCollation(required, left, right);
+
+            // if sort keys are subset of left join keys, we can extend 
collations to make sure all join
+            // keys are sorted.
+            nodeCollation = collation;
+            leftCollation = extendCollation(collation, leftKeys);
+            rightCollation = leftCollation.apply(buildTransposeMapping(true));
+        }
+        else if (containsOrderless(collation, leftKeys) && 
reqKeys.stream().allMatch(i -> i < leftInputFieldCount)) {
+            if (joinType == RIGHT)
+                return passThroughDefaultCollation(required, left, right);
+
+            // if sort keys are superset of left join keys, and left join keys 
is prefix of sort keys
+            // (order not matter), also sort keys are all from left join input.
+            nodeCollation = collation;
+            leftCollation = collation;
+            rightCollation = leftCollation.apply(buildTransposeMapping(true));
+        }
+        else if (reqKeySet.equals(rightKeySet)) {
+            if (joinType == LEFT)
+                return passThroughDefaultCollation(required, left, right);
 
-            newRightCollation = 
newLeftCollation.stream().map(leftToRight::get).collect(Collectors.toList());
+            nodeCollation = collation;
+            rightCollation = RelCollations.shift(collation, 
-leftInputFieldCount);
+            leftCollation = rightCollation.apply(buildTransposeMapping(false));
         }
+        else if (containsOrderless(rightKeys, collation)) {
+            if (joinType == LEFT)
+                return passThroughDefaultCollation(required, left, right);
 
-        RelCollation leftCollation = createCollation(newLeftCollation);
-        RelCollation rightCollation = createCollation(newRightCollation);
+            nodeCollation = collation;
+            rightCollation = RelCollations.shift(extendCollation(collation, 
rightKeys), -leftInputFieldCount);
+            leftCollation = rightCollation.apply(buildTransposeMapping(false));
+        }
+        else {
+            nodeCollation = EMPTY;

Review comment:
       fixed

##########
File path: 
modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/MergeJoinPlannerTest.java
##########
@@ -0,0 +1,2799 @@
+/*
+ * 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.planner;
+
+import java.util.List;
+
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelFieldCollation;
+import org.apache.calcite.rel.core.Join;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteSort;
+import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema;
+import 
org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
+import org.junit.Test;
+
+import static org.apache.calcite.rel.RelFieldCollation.Direction.ASCENDING;
+import static org.apache.calcite.rel.RelFieldCollation.Direction.DESCENDING;
+
+/** MergeJoin planner test. */
+public class MergeJoinPlannerTest extends AbstractPlannerTest {
+    /** Only MergeJoin encourage. */
+    private static final String[] DISABLED_RULES = {
+        "NestedLoopJoinConverter",
+        "CorrelatedNestedLoopJoin",
+        "FilterSpoolMergeRule",
+        "JoinCommuteRule"
+    };
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys of left table 
should be propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1, LEFT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys of left table 
should be propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft2() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 DESC, LEFT_T.c2 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, DESCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys of left table 
should be propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft3() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 DESC, LEFT_T.c2 ASC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys of left table 
should be propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft4() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 ASC NULLS LAST, LEFT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys of left table 
should be propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft5() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 ASC NULLS FIRST, LEFT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is superset of join keys should 
be propagated as is,
+     * if it doesn't include fields from right table.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft6() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1, LEFT_T.c2, LEFT_T.c3";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            sortNodes.get(0).collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortNodes.get(1).collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is superset of join keys should 
be propagated as is,
+     * if it doesn't include fields from right table.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft7() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 DESC, LEFT_T.c2 DESC, LEFT_T.c3 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING),
+                new RelFieldCollation(2, DESCENDING)
+            ),
+            sortNodes.get(0).collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            sortNodes.get(1).collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is superset of join keys should 
be propagated as is,
+     * if it doesn't include fields from right table.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft8() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 DESC, LEFT_T.c2 ASC, LEFT_T.c3 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, DESCENDING)
+            ),
+            sortNodes.get(0).collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortNodes.get(1).collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is superset of join keys should 
be propagated as is,
+     * if it doesn't include fields from right table.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft9() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 ASC NULLS LAST, LEFT_T.c2 ASC NULLS LAST, 
LEFT_T.c3 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            sortNodes.get(0).collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortNodes.get(1).collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is superset of join keys should 
be propagated as is,
+     * if it doesn't include fields from right table.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft10() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1 ASC NULLS FIRST, LEFT_T.c2 ASC NULLS LAST, 
LEFT_T.c3 ASC NULLS FIRST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING, 
RelFieldCollation.NullDirection.FIRST)
+            ),
+            sortNodes.get(0).collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortNodes.get(1).collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is superset of join keys, but its 
prefix
+     * contains columns outside of join keys, can't be propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft11() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c3, LEFT_T.c2, LEFT_T.c1";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        IgniteSort topSortNode = sortOnTopOfJoin(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(2, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(0, ASCENDING)
+            ),
+            topSortNode.collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortNodes.get(0).collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortNodes.get(1).collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft12() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by LEFT_T.c1, LEFT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft13() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by LEFT_T.c1 DESC, LEFT_T.c2 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, DESCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft14() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by LEFT_T.c1 DESC, LEFT_T.c2 ASC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft15() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by LEFT_T.c1 ASC NULLS LAST, LEFT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByLeft16() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by LEFT_T.c1 ASC NULLS FIRST, LEFT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight2() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 DESC, RIGHT_T.c2 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, DESCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight3() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 DESC, RIGHT_T.c2 ASC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight4() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 ASC NULLS LAST, RIGHT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight5() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 ASC NULLS FIRST, RIGHT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * Any collation that contains column from right table which is not part
+     * of join keys can't be propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight6() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2, RIGHT_T.c3";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        IgniteSort topSortNode = sortOnTopOfJoin(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(3, ASCENDING),
+                new RelFieldCollation(4, ASCENDING),
+                new RelFieldCollation(5, ASCENDING)
+            ),
+            topSortNode.collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expectedBottomCollation = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expectedBottomCollation, sortNodes.get(0).collation());
+        assertEquals(expectedBottomCollation, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight7() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight8() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by RIGHT_T.c1 DESC, RIGHT_T.c2 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, DESCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight9() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by RIGHT_T.c1 DESC, RIGHT_T.c2 ASC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight10() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by RIGHT_T.c1 ASC NULLS LAST, RIGHT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, collation that is subset of join keys should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerPassThroughOrderByRight11() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by RIGHT_T.c1 ASC NULLS FIRST, RIGHT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation consisted of left keys can't be 
propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByLeft1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1, LEFT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        IgniteSort topSortNode = sortOnTopOfJoin(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            topSortNode.collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation that is superset of join keys can't be 
propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByLeft2() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1, LEFT_T.c2, LEFT_T.c3";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        IgniteSort topSortNode = sortOnTopOfJoin(rel);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            topSortNode.collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByRight1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByRight2() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 DESC, RIGHT_T.c2 DESC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, DESCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByRight3() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 DESC, RIGHT_T.c2 ASC";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, DESCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByRight4() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 ASC NULLS LAST, RIGHT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of right join, collation consisted of join keys only should be 
propagated as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testRightPassThroughOrderByRight5() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            " right join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1 ASC NULLS FIRST, RIGHT_T.c2 ASC NULLS LAST";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of left join, collation consisted of join keys of right table 
can't be propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testLeftPassThroughOrderByRight1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  left join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(3, ASCENDING),
+                new RelFieldCollation(4, ASCENDING)
+            ),
+            sortOnTopOfJoin(rel).collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of left join, collation consisted of join keys of right table 
can't be propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testLeftPassThroughOrderByRight2() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  left join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(3, ASCENDING),
+                new RelFieldCollation(4, ASCENDING)
+            ),
+            sortOnTopOfJoin(rel).collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING),
+            new RelFieldCollation(2, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of full join, any collation can't be propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testFullPassThroughOrderByLeft1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  full join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1, LEFT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfJoin(rel).collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of full join, any collation can't be propagated.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testFullPassThroughOrderByRight1() throws Exception {
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  full join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by RIGHT_T.c1, RIGHT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(3, ASCENDING),
+                new RelFieldCollation(4, ASCENDING)
+            ),
+            sortOnTopOfJoin(rel).collation()
+        );
+
+        List<IgniteSort> sortNodes = sortOnTopOfScan(rel);
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortNodes.get(0).collation());
+        assertEquals(expected, sortNodes.get(1).collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft1() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft2() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft3() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft4() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.LAST),
+                new RelFieldCollation(1, ASCENDING, 
RelFieldCollation.NullDirection.LAST)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft5() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft6() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft7() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING),
+                new RelFieldCollation(2, DESCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft8() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal, but 
its prefix contains columns
+     * outside of join keys, can't be derived.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft9() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(2, ASCENDING),
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset or equal to join 
keys could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft10() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is subset of join keys, is 
not suitable.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft11() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            "   and LEFT_T.c3 = RIGHT_T.c3 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset of join keys and 
has a common prefix could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft12() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c1, LEFT_T.c2, LEFT_T.c3";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfJoin(rel));
+
+        assertNull(sortOnTopOfScan(rel, "LEFT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "RIGHT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, left collation that is superset of join keys, 
and there is no a common prefix,
+     * could not be derived.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveLeft13() throws Exception {
+        TestTable left = createTable("LEFT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        left.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(2, ASCENDING),
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            left,
+            createTable("RIGHT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class)
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 " +
+            " order by LEFT_T.c3, LEFT_T.c1, LEFT_T.c2";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(2, ASCENDING),
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfJoin(rel).collation()
+        );
+
+        RelCollation expected = RelCollations.of(
+            new RelFieldCollation(0, ASCENDING),
+            new RelFieldCollation(1, ASCENDING)
+        );
+
+        assertEquals(expected, sortOnTopOfScan(rel, "LEFT_T").collation());
+        assertEquals(expected, sortOnTopOfScan(rel, "RIGHT_T").collation());
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that consists of join keys only 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight1() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that consists of join keys only 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight2() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that consists of join keys only 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight3() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that consists of join keys only 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight4() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that consists of join keys only 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight5() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that is superset of join keys 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight6() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that is superset of join keys 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight7() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING),
+                new RelFieldCollation(2, DESCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, DESCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that is superset of join keys 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight8() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, DESCENDING),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that is superset of join keys 
could be derived as is.
+     *
+     * @throws Exception In case of any unexpected error.
+     */
+    @Test
+    public void testInnerDerivePreserveRight9() throws Exception {
+        TestTable right = createTable("RIGHT_T", IgniteDistributions.single(),
+            "C1", Integer.class, "C2", Integer.class, "C3", Integer.class);
+
+        right.addIndex(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING),
+                new RelFieldCollation(2, ASCENDING)
+            ),
+            "idx"
+        );
+
+        IgniteSchema schema = createSchema(
+            createTable("LEFT_T", IgniteDistributions.single(), "C1", 
Integer.class, "C2", Integer.class, "C3", Integer.class),
+            right
+        );
+
+        String sql = "" +
+            "select * " +
+            "  from LEFT_T " +
+            "  join RIGHT_T " +
+            "    on LEFT_T.c1 = RIGHT_T.c1 " +
+            "   and LEFT_T.c2 = RIGHT_T.c2 ";
+
+        IgniteRel rel = physicalPlan(sql, schema, DISABLED_RULES);
+
+        assertNull(sortOnTopOfScan(rel, "RIGHT_T"));
+        assertEquals(
+            RelCollations.of(
+                new RelFieldCollation(0, ASCENDING, 
RelFieldCollation.NullDirection.FIRST),
+                new RelFieldCollation(1, ASCENDING)
+            ),
+            sortOnTopOfScan(rel, "LEFT_T").collation()
+        );
+    }
+
+    /**
+     * Test verifies the collation propagation from a parent node.
+     *
+     * In case of inner join, right collation that is superset of join keys 
could be derived as is.

Review comment:
       fixed

##########
File path: 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/IgniteMdCollation.java
##########
@@ -548,9 +548,9 @@ public int compare(List<RexLiteral> o1, List<RexLiteral> 
o2) {
             case FULL:
                 for (RelCollation collation : leftCollations) {
                     for (RelFieldCollation field : 
collation.getFieldCollations()) {
-                        if (!(RelFieldCollation.NullDirection.LAST == 
field.nullDirection)) {
+                        if 
(!(RelFieldCollation.NullDirection.LAST.nullComparison ==

Review comment:
       Cleaned up this mess a bit

##########
File path: 
modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SchemaHolderImpl.java
##########
@@ -247,8 +248,11 @@ private static Object 
affinityIdentity(CacheConfiguration<?, ?> ccfg) {
             boolean descending = idxDesc.descending(idxField);
             int fieldIdx = fieldDesc.fieldIndex();
 
-            RelFieldCollation collation = new RelFieldCollation(fieldIdx,
-                descending ? RelFieldCollation.Direction.DESCENDING : 
RelFieldCollation.Direction.ASCENDING);
+            RelFieldCollation collation = new RelFieldCollation(
+                fieldIdx,
+                descending ? RelFieldCollation.Direction.DESCENDING : 
RelFieldCollation.Direction.ASCENDING,
+                RelFieldCollation.NullDirection.FIRST

Review comment:
       good catch, folks! Problem fixed, new tests added, 
`defaultNullCollation` changed to `LOW`




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to