You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2016/02/15 08:51:51 UTC

[07/50] [abbrv] ignite git commit: ignite-split2 - renamings

ignite-split2 - renamings


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d86e0aeb
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d86e0aeb
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d86e0aeb

Branch: refs/heads/ignite-1232
Commit: d86e0aeb154f4152dab95c6ae63ee5661776d2a8
Parents: b3b0312
Author: S.Vladykin <sv...@gridgain.com>
Authored: Sun Dec 6 02:27:29 2015 +0300
Committer: S.Vladykin <sv...@gridgain.com>
Committed: Sun Dec 6 02:27:29 2015 +0300

----------------------------------------------------------------------
 .../query/h2/opt/GridH2Collocation.java         | 661 ------------------
 .../query/h2/opt/GridH2CollocationModel.java    | 662 +++++++++++++++++++
 .../query/h2/opt/GridH2IndexBase.java           |   6 +-
 .../query/h2/opt/GridH2QueryContext.java        |  12 +-
 .../query/h2/sql/GridSqlQuerySplitter.java      |   2 +-
 5 files changed, 672 insertions(+), 671 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/d86e0aeb/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Collocation.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Collocation.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Collocation.java
deleted file mode 100644
index c4cc6a5..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Collocation.java
+++ /dev/null
@@ -1,661 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.query.h2.opt;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import org.h2.command.dml.Query;
-import org.h2.command.dml.Select;
-import org.h2.command.dml.SelectUnion;
-import org.h2.expression.Expression;
-import org.h2.expression.ExpressionColumn;
-import org.h2.index.IndexCondition;
-import org.h2.index.ViewIndex;
-import org.h2.table.Column;
-import org.h2.table.SubQueryInfo;
-import org.h2.table.Table;
-import org.h2.table.TableFilter;
-
-/**
- * Collocation model for a query.
- */
-public final class GridH2Collocation {
-    /** */
-    public static final int MULTIPLIER_COLLOCATED = 1;
-
-    /** */
-    private static final int MULTIPLIER_UNICAST = 20;
-
-    /** */
-    private static final int MULTIPLIER_BROADCAST = 80;
-
-    /** */
-    private final GridH2Collocation upper;
-
-    /** */
-    private final int filter;
-
-    /** */
-    private int multiplier;
-
-    /** */
-    private Type type;
-
-    /** */
-    private GridH2Collocation[] children;
-
-    /** */
-    private TableFilter[] childFilters;
-
-    /** */
-    private List<GridH2Collocation> unions;
-
-    /** */
-    private Select select;
-
-    /**
-     * @param upper Upper.
-     * @param filter Filter.
-     */
-    private GridH2Collocation(GridH2Collocation upper, int filter) {
-        this.upper = upper;
-        this.filter = filter;
-    }
-
-    /**
-     * @param upper Upper.
-     * @param filter Filter.
-     * @param unions Unions.
-     * @return Create collocation model.
-     */
-    private static GridH2Collocation createChild(GridH2Collocation upper, int filter, List<GridH2Collocation> unions) {
-        GridH2Collocation child = new GridH2Collocation(upper, filter);
-
-        if (unions != null) {
-            // Bind created child to unions.
-            assert upper == null || upper.child(filter, false) != null;
-
-            unions.add(child);
-
-            child.unions = unions;
-        }
-        else if (upper != null) {
-            // Bind created child to upper model.
-            assert upper.child(filter, false) == null;
-
-            upper.children[filter] = child;
-        }
-
-        return child;
-    }
-
-    /**
-     * @param childFilters New child filters.
-     * @return {@code true} If child filters were updated.
-     */
-    private boolean childFilters(TableFilter[] childFilters) {
-        assert childFilters != null;
-
-        Select select = childFilters[0].getSelect();
-
-        assert this.select == null || this.select == select;
-
-        if (this.select == null) {
-            this.select = select;
-
-            assert this.childFilters == null;
-        }
-        else if (Arrays.equals(this.childFilters, childFilters))
-            return false;
-
-        if (this.childFilters == null) {
-            // We have to clone because H2 reuses array and reorders elements.
-            this.childFilters = childFilters.clone();
-
-            children = new GridH2Collocation[childFilters.length];
-        }
-        else {
-            assert this.childFilters.length == childFilters.length;
-
-            // We have to copy because H2 reuses array and reorders elements.
-            System.arraycopy(childFilters, 0, this.childFilters, 0, childFilters.length);
-
-            Arrays.fill(children, null);
-        }
-
-        // Reset results.
-        type = null;
-        multiplier = 0;
-
-        return true;
-    }
-
-    /**
-     * @param i Index.
-     * @param f Table filter.
-     * @return {@code true} If the child is not a table or view.
-     */
-    private boolean isChildTableOrView(int i, TableFilter f) {
-        if (f == null)
-            f = childFilters[i];
-
-        Table t = f.getTable();
-
-        return t.isView() || t instanceof GridH2Table;
-    }
-
-    /**
-     * Do the needed calculations.
-     */
-    private void calculate() {
-        if (type != null)
-            return;
-
-        if (childFilters != null) {
-            // We are at sub-query.
-            boolean collocated = true;
-            boolean partitioned = false;
-            int maxMultiplier = MULTIPLIER_COLLOCATED;
-
-            for (int i = 0; i < childFilters.length; i++) {
-                GridH2Collocation c = child(i, true);
-
-                Type t = c.type(true);
-
-                if (t.isPartitioned()) {
-                    partitioned = true;
-
-                    if (!t.isCollocated()) {
-                        collocated = false;
-
-                        int m = c.multiplier(true);
-
-                        if (m > maxMultiplier) {
-                            maxMultiplier = m;
-
-                            if (maxMultiplier == MULTIPLIER_BROADCAST)
-                                break;
-                        }
-                    }
-                }
-            }
-
-            type = Type.of(partitioned, collocated);
-            multiplier = maxMultiplier;
-        }
-        else {
-            assert upper != null;
-
-            // We are at table instance.
-            GridH2Table tbl = (GridH2Table)upper.childFilters[filter].getTable();
-
-            // Only partitioned tables will do distributed joins.
-            if (!tbl.isPartitioned()) {
-                type = Type.REPLICATED;
-                multiplier = MULTIPLIER_COLLOCATED;
-
-                return;
-            }
-
-            // If we are the first partitioned table in a join, then we are "base" for all the rest partitioned tables
-            // which will need to get remote result (if there is no affinity condition). Since this query is broadcasted
-            // to all the affinity nodes the "base" does not need to get remote results.
-            if (!upper.findPartitionedTableBefore(filter)) {
-                type = Type.PARTITIONED_COLLOCATED;
-                multiplier = MULTIPLIER_COLLOCATED;
-
-                return;
-            }
-
-            // It is enough to make sure that our previous join by affinity key is collocated, then we are
-            // collocated. If we at least have affinity key condition, then we do unicast which is cheaper.
-            switch (upper.joinedWithCollocated(filter)) {
-                case JOINED_WITH_COLLOCATED:
-                    type = Type.PARTITIONED_COLLOCATED;
-                    multiplier = MULTIPLIER_COLLOCATED;
-
-                    break;
-
-                case HAS_AFFINITY_CONDITION:
-                    type = Type.PARTITIONED_NOT_COLLOCATED;
-                    multiplier = MULTIPLIER_UNICAST;
-
-                    break;
-
-                case NONE:
-                    type = Type.PARTITIONED_NOT_COLLOCATED;
-                    multiplier = MULTIPLIER_BROADCAST;
-
-                    break;
-
-                default:
-                    throw new IllegalStateException();
-            }
-        }
-    }
-
-    /**
-     * @param f Current filter.
-     * @return {@code true} If partitioned table was found.
-     */
-    private boolean findPartitionedTableBefore(int f) {
-        for (int i = 0; i < f; i++) {
-            GridH2Collocation c = child(i, true);
-
-            // The c can be null if it is not a GridH2Table and not a sub-query,
-            // it is a some kind of function table or anything else that considered replicated.
-            if (c != null && c.type(true).isPartitioned())
-                return true;
-        }
-
-        // We have to search globally in upper queries as well.
-        return upper != null && upper.findPartitionedTableBefore(filter);
-    }
-
-    /**
-     * @param f Filter.
-     * @return Affinity join type.
-     */
-    private Affinity joinedWithCollocated(int f) {
-        TableFilter tf = childFilters[f];
-
-        ArrayList<IndexCondition> idxConditions = tf.getIndexConditions();
-
-        int affColId = ((GridH2Table)tf.getTable()).getAffinityKeyColumnId();
-
-        boolean affKeyConditionFound = false;
-
-        for (int i = 0; i < idxConditions.size(); i++) {
-            IndexCondition c = idxConditions.get(i);
-
-            if (c.getCompareType() == IndexCondition.EQUALITY &&
-                c.getColumn().getColumnId() == affColId && c.isEvaluatable()) {
-                affKeyConditionFound = true;
-
-                Expression exp = c.getExpression();
-
-                exp = exp.getNonAliasExpression();
-
-                if (exp instanceof ExpressionColumn) {
-                    ExpressionColumn expCol = (ExpressionColumn)exp;
-
-                    // This is one of our previous joins.
-                    TableFilter prevJoin = expCol.getTableFilter();
-
-                    if (prevJoin != null) {
-                        GridH2Collocation co = child(indexOf(prevJoin), true);
-
-                        if (co != null) {
-                            Type t = co.type(true);
-
-                            if (t.isPartitioned() && t.isCollocated() && isAffinityColumn(prevJoin, expCol))
-                                return Affinity.JOINED_WITH_COLLOCATED;
-                        }
-                    }
-                }
-            }
-        }
-
-        return affKeyConditionFound ? Affinity.HAS_AFFINITY_CONDITION : Affinity.NONE;
-    }
-
-    /**
-     * @param f Table filter.
-     * @return Index.
-     */
-    private int indexOf(TableFilter f) {
-        for (int i = 0; i < childFilters.length; i++) {
-            if (childFilters[i] == f)
-                return i;
-        }
-
-        throw new IllegalStateException();
-    }
-
-    /**
-     * @param f Table filter.
-     * @param expCol Expression column.
-     * @return {@code true} It it is an affinity column.
-     */
-    private static boolean isAffinityColumn(TableFilter f, ExpressionColumn expCol) {
-        Column col = expCol.getColumn();
-
-        if (col == null)
-            return false;
-
-        Table t = col.getTable();
-
-        if (t.isView()) {
-            Query qry = getSubQuery(f);
-
-            return isAffinityColumn(qry, expCol);
-        }
-
-        return t instanceof GridH2Table &&
-            col.getColumnId() == ((GridH2Table)t).getAffinityKeyColumnId();
-    }
-
-    /**
-     * @param qry Query.
-     * @param expCol Expression column.
-     * @return {@code true} It it is an affinity column.
-     */
-    private static boolean isAffinityColumn(Query qry, ExpressionColumn expCol) {
-        if (qry.isUnion()) {
-            SelectUnion union = (SelectUnion)qry;
-
-            return isAffinityColumn(union.getLeft(), expCol) && isAffinityColumn(union.getRight(), expCol);
-        }
-
-        Expression exp = qry.getExpressions().get(expCol.getColumn().getColumnId()).getNonAliasExpression();
-
-        if (exp instanceof ExpressionColumn) {
-            expCol = (ExpressionColumn)exp;
-
-            return isAffinityColumn(expCol.getTableFilter(), expCol);
-        }
-
-        return false;
-    }
-
-    /**
-     * @return Multiplier.
-     */
-    public int calculateMultiplier() {
-        // We don't need multiplier for union here because it will be summarized in H2.
-        return multiplier(false);
-    }
-
-    /**
-     * @param withUnion With respect to union.
-     * @return Multiplier.
-     */
-    private int multiplier(boolean withUnion) {
-        calculate();
-
-        assert multiplier != 0;
-
-        if (withUnion && unions != null) {
-            int maxMultiplier = 0;
-
-            for (int i = 0; i < unions.size(); i++) {
-                int m = unions.get(i).multiplier(false);
-
-                if (m > maxMultiplier)
-                    maxMultiplier = m;
-            }
-
-            return maxMultiplier;
-        }
-
-        return multiplier;
-    }
-
-    /**
-     * @param withUnion With respect to union.
-     * @return Type.
-     */
-    private Type type(boolean withUnion) {
-        calculate();
-
-        assert type != null;
-
-        if (withUnion && unions != null) {
-            Type left = unions.get(0).type(false);
-
-            for (int i = 1; i < unions.size(); i++) {
-                Type right = unions.get(i).type(false);
-
-                if (!left.isCollocated() || !right.isCollocated()) {
-                    left = Type.PARTITIONED_NOT_COLLOCATED;
-
-                    break;
-                }
-                else if (!left.isPartitioned() && !right.isPartitioned())
-                    left = Type.REPLICATED;
-                else
-                    left = Type.PARTITIONED_COLLOCATED;
-            }
-
-            return left;
-        }
-
-        return type;
-    }
-
-    /**
-     * @param i Index.
-     * @param create Create child if needed.
-     * @return Child collocation.
-     */
-    private GridH2Collocation child(int i, boolean create) {
-        GridH2Collocation child = children[i];
-
-        if (child == null && create && isChildTableOrView(i, null)) {
-            TableFilter f = childFilters[i];
-
-            children[i] = child = f.getTable().isView() ?
-                buildCollocationModel(this, i, getSubQuery(f), null) :
-                createChild(this, i, null);
-        }
-
-        return child;
-    }
-
-    /**
-     * @param f Table filter.
-     * @return Sub-query.
-     */
-    private static Query getSubQuery(TableFilter f) {
-        return ((ViewIndex)f.getIndex()).getQuery();
-    }
-
-    /**
-     * @return Unions list.
-     */
-    private List<GridH2Collocation> getOrCreateUnions() {
-        if (unions == null) {
-            unions = new ArrayList<>(4);
-
-            unions.add(this);
-        }
-
-        return unions;
-    }
-
-    /**
-     * @param qctx Query context.
-     * @param info Sub-query info.
-     * @param filters Filters.
-     * @param filter Filter.
-     * @return Collocation.
-     */
-    public static GridH2Collocation buildCollocationModel(GridH2QueryContext qctx, SubQueryInfo info,
-        TableFilter[] filters, int filter) {
-        GridH2Collocation c;
-
-        if (info != null) {
-            // Go up until we reach the root query.
-            c = buildCollocationModel(qctx, info.getUpper(), info.getFilters(), info.getFilter());
-        }
-        else {
-            // We are at the root query.
-            c = qctx.queryCollocation();
-
-            if (c == null) {
-                c = createChild(null, -1, null);
-
-                qctx.queryCollocation(c);
-            }
-        }
-
-        Select select = filters[0].getSelect();
-
-        // Handle union. We have to rely on fact that select will be the same on uppermost select.
-        // For sub-queries we will drop collocation models, so that they will be recalculated anyways.
-        if (c.select != null && c.select != select) {
-            List<GridH2Collocation> unions = c.getOrCreateUnions();
-
-            // Try to find this select in existing unions.
-            // Start with 1 because at 0 it always will be c.
-            for (int i = 1; i < unions.size(); i++) {
-                GridH2Collocation u = unions.get(i);
-
-                if (u.select == select) {
-                    c = u;
-
-                    break;
-                }
-            }
-
-            // Nothing was found, need to create new child in union.
-            if (c.select != select)
-                c = createChild(c.upper, c.filter, unions);
-        }
-
-        c.childFilters(filters);
-
-        return c.child(filter, true);
-    }
-
-    /**
-     * @param qry Query.
-     * @return {@code true} If the query is collocated.
-     */
-    public static boolean isCollocated(Query qry) {
-        return buildCollocationModel(null, -1, qry, null).type(true).isCollocated();
-    }
-
-    /**
-     * @param upper Upper.
-     * @param filter Filter.
-     * @param qry Query.
-     * @param unions Unions.
-     * @return Built model.
-     */
-    private static GridH2Collocation buildCollocationModel(GridH2Collocation upper, int filter, Query qry,
-        List<GridH2Collocation> unions) {
-        if (qry.isUnion()) {
-            if (unions == null)
-                unions = new ArrayList<>();
-
-            SelectUnion union = (SelectUnion)qry;
-
-            GridH2Collocation a = buildCollocationModel(upper, filter, union.getLeft(), unions);
-            GridH2Collocation b = buildCollocationModel(upper, filter, union.getRight(), unions);
-
-            return a == null ? b : a;
-        }
-
-        Select select = (Select)qry;
-
-        List<TableFilter> list = new ArrayList<>();
-
-        for (TableFilter f = select.getTopTableFilter(); f != null; f = f.getJoin())
-            list.add(f);
-
-        TableFilter[] filters = list.toArray(new TableFilter[list.size()]);
-
-        GridH2Collocation c = createChild(upper, filter, unions);
-
-        c.childFilters(filters);
-
-        for (int i = 0; i < filters.length; i++) {
-            TableFilter f = filters[i];
-
-            if (f.getTable().isView())
-                buildCollocationModel(c, i, getSubQuery(f), null);
-            else if (f.getTable() instanceof GridH2Table)
-                createChild(c, i, null);
-        }
-
-        return upper == null ? c : null;
-    }
-
-    /**
-     * Collocation type.
-     */
-    private enum Type {
-        /** */
-        PARTITIONED_COLLOCATED(true, true),
-
-        /** */
-        PARTITIONED_NOT_COLLOCATED(true, false),
-
-        /** */
-        REPLICATED(false, true);
-
-        /** */
-        private final boolean partitioned;
-
-        /** */
-        private final boolean collocated;
-
-        /**
-         * @param partitioned Partitioned.
-         * @param collocated Collocated.
-         */
-        Type(boolean partitioned, boolean collocated) {
-            this.partitioned = partitioned;
-            this.collocated = collocated;
-        }
-
-        /**
-         * @return {@code true} If partitioned.
-         */
-        public boolean isPartitioned() {
-            return partitioned;
-        }
-
-        /**
-         * @return {@code true} If collocated.
-         */
-        public boolean isCollocated() {
-            return collocated;
-        }
-
-        /**
-         * @param partitioned Partitioned.
-         * @param collocated Collocated.
-         * @return Type.
-         */
-        static Type of(boolean partitioned, boolean collocated) {
-            if (collocated)
-                return partitioned ? Type.PARTITIONED_COLLOCATED : Type.REPLICATED;
-
-            assert partitioned;
-
-            return Type.PARTITIONED_NOT_COLLOCATED;
-        }
-    }
-
-    /**
-     * Affinity of a table relative to previous joined tables.
-     */
-    private enum Affinity {
-        /** */
-        NONE,
-
-        /** */
-        HAS_AFFINITY_CONDITION,
-
-        /** */
-        JOINED_WITH_COLLOCATED
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d86e0aeb/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java
new file mode 100644
index 0000000..2a1437c
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2CollocationModel.java
@@ -0,0 +1,662 @@
+/*
+ * 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.h2.opt;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.h2.command.dml.Query;
+import org.h2.command.dml.Select;
+import org.h2.command.dml.SelectUnion;
+import org.h2.expression.Expression;
+import org.h2.expression.ExpressionColumn;
+import org.h2.index.IndexCondition;
+import org.h2.index.ViewIndex;
+import org.h2.table.Column;
+import org.h2.table.SubQueryInfo;
+import org.h2.table.Table;
+import org.h2.table.TableFilter;
+
+/**
+ * Collocation model for a query.
+ */
+public final class GridH2CollocationModel {
+    /** */
+    public static final int MULTIPLIER_COLLOCATED = 1;
+
+    /** */
+    private static final int MULTIPLIER_UNICAST = 20;
+
+    /** */
+    private static final int MULTIPLIER_BROADCAST = 80;
+
+    /** */
+    private final GridH2CollocationModel upper;
+
+    /** */
+    private final int filter;
+
+    /** */
+    private int multiplier;
+
+    /** */
+    private Type type;
+
+    /** */
+    private GridH2CollocationModel[] children;
+
+    /** */
+    private TableFilter[] childFilters;
+
+    /** */
+    private List<GridH2CollocationModel> unions;
+
+    /** */
+    private Select select;
+
+    /**
+     * @param upper Upper.
+     * @param filter Filter.
+     */
+    private GridH2CollocationModel(GridH2CollocationModel upper, int filter) {
+        this.upper = upper;
+        this.filter = filter;
+    }
+
+    /**
+     * @param upper Upper.
+     * @param filter Filter.
+     * @param unions Unions.
+     * @return Created child collocation model.
+     */
+    private static GridH2CollocationModel createChildModel(GridH2CollocationModel upper, int filter,
+        List<GridH2CollocationModel> unions) {
+        GridH2CollocationModel child = new GridH2CollocationModel(upper, filter);
+
+        if (unions != null) {
+            // Bind created child to unions.
+            assert upper == null || upper.child(filter, false) != null;
+
+            unions.add(child);
+
+            child.unions = unions;
+        }
+        else if (upper != null) {
+            // Bind created child to upper model.
+            assert upper.child(filter, false) == null;
+
+            upper.children[filter] = child;
+        }
+
+        return child;
+    }
+
+    /**
+     * @param childFilters New child filters.
+     * @return {@code true} If child filters were updated.
+     */
+    private boolean childFilters(TableFilter[] childFilters) {
+        assert childFilters != null;
+
+        Select select = childFilters[0].getSelect();
+
+        assert this.select == null || this.select == select;
+
+        if (this.select == null) {
+            this.select = select;
+
+            assert this.childFilters == null;
+        }
+        else if (Arrays.equals(this.childFilters, childFilters))
+            return false;
+
+        if (this.childFilters == null) {
+            // We have to clone because H2 reuses array and reorders elements.
+            this.childFilters = childFilters.clone();
+
+            children = new GridH2CollocationModel[childFilters.length];
+        }
+        else {
+            assert this.childFilters.length == childFilters.length;
+
+            // We have to copy because H2 reuses array and reorders elements.
+            System.arraycopy(childFilters, 0, this.childFilters, 0, childFilters.length);
+
+            Arrays.fill(children, null);
+        }
+
+        // Reset results.
+        type = null;
+        multiplier = 0;
+
+        return true;
+    }
+
+    /**
+     * @param i Index.
+     * @param f Table filter.
+     * @return {@code true} If the child is not a table or view.
+     */
+    private boolean isChildTableOrView(int i, TableFilter f) {
+        if (f == null)
+            f = childFilters[i];
+
+        Table t = f.getTable();
+
+        return t.isView() || t instanceof GridH2Table;
+    }
+
+    /**
+     * Do the needed calculations.
+     */
+    private void calculate() {
+        if (type != null)
+            return;
+
+        if (childFilters != null) {
+            // We are at sub-query.
+            boolean collocated = true;
+            boolean partitioned = false;
+            int maxMultiplier = MULTIPLIER_COLLOCATED;
+
+            for (int i = 0; i < childFilters.length; i++) {
+                GridH2CollocationModel child = child(i, true);
+
+                Type t = child.type(true);
+
+                if (t.isPartitioned()) {
+                    partitioned = true;
+
+                    if (!t.isCollocated()) {
+                        collocated = false;
+
+                        int m = child.multiplier(true);
+
+                        if (m > maxMultiplier) {
+                            maxMultiplier = m;
+
+                            if (maxMultiplier == MULTIPLIER_BROADCAST)
+                                break;
+                        }
+                    }
+                }
+            }
+
+            type = Type.of(partitioned, collocated);
+            multiplier = maxMultiplier;
+        }
+        else {
+            assert upper != null;
+
+            // We are at table instance.
+            GridH2Table tbl = (GridH2Table)upper.childFilters[filter].getTable();
+
+            // Only partitioned tables will do distributed joins.
+            if (!tbl.isPartitioned()) {
+                type = Type.REPLICATED;
+                multiplier = MULTIPLIER_COLLOCATED;
+
+                return;
+            }
+
+            // If we are the first partitioned table in a join, then we are "base" for all the rest partitioned tables
+            // which will need to get remote result (if there is no affinity condition). Since this query is broadcasted
+            // to all the affinity nodes the "base" does not need to get remote results.
+            if (!upper.findPartitionedTableBefore(filter)) {
+                type = Type.PARTITIONED_COLLOCATED;
+                multiplier = MULTIPLIER_COLLOCATED;
+
+                return;
+            }
+
+            // It is enough to make sure that our previous join by affinity key is collocated, then we are
+            // collocated. If we at least have affinity key condition, then we do unicast which is cheaper.
+            switch (upper.joinedWithCollocated(filter)) {
+                case JOINED_WITH_COLLOCATED:
+                    type = Type.PARTITIONED_COLLOCATED;
+                    multiplier = MULTIPLIER_COLLOCATED;
+
+                    break;
+
+                case HAS_AFFINITY_CONDITION:
+                    type = Type.PARTITIONED_NOT_COLLOCATED;
+                    multiplier = MULTIPLIER_UNICAST;
+
+                    break;
+
+                case NONE:
+                    type = Type.PARTITIONED_NOT_COLLOCATED;
+                    multiplier = MULTIPLIER_BROADCAST;
+
+                    break;
+
+                default:
+                    throw new IllegalStateException();
+            }
+        }
+    }
+
+    /**
+     * @param f Current filter.
+     * @return {@code true} If partitioned table was found.
+     */
+    private boolean findPartitionedTableBefore(int f) {
+        for (int i = 0; i < f; i++) {
+            GridH2CollocationModel child = child(i, true);
+
+            // The c can be null if it is not a GridH2Table and not a sub-query,
+            // it is a some kind of function table or anything else that considered replicated.
+            if (child != null && child.type(true).isPartitioned())
+                return true;
+        }
+
+        // We have to search globally in upper queries as well.
+        return upper != null && upper.findPartitionedTableBefore(filter);
+    }
+
+    /**
+     * @param f Filter.
+     * @return Affinity join type.
+     */
+    private Affinity joinedWithCollocated(int f) {
+        TableFilter tf = childFilters[f];
+
+        ArrayList<IndexCondition> idxConditions = tf.getIndexConditions();
+
+        int affColId = ((GridH2Table)tf.getTable()).getAffinityKeyColumnId();
+
+        boolean affKeyConditionFound = false;
+
+        for (int i = 0; i < idxConditions.size(); i++) {
+            IndexCondition c = idxConditions.get(i);
+
+            if (c.getCompareType() == IndexCondition.EQUALITY &&
+                c.getColumn().getColumnId() == affColId && c.isEvaluatable()) {
+                affKeyConditionFound = true;
+
+                Expression exp = c.getExpression();
+
+                exp = exp.getNonAliasExpression();
+
+                if (exp instanceof ExpressionColumn) {
+                    ExpressionColumn expCol = (ExpressionColumn)exp;
+
+                    // This is one of our previous joins.
+                    TableFilter prevJoin = expCol.getTableFilter();
+
+                    if (prevJoin != null) {
+                        GridH2CollocationModel cm = child(indexOf(prevJoin), true);
+
+                        if (cm != null) {
+                            Type t = cm.type(true);
+
+                            if (t.isPartitioned() && t.isCollocated() && isAffinityColumn(prevJoin, expCol))
+                                return Affinity.JOINED_WITH_COLLOCATED;
+                        }
+                    }
+                }
+            }
+        }
+
+        return affKeyConditionFound ? Affinity.HAS_AFFINITY_CONDITION : Affinity.NONE;
+    }
+
+    /**
+     * @param f Table filter.
+     * @return Index.
+     */
+    private int indexOf(TableFilter f) {
+        for (int i = 0; i < childFilters.length; i++) {
+            if (childFilters[i] == f)
+                return i;
+        }
+
+        throw new IllegalStateException();
+    }
+
+    /**
+     * @param f Table filter.
+     * @param expCol Expression column.
+     * @return {@code true} It it is an affinity column.
+     */
+    private static boolean isAffinityColumn(TableFilter f, ExpressionColumn expCol) {
+        Column col = expCol.getColumn();
+
+        if (col == null)
+            return false;
+
+        Table t = col.getTable();
+
+        if (t.isView()) {
+            Query qry = getSubQuery(f);
+
+            return isAffinityColumn(qry, expCol);
+        }
+
+        return t instanceof GridH2Table &&
+            col.getColumnId() == ((GridH2Table)t).getAffinityKeyColumnId();
+    }
+
+    /**
+     * @param qry Query.
+     * @param expCol Expression column.
+     * @return {@code true} It it is an affinity column.
+     */
+    private static boolean isAffinityColumn(Query qry, ExpressionColumn expCol) {
+        if (qry.isUnion()) {
+            SelectUnion union = (SelectUnion)qry;
+
+            return isAffinityColumn(union.getLeft(), expCol) && isAffinityColumn(union.getRight(), expCol);
+        }
+
+        Expression exp = qry.getExpressions().get(expCol.getColumn().getColumnId()).getNonAliasExpression();
+
+        if (exp instanceof ExpressionColumn) {
+            expCol = (ExpressionColumn)exp;
+
+            return isAffinityColumn(expCol.getTableFilter(), expCol);
+        }
+
+        return false;
+    }
+
+    /**
+     * @return Multiplier.
+     */
+    public int calculateMultiplier() {
+        // We don't need multiplier for union here because it will be summarized in H2.
+        return multiplier(false);
+    }
+
+    /**
+     * @param withUnion With respect to union.
+     * @return Multiplier.
+     */
+    private int multiplier(boolean withUnion) {
+        calculate();
+
+        assert multiplier != 0;
+
+        if (withUnion && unions != null) {
+            int maxMultiplier = 0;
+
+            for (int i = 0; i < unions.size(); i++) {
+                int m = unions.get(i).multiplier(false);
+
+                if (m > maxMultiplier)
+                    maxMultiplier = m;
+            }
+
+            return maxMultiplier;
+        }
+
+        return multiplier;
+    }
+
+    /**
+     * @param withUnion With respect to union.
+     * @return Type.
+     */
+    private Type type(boolean withUnion) {
+        calculate();
+
+        assert type != null;
+
+        if (withUnion && unions != null) {
+            Type left = unions.get(0).type(false);
+
+            for (int i = 1; i < unions.size(); i++) {
+                Type right = unions.get(i).type(false);
+
+                if (!left.isCollocated() || !right.isCollocated()) {
+                    left = Type.PARTITIONED_NOT_COLLOCATED;
+
+                    break;
+                }
+                else if (!left.isPartitioned() && !right.isPartitioned())
+                    left = Type.REPLICATED;
+                else
+                    left = Type.PARTITIONED_COLLOCATED;
+            }
+
+            return left;
+        }
+
+        return type;
+    }
+
+    /**
+     * @param i Index.
+     * @param create Create child if needed.
+     * @return Child collocation.
+     */
+    private GridH2CollocationModel child(int i, boolean create) {
+        GridH2CollocationModel child = children[i];
+
+        if (child == null && create && isChildTableOrView(i, null)) {
+            TableFilter f = childFilters[i];
+
+            children[i] = child = f.getTable().isView() ?
+                buildCollocationModel(this, i, getSubQuery(f), null) :
+                createChildModel(this, i, null);
+        }
+
+        return child;
+    }
+
+    /**
+     * @param f Table filter.
+     * @return Sub-query.
+     */
+    private static Query getSubQuery(TableFilter f) {
+        return ((ViewIndex)f.getIndex()).getQuery();
+    }
+
+    /**
+     * @return Unions list.
+     */
+    private List<GridH2CollocationModel> getOrCreateUnions() {
+        if (unions == null) {
+            unions = new ArrayList<>(4);
+
+            unions.add(this);
+        }
+
+        return unions;
+    }
+
+    /**
+     * @param qctx Query context.
+     * @param info Sub-query info.
+     * @param filters Filters.
+     * @param filter Filter.
+     * @return Collocation.
+     */
+    public static GridH2CollocationModel buildCollocationModel(GridH2QueryContext qctx, SubQueryInfo info,
+        TableFilter[] filters, int filter) {
+        GridH2CollocationModel cm;
+
+        if (info != null) {
+            // Go up until we reach the root query.
+            cm = buildCollocationModel(qctx, info.getUpper(), info.getFilters(), info.getFilter());
+        }
+        else {
+            // We are at the root query.
+            cm = qctx.queryCollocationModel();
+
+            if (cm == null) {
+                cm = createChildModel(null, -1, null);
+
+                qctx.queryCollocationModel(cm);
+            }
+        }
+
+        Select select = filters[0].getSelect();
+
+        // Handle union. We have to rely on fact that select will be the same on uppermost select.
+        // For sub-queries we will drop collocation models, so that they will be recalculated anyways.
+        if (cm.select != null && cm.select != select) {
+            List<GridH2CollocationModel> unions = cm.getOrCreateUnions();
+
+            // Try to find this select in existing unions.
+            // Start with 1 because at 0 it always will be c.
+            for (int i = 1; i < unions.size(); i++) {
+                GridH2CollocationModel u = unions.get(i);
+
+                if (u.select == select) {
+                    cm = u;
+
+                    break;
+                }
+            }
+
+            // Nothing was found, need to create new child in union.
+            if (cm.select != select)
+                cm = createChildModel(cm.upper, cm.filter, unions);
+        }
+
+        cm.childFilters(filters);
+
+        return cm.child(filter, true);
+    }
+
+    /**
+     * @param qry Query.
+     * @return {@code true} If the query is collocated.
+     */
+    public static boolean isCollocated(Query qry) {
+        return buildCollocationModel(null, -1, qry, null).type(true).isCollocated();
+    }
+
+    /**
+     * @param upper Upper.
+     * @param filter Filter.
+     * @param qry Query.
+     * @param unions Unions.
+     * @return Built model.
+     */
+    private static GridH2CollocationModel buildCollocationModel(GridH2CollocationModel upper, int filter, Query qry,
+        List<GridH2CollocationModel> unions) {
+        if (qry.isUnion()) {
+            if (unions == null)
+                unions = new ArrayList<>();
+
+            SelectUnion union = (SelectUnion)qry;
+
+            GridH2CollocationModel a = buildCollocationModel(upper, filter, union.getLeft(), unions);
+            GridH2CollocationModel b = buildCollocationModel(upper, filter, union.getRight(), unions);
+
+            return a == null ? b : a;
+        }
+
+        Select select = (Select)qry;
+
+        List<TableFilter> list = new ArrayList<>();
+
+        for (TableFilter f = select.getTopTableFilter(); f != null; f = f.getJoin())
+            list.add(f);
+
+        TableFilter[] filters = list.toArray(new TableFilter[list.size()]);
+
+        GridH2CollocationModel cm = createChildModel(upper, filter, unions);
+
+        cm.childFilters(filters);
+
+        for (int i = 0; i < filters.length; i++) {
+            TableFilter f = filters[i];
+
+            if (f.getTable().isView())
+                buildCollocationModel(cm, i, getSubQuery(f), null);
+            else if (f.getTable() instanceof GridH2Table)
+                createChildModel(cm, i, null);
+        }
+
+        return upper == null ? cm : null;
+    }
+
+    /**
+     * Collocation type.
+     */
+    private enum Type {
+        /** */
+        PARTITIONED_COLLOCATED(true, true),
+
+        /** */
+        PARTITIONED_NOT_COLLOCATED(true, false),
+
+        /** */
+        REPLICATED(false, true);
+
+        /** */
+        private final boolean partitioned;
+
+        /** */
+        private final boolean collocated;
+
+        /**
+         * @param partitioned Partitioned.
+         * @param collocated Collocated.
+         */
+        Type(boolean partitioned, boolean collocated) {
+            this.partitioned = partitioned;
+            this.collocated = collocated;
+        }
+
+        /**
+         * @return {@code true} If partitioned.
+         */
+        public boolean isPartitioned() {
+            return partitioned;
+        }
+
+        /**
+         * @return {@code true} If collocated.
+         */
+        public boolean isCollocated() {
+            return collocated;
+        }
+
+        /**
+         * @param partitioned Partitioned.
+         * @param collocated Collocated.
+         * @return Type.
+         */
+        static Type of(boolean partitioned, boolean collocated) {
+            if (collocated)
+                return partitioned ? Type.PARTITIONED_COLLOCATED : Type.REPLICATED;
+
+            assert partitioned;
+
+            return Type.PARTITIONED_NOT_COLLOCATED;
+        }
+    }
+
+    /**
+     * Affinity of a table relative to previous joined tables.
+     */
+    private enum Affinity {
+        /** */
+        NONE,
+
+        /** */
+        HAS_AFFINITY_CONDITION,
+
+        /** */
+        JOINED_WITH_COLLOCATED
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/d86e0aeb/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
index b063528..20795bc 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2IndexBase.java
@@ -36,7 +36,7 @@ import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VAL_COL;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2Collocation.buildCollocationModel;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2CollocationModel.buildCollocationModel;
 import static org.apache.ignite.internal.processors.query.h2.opt.GridH2QueryType.PREPARE;
 
 /**
@@ -140,7 +140,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
         // and thus multiplier will be always the same, so it will not affect choice of index.
         // Query expressions can not be distributed as well.
         if (qctx == null || qctx.type() != PREPARE || !qctx.distributedJoins() || ses.isPreparingQueryExpression())
-            return GridH2Collocation.MULTIPLIER_COLLOCATED;
+            return GridH2CollocationModel.MULTIPLIER_COLLOCATED;
 
         // We have to clear this cache because normally sub-query plan cost does not depend on anything
         // other than index condition masks and sort order, but in our case it can depend on order
@@ -149,7 +149,7 @@ public abstract class GridH2IndexBase extends BaseIndex {
 
         assert filters != null;
 
-        GridH2Collocation c = buildCollocationModel(qctx, ses.getSubQueryInfo(), filters, filter);
+        GridH2CollocationModel c = buildCollocationModel(qctx, ses.getSubQueryInfo(), filters, filter);
 
         return c.calculateMultiplier();
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/d86e0aeb/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
index 29bbbaf..8812086 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2QueryContext.java
@@ -79,7 +79,7 @@ public class GridH2QueryContext {
     private int pageSize;
 
     /** */
-    private GridH2Collocation qryCollocation;
+    private GridH2CollocationModel qryCollocationModel;
 
     /**
      * @param locNodeId Local node ID.
@@ -115,15 +115,15 @@ public class GridH2QueryContext {
     /**
      * @return Query collocation model.
      */
-    public GridH2Collocation queryCollocation() {
-        return qryCollocation;
+    public GridH2CollocationModel queryCollocationModel() {
+        return qryCollocationModel;
     }
 
     /**
-     * @param qryCollocation Query collocation model.
+     * @param qryCollocationModel Query collocation model.
      */
-    public void queryCollocation(GridH2Collocation qryCollocation) {
-        this.qryCollocation = qryCollocation;
+    public void queryCollocationModel(GridH2CollocationModel qryCollocationModel) {
+        this.qryCollocationModel = qryCollocationModel;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/d86e0aeb/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
index 034eebe..0d41e95 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java
@@ -37,7 +37,7 @@ import org.h2.table.IndexColumn;
 import org.h2.util.IntArray;
 import org.jetbrains.annotations.Nullable;
 
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2Collocation.isCollocated;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2CollocationModel.isCollocated;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.AVG;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.CAST;
 import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.COUNT;