You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2018/11/13 13:25:12 UTC
ignite git commit: IGNITE-10231: SQL: moved partition extraction
logic into separate class to simplify further development. This closes #5374.
Repository: ignite
Updated Branches:
refs/heads/master 41bc714df -> 4ea3d4c3e
IGNITE-10231: SQL: moved partition extraction logic into separate class to simplify further development. This closes #5374.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/4ea3d4c3
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/4ea3d4c3
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/4ea3d4c3
Branch: refs/heads/master
Commit: 4ea3d4c3e3464de9cac7e05a639eee0aa57c22ed
Parents: 41bc714
Author: devozerov <vo...@gridgain.com>
Authored: Tue Nov 13 16:25:04 2018 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Tue Nov 13 16:25:04 2018 +0300
----------------------------------------------------------------------
.../cache/query/CacheQueryPartitionInfo.java | 143 -------
.../cache/query/GridCacheTwoStepQuery.java | 8 +-
.../processors/query/h2/IgniteH2Indexing.java | 8 +-
.../query/h2/affinity/PartitionExtractor.java | 384 +++++++++++++++++++
.../query/h2/affinity/PartitionInfo.java | 143 +++++++
.../query/h2/sql/GridSqlQuerySplitter.java | 338 +---------------
6 files changed, 539 insertions(+), 485 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea3d4c3/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/CacheQueryPartitionInfo.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/CacheQueryPartitionInfo.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/CacheQueryPartitionInfo.java
deleted file mode 100644
index 12e9a39..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/CacheQueryPartitionInfo.java
+++ /dev/null
@@ -1,143 +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.cache.query;
-
-import org.apache.ignite.internal.util.typedef.internal.S;
-
-/**
- * Holds the partition calculation info extracted from a query.
- * The query may have several such items associated with it.
- *
- * The query may contain expressions containing key or affinity key.
- * Such expressions can be used as hints to derive small isolated set
- * of partitions the query needs to run on.
- *
- * In case expression contains constant (e.g. _key = 100), the partition
- * can be calculated right away and saved into cache along with the query.
- *
- * In case expression has a parameter (e.g. _key = ?), the effective
- * partition varies with each run of the query. Hence, instead of partition,
- * one must store the info required to calculate partition.
- *
- * The given class holds the required info, so that effective partition
- * can be calculated during query parameter binding.
- */
-public class CacheQueryPartitionInfo {
- /** */
- private final int partId;
-
- /** */
- private final String cacheName;
-
- /** */
- private final String tableName;
-
- /** */
- private final int dataType;
-
- /** */
- private final int paramIdx;
-
- /**
- * @param partId Partition id, or -1 if parameter binding required.
- * @param cacheName Cache name required for partition calculation.
- * @param tableName Table name required for proper type conversion.
- * @param dataType Required data type id for the query parameter.
- * @param paramIdx Query parameter index required for partition calculation.
- */
- public CacheQueryPartitionInfo(int partId, String cacheName, String tableName, int dataType, int paramIdx) {
- // In case partition is not known, both cacheName and tableName must be provided.
- assert (partId >= 0) ^ ((cacheName != null) && (tableName != null));
-
- this.partId = partId;
- this.cacheName = cacheName;
- this.tableName = tableName;
- this.dataType = dataType;
- this.paramIdx = paramIdx;
- }
-
- /**
- * @return Partition id, or -1 if parameter binding is required to calculate partition.
- */
- public int partition() {
- return partId;
- }
-
- /**
- * @return Cache name required for partition calculation.
- */
- public String cacheName() {
- return cacheName;
- }
-
- /**
- * @return Table name.
- */
- public String tableName() {
- return tableName;
- }
-
- /**
- * @return Required data type for the query parameter.
- */
- public int dataType() {
- return dataType;
- }
-
- /**
- * @return Query parameter index required for partition calculation.
- */
- public int paramIdx() {
- return paramIdx;
- }
-
- /** {@inheritDoc} */
- @Override public int hashCode() {
- return partId ^ dataType ^ paramIdx ^
- (cacheName == null ? 0 : cacheName.hashCode()) ^
- (tableName == null ? 0 : tableName.hashCode());
- }
-
- /** {@inheritDoc} */
- @SuppressWarnings("SimplifiableIfStatement")
- @Override public boolean equals(Object obj) {
- if (this == obj)
- return true;
-
- if (!(obj instanceof CacheQueryPartitionInfo))
- return false;
-
- CacheQueryPartitionInfo other = (CacheQueryPartitionInfo)obj;
-
- if (partId >= 0)
- return partId == other.partId;
-
- if (other.cacheName == null || other.tableName == null)
- return false;
-
- return other.cacheName.equals(cacheName) &&
- other.tableName.equals(tableName) &&
- other.dataType == dataType &&
- other.paramIdx == paramIdx;
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return S.toString(CacheQueryPartitionInfo.class, this);
- }
-}
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea3d4c3/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
index a5f0ca2..526b37b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.processors.cache.query;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+
+import org.apache.ignite.internal.processors.query.h2.affinity.PartitionInfo;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -63,7 +65,7 @@ public class GridCacheTwoStepQuery {
private boolean local;
/** */
- private CacheQueryPartitionInfo[] derivedPartitions;
+ private PartitionInfo[] derivedPartitions;
/** */
private boolean mvccEnabled;
@@ -221,14 +223,14 @@ public class GridCacheTwoStepQuery {
/**
* @return Query derived partitions info.
*/
- public CacheQueryPartitionInfo[] derivedPartitions() {
+ public PartitionInfo[] derivedPartitions() {
return this.derivedPartitions;
}
/**
* @param derivedPartitions Query derived partitions info.
*/
- public void derivedPartitions(CacheQueryPartitionInfo[] derivedPartitions) {
+ public void derivedPartitions(PartitionInfo[] derivedPartitions) {
this.derivedPartitions = derivedPartitions;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea3d4c3/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
index 283e503..0cb3a21 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java
@@ -78,7 +78,7 @@ import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.mvcc.MvccUtils;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
-import org.apache.ignite.internal.processors.cache.query.CacheQueryPartitionInfo;
+import org.apache.ignite.internal.processors.query.h2.affinity.PartitionInfo;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryManager;
import org.apache.ignite.internal.processors.cache.query.GridCacheQueryMarshallable;
import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
@@ -3691,12 +3691,12 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @return Partitions.
* @throws IgniteCheckedException, If fails.
*/
- private int[] calculateQueryPartitions(CacheQueryPartitionInfo[] partInfoList, Object[] params)
+ private int[] calculateQueryPartitions(PartitionInfo[] partInfoList, Object[] params)
throws IgniteCheckedException {
ArrayList<Integer> list = new ArrayList<>(partInfoList.length);
- for (CacheQueryPartitionInfo partInfo: partInfoList) {
+ for (PartitionInfo partInfo: partInfoList) {
int partId = (partInfo.partition() >= 0) ? partInfo.partition() :
bindPartitionInfoParameter(partInfo, params);
@@ -3729,7 +3729,7 @@ public class IgniteH2Indexing implements GridQueryIndexing {
* @return Partition.
* @throws IgniteCheckedException, If fails.
*/
- private int bindPartitionInfoParameter(CacheQueryPartitionInfo partInfo, Object[] params)
+ private int bindPartitionInfoParameter(PartitionInfo partInfo, Object[] params)
throws IgniteCheckedException {
assert partInfo != null;
assert partInfo.partition() < 0;
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea3d4c3/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java
new file mode 100644
index 0000000..e76b211
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java
@@ -0,0 +1,384 @@
+/*
+ * 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.affinity;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlAst;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlColumn;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlElement;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperation;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlParameter;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuery;
+import org.apache.ignite.internal.processors.query.h2.sql.GridSqlSelect;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
+
+/**
+ * Utility class to extract partitions from the query.
+ */
+public class PartitionExtractor {
+ /**
+ * Ensures all given queries have non-empty derived partitions and merges them.
+ *
+ * @param queries Collection of queries.
+ * @return Derived partitions for all queries, or {@code null}.
+ */
+ public static PartitionInfo[] mergePartitionsFromMultipleQueries(List<GridCacheSqlQuery> queries) {
+ PartitionInfo[] result = null;
+
+ for (GridCacheSqlQuery qry : queries) {
+ PartitionInfo[] partInfo = (PartitionInfo[])qry.derivedPartitions();
+
+ if (partInfo == null) {
+ result = null;
+
+ break;
+ }
+
+ if (result == null)
+ result = partInfo;
+ else
+ result = mergePartitionInfo(result, partInfo);
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks if given query contains expressions over key or affinity key
+ * that make it possible to run it only on a small isolated
+ * set of partitions.
+ *
+ * @param qry Query.
+ * @param ctx Kernal context.
+ * @return Array of partitions, or {@code null} if none identified
+ */
+ public static PartitionInfo[] derivePartitionsFromQuery(GridSqlQuery qry, GridKernalContext ctx)
+ throws IgniteCheckedException {
+
+ // No unions support yet.
+ if (!(qry instanceof GridSqlSelect))
+ return null;
+
+ GridSqlSelect select = (GridSqlSelect)qry;
+
+ // no joins support yet.
+ if (select.from() == null || select.from().size() != 1)
+ return null;
+
+ return extractPartition(select.where(), ctx);
+ }
+
+ /**
+ * @param el AST element to start with.
+ * @param ctx Kernal context.
+ * @return Array of partition info objects, or {@code null} if none identified
+ */
+ private static PartitionInfo[] extractPartition(GridSqlAst el, GridKernalContext ctx)
+ throws IgniteCheckedException {
+
+ if (!(el instanceof GridSqlOperation))
+ return null;
+
+ GridSqlOperation op = (GridSqlOperation)el;
+
+ switch (op.operationType()) {
+ case EQUAL: {
+ PartitionInfo partInfo = extractPartitionFromEquality(op, ctx);
+
+ if (partInfo != null)
+ return new PartitionInfo[] { partInfo };
+
+ return null;
+ }
+
+ case AND: {
+ assert op.size() == 2;
+
+ PartitionInfo[] partsLeft = extractPartition(op.child(0), ctx);
+ PartitionInfo[] partsRight = extractPartition(op.child(1), ctx);
+
+ if (partsLeft != null && partsRight != null)
+ return null; //kind of conflict (_key = 1) and (_key = 2)
+
+ if (partsLeft != null)
+ return partsLeft;
+
+ if (partsRight != null)
+ return partsRight;
+
+ return null;
+ }
+
+ case OR: {
+ assert op.size() == 2;
+
+ PartitionInfo[] partsLeft = extractPartition(op.child(0), ctx);
+ PartitionInfo[] partsRight = extractPartition(op.child(1), ctx);
+
+ if (partsLeft != null && partsRight != null)
+ return mergePartitionInfo(partsLeft, partsRight);
+
+ return null;
+ }
+
+ case IN: {
+ // Operation should contain at least two children: left (column) and right (const or column).
+ if (op.size() < 2)
+ return null;
+
+ // Left operand should be column.
+ GridSqlAst left = op.child();
+
+ GridSqlColumn leftCol;
+
+ if (left instanceof GridSqlColumn)
+ leftCol = (GridSqlColumn)left;
+ else
+ return null;
+
+ // Can work only with Ignite's tables.
+ if (!(leftCol.column().getTable() instanceof GridH2Table))
+ return null;
+
+ PartitionInfo[] res = new PartitionInfo[op.size() - 1];
+
+ for (int i = 1; i < op.size(); i++) {
+ GridSqlAst right = op.child(i);
+
+ GridSqlConst rightConst;
+ GridSqlParameter rightParam;
+
+ if (right instanceof GridSqlConst) {
+ rightConst = (GridSqlConst)right;
+ rightParam = null;
+ }
+ else if (right instanceof GridSqlParameter) {
+ rightConst = null;
+ rightParam = (GridSqlParameter)right;
+ }
+ else
+ // One of members of "IN" list is neither const, nor param, so we do no know it's partition.
+ // As this is disjunction, not knowing partition of a single element leads to unknown partition
+ // set globally. Hence, returning null.
+ return null;
+
+ PartitionInfo cur = getCacheQueryPartitionInfo(
+ leftCol.column(),
+ rightConst,
+ rightParam,
+ ctx
+ );
+
+ // Same thing as above: single unknown partition in disjunction defeats optimization.
+ if (cur == null)
+ return null;
+
+ res[i - 1] = cur;
+ }
+
+ return res;
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Analyses the equality operation and extracts the partition if possible
+ *
+ * @param op AST equality operation.
+ * @param ctx Kernal Context.
+ * @return partition info, or {@code null} if none identified
+ */
+ private static PartitionInfo extractPartitionFromEquality(GridSqlOperation op, GridKernalContext ctx)
+ throws IgniteCheckedException {
+
+ assert op.operationType() == GridSqlOperationType.EQUAL;
+
+ GridSqlElement left = op.child(0);
+ GridSqlElement right = op.child(1);
+
+ GridSqlColumn leftCol;
+
+ if (left instanceof GridSqlColumn)
+ leftCol = (GridSqlColumn)left;
+ else
+ return null;
+
+ if (!(leftCol.column().getTable() instanceof GridH2Table))
+ return null;
+
+ GridSqlConst rightConst;
+ GridSqlParameter rightParam;
+
+ if (right instanceof GridSqlConst) {
+ rightConst = (GridSqlConst)right;
+ rightParam = null;
+ }
+ else if (right instanceof GridSqlParameter) {
+ rightConst = null;
+ rightParam = (GridSqlParameter)right;
+ }
+ else
+ return null;
+
+ return getCacheQueryPartitionInfo(leftCol.column(), rightConst, rightParam, ctx);
+ }
+
+ /**
+ * Merges two partition info arrays, removing duplicates
+ *
+ * @param a Partition info array.
+ * @param b Partition info array.
+ * @return Result.
+ */
+ private static PartitionInfo[] mergePartitionInfo(PartitionInfo[] a, PartitionInfo[] b) {
+ assert a != null;
+ assert b != null;
+
+ if (a.length == 1 && b.length == 1) {
+ if (a[0].equals(b[0]))
+ return new PartitionInfo[] { a[0] };
+
+ return new PartitionInfo[] { a[0], b[0] };
+ }
+
+ ArrayList<PartitionInfo> list = new ArrayList<>(a.length + b.length);
+
+ Collections.addAll(list, a);
+
+ for (PartitionInfo part: b) {
+ int i = 0;
+
+ while (i < list.size() && !list.get(i).equals(part))
+ i++;
+
+ if (i == list.size())
+ list.add(part);
+ }
+
+ PartitionInfo[] result = new PartitionInfo[list.size()];
+
+ for (int i = 0; i < list.size(); i++)
+ result[i] = list.get(i);
+
+ return result;
+ }
+
+ /**
+ * Extracts the partition if possible
+ * @param leftCol Column on the lsft side.
+ * @param rightConst Constant on the right side.
+ * @param rightParam Parameter on the right side.
+ * @param ctx Kernal Context.
+ * @return partition info, or {@code null} if none identified
+ * @throws IgniteCheckedException If failed.
+ */
+ @Nullable
+ private static PartitionInfo getCacheQueryPartitionInfo(
+ Column leftCol,
+ GridSqlConst rightConst,
+ GridSqlParameter rightParam,
+ GridKernalContext ctx
+ ) throws IgniteCheckedException {
+ assert leftCol != null;
+ assert leftCol.getTable() != null;
+ assert leftCol.getTable() instanceof GridH2Table;
+
+ GridH2Table tbl = (GridH2Table)leftCol.getTable();
+
+ if (!isAffinityKey(leftCol.getColumnId(), tbl))
+ return null;
+
+ GridH2RowDescriptor desc = tbl.rowDescriptor();
+
+ IndexColumn affKeyCol = tbl.getAffinityKeyColumn();
+
+ int colId = leftCol.getColumnId();
+
+ if ((affKeyCol == null || colId != affKeyCol.column.getColumnId()) && !desc.isKeyColumn(colId))
+ return null;
+
+ if (rightConst != null) {
+ int part = ctx.affinity().partition(tbl.cacheName(), rightConst.value().getObject());
+
+ return new PartitionInfo(
+ part,
+ null,
+ null,
+ -1,
+ -1
+ );
+ }
+ else if (rightParam != null) {
+ return new PartitionInfo(
+ -1,
+ tbl.cacheName(),
+ tbl.getName(),
+ leftCol.getType(),
+ rightParam.index()
+ );
+ }
+ else
+ return null;
+ }
+
+ /**
+ *
+ * @param colId Column ID to check
+ * @param tbl H2 Table
+ * @return is affinity key or not
+ */
+ private static boolean isAffinityKey(int colId, GridH2Table tbl) {
+ GridH2RowDescriptor desc = tbl.rowDescriptor();
+
+ if (desc.isKeyColumn(colId))
+ return true;
+
+ IndexColumn affKeyCol = tbl.getAffinityKeyColumn();
+
+ try {
+ return affKeyCol != null && colId >= DEFAULT_COLUMNS_COUNT && desc.isColumnKeyProperty(colId - DEFAULT_COLUMNS_COUNT) && colId == affKeyCol.column.getColumnId();
+ }
+ catch(IllegalStateException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Private constructor.
+ */
+ private PartitionExtractor() {
+ // No-op.
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea3d4c3/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionInfo.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionInfo.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionInfo.java
new file mode 100644
index 0000000..a6c8dba
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionInfo.java
@@ -0,0 +1,143 @@
+/*
+ * 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.affinity;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+/**
+ * Holds the partition calculation info extracted from a query.
+ * The query may have several such items associated with it.
+ *
+ * The query may contain expressions containing key or affinity key.
+ * Such expressions can be used as hints to derive small isolated set
+ * of partitions the query needs to run on.
+ *
+ * In case expression contains constant (e.g. _key = 100), the partition
+ * can be calculated right away and saved into cache along with the query.
+ *
+ * In case expression has a parameter (e.g. _key = ?), the effective
+ * partition varies with each run of the query. Hence, instead of partition,
+ * one must store the info required to calculate partition.
+ *
+ * The given class holds the required info, so that effective partition
+ * can be calculated during query parameter binding.
+ */
+public class PartitionInfo {
+ /** */
+ private final int partId;
+
+ /** */
+ private final String cacheName;
+
+ /** */
+ private final String tableName;
+
+ /** */
+ private final int dataType;
+
+ /** */
+ private final int paramIdx;
+
+ /**
+ * @param partId Partition id, or -1 if parameter binding required.
+ * @param cacheName Cache name required for partition calculation.
+ * @param tableName Table name required for proper type conversion.
+ * @param dataType Required data type id for the query parameter.
+ * @param paramIdx Query parameter index required for partition calculation.
+ */
+ public PartitionInfo(int partId, String cacheName, String tableName, int dataType, int paramIdx) {
+ // In case partition is not known, both cacheName and tableName must be provided.
+ assert (partId >= 0) ^ ((cacheName != null) && (tableName != null));
+
+ this.partId = partId;
+ this.cacheName = cacheName;
+ this.tableName = tableName;
+ this.dataType = dataType;
+ this.paramIdx = paramIdx;
+ }
+
+ /**
+ * @return Partition id, or -1 if parameter binding is required to calculate partition.
+ */
+ public int partition() {
+ return partId;
+ }
+
+ /**
+ * @return Cache name required for partition calculation.
+ */
+ public String cacheName() {
+ return cacheName;
+ }
+
+ /**
+ * @return Table name.
+ */
+ public String tableName() {
+ return tableName;
+ }
+
+ /**
+ * @return Required data type for the query parameter.
+ */
+ public int dataType() {
+ return dataType;
+ }
+
+ /**
+ * @return Query parameter index required for partition calculation.
+ */
+ public int paramIdx() {
+ return paramIdx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return partId ^ dataType ^ paramIdx ^
+ (cacheName == null ? 0 : cacheName.hashCode()) ^
+ (tableName == null ? 0 : tableName.hashCode());
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("SimplifiableIfStatement")
+ @Override public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (!(obj instanceof PartitionInfo))
+ return false;
+
+ PartitionInfo other = (PartitionInfo)obj;
+
+ if (partId >= 0)
+ return partId == other.partId;
+
+ if (other.cacheName == null || other.tableName == null)
+ return false;
+
+ return other.cacheName.equals(cacheName) &&
+ other.tableName.equals(tableName) &&
+ other.dataType == dataType &&
+ other.paramIdx == paramIdx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(PartitionInfo.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/4ea3d4c3/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 9987a0d..bbf10ff 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
@@ -35,7 +35,6 @@ import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.GridKernalContext;
-import org.apache.ignite.internal.processors.cache.query.CacheQueryPartitionInfo;
import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery;
import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
@@ -43,8 +42,7 @@ import org.apache.ignite.internal.processors.cache.query.QueryTable;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.h2.H2Utils;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.affinity.PartitionExtractor;
import org.apache.ignite.internal.util.lang.GridTreePrinter;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.F;
@@ -53,13 +51,10 @@ import org.apache.ignite.internal.util.typedef.internal.S;
import org.h2.command.Prepared;
import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
-import org.h2.table.Column;
-import org.h2.table.IndexColumn;
import org.h2.value.Value;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.query.h2.opt.GridH2CollocationModel.isCollocated;
-import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst.TRUE;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.AVG;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.CAST;
@@ -266,7 +261,7 @@ public class GridSqlQuerySplitter {
twoStepQry.distributedJoins(distributedJoins);
// all map queries must have non-empty derivedPartitions to use this feature.
- twoStepQry.derivedPartitions(mergePartitionsFromMultipleQueries(twoStepQry.mapQueries()));
+ twoStepQry.derivedPartitions(PartitionExtractor.mergePartitionsFromMultipleQueries(twoStepQry.mapQueries()));
twoStepQry.forUpdate(forUpdate);
@@ -1554,7 +1549,7 @@ public class GridSqlQuerySplitter {
map.hasSubQueries(hasSubQueries);
if (map.isPartitioned())
- map.derivedPartitions(derivePartitionsFromQuery(mapQry, ctx));
+ map.derivedPartitions(PartitionExtractor.derivePartitionsFromQuery(mapQry, ctx));
mapSqlQrys.add(map);
}
@@ -2267,306 +2262,6 @@ public class GridSqlQuerySplitter {
}
/**
- * Checks if given query contains expressions over key or affinity key
- * that make it possible to run it only on a small isolated
- * set of partitions.
- *
- * @param qry Query.
- * @param ctx Kernal context.
- * @return Array of partitions, or {@code null} if none identified
- */
- private static CacheQueryPartitionInfo[] derivePartitionsFromQuery(GridSqlQuery qry, GridKernalContext ctx)
- throws IgniteCheckedException {
-
- if (!(qry instanceof GridSqlSelect))
- return null;
-
- GridSqlSelect select = (GridSqlSelect)qry;
-
- // no joins support yet
- if (select.from() == null || select.from().size() != 1)
- return null;
-
- return extractPartition(select.where(), ctx);
- }
-
- /**
- * @param el AST element to start with.
- * @param ctx Kernal context.
- * @return Array of partition info objects, or {@code null} if none identified
- */
- private static CacheQueryPartitionInfo[] extractPartition(GridSqlAst el, GridKernalContext ctx)
- throws IgniteCheckedException {
-
- if (!(el instanceof GridSqlOperation))
- return null;
-
- GridSqlOperation op = (GridSqlOperation)el;
-
- switch (op.operationType()) {
- case EQUAL: {
- CacheQueryPartitionInfo partInfo = extractPartitionFromEquality(op, ctx);
-
- if (partInfo != null)
- return new CacheQueryPartitionInfo[] { partInfo };
-
- return null;
- }
-
- case AND: {
- assert op.size() == 2;
-
- CacheQueryPartitionInfo[] partsLeft = extractPartition(op.child(0), ctx);
- CacheQueryPartitionInfo[] partsRight = extractPartition(op.child(1), ctx);
-
- if (partsLeft != null && partsRight != null)
- return null; //kind of conflict (_key = 1) and (_key = 2)
-
- if (partsLeft != null)
- return partsLeft;
-
- if (partsRight != null)
- return partsRight;
-
- return null;
- }
-
- case OR: {
- assert op.size() == 2;
-
- CacheQueryPartitionInfo[] partsLeft = extractPartition(op.child(0), ctx);
- CacheQueryPartitionInfo[] partsRight = extractPartition(op.child(1), ctx);
-
- if (partsLeft != null && partsRight != null)
- return mergePartitionInfo(partsLeft, partsRight);
-
- return null;
- }
-
- case IN: {
- // Operation should contain at least two children: left (column) and right (const or column).
- if (op.size() < 2)
- return null;
-
- // Left operand should be column.
- GridSqlAst left = op.child();
-
- GridSqlColumn leftCol;
-
- if (left instanceof GridSqlColumn)
- leftCol = (GridSqlColumn)left;
- else
- return null;
-
- // Can work only with Ignite's tables.
- if (!(leftCol.column().getTable() instanceof GridH2Table))
- return null;
-
- CacheQueryPartitionInfo[] res = new CacheQueryPartitionInfo[op.size() - 1];
-
- for (int i = 1; i < op.size(); i++) {
- GridSqlAst right = op.child(i);
-
- GridSqlConst rightConst;
- GridSqlParameter rightParam;
-
- if (right instanceof GridSqlConst) {
- rightConst = (GridSqlConst)right;
- rightParam = null;
- }
- else if (right instanceof GridSqlParameter) {
- rightConst = null;
- rightParam = (GridSqlParameter)right;
- }
- else
- // One of members of "IN" list is neither const, nor param, so we do no know it's partition.
- // As this is disjunction, not knowing partition of a single element leads to unknown partition
- // set globally. Hence, returning null.
- return null;
-
- CacheQueryPartitionInfo cur = getCacheQueryPartitionInfo(
- leftCol.column(),
- rightConst,
- rightParam,
- ctx
- );
-
- // Same thing as above: single unknown partition in disjunction defeats optimization.
- if (cur == null)
- return null;
-
- res[i - 1] = cur;
- }
-
- return res;
- }
-
- default:
- return null;
- }
- }
-
- /**
- * Analyses the equality operation and extracts the partition if possible
- *
- * @param op AST equality operation.
- * @param ctx Kernal Context.
- * @return partition info, or {@code null} if none identified
- */
- private static CacheQueryPartitionInfo extractPartitionFromEquality(GridSqlOperation op, GridKernalContext ctx)
- throws IgniteCheckedException {
-
- assert op.operationType() == GridSqlOperationType.EQUAL;
-
- GridSqlElement left = op.child(0);
- GridSqlElement right = op.child(1);
-
- GridSqlColumn leftCol;
-
- if (left instanceof GridSqlColumn)
- leftCol = (GridSqlColumn)left;
- else
- return null;
-
- if (!(leftCol.column().getTable() instanceof GridH2Table))
- return null;
-
- GridSqlConst rightConst;
- GridSqlParameter rightParam;
-
- if (right instanceof GridSqlConst) {
- rightConst = (GridSqlConst)right;
- rightParam = null;
- }
- else if (right instanceof GridSqlParameter) {
- rightConst = null;
- rightParam = (GridSqlParameter)right;
- }
- else
- return null;
-
- return getCacheQueryPartitionInfo(leftCol.column(), rightConst, rightParam, ctx);
- }
-
- /**
- * Extracts the partition if possible
- * @param leftCol Column on the lsft side.
- * @param rightConst Constant on the right side.
- * @param rightParam Parameter on the right side.
- * @param ctx Kernal Context.
- * @return partition info, or {@code null} if none identified
- * @throws IgniteCheckedException If failed.
- */
- @Nullable private static CacheQueryPartitionInfo getCacheQueryPartitionInfo(
- Column leftCol,
- GridSqlConst rightConst,
- GridSqlParameter rightParam,
- GridKernalContext ctx
- ) throws IgniteCheckedException {
- assert leftCol != null;
- assert leftCol.getTable() != null;
- assert leftCol.getTable() instanceof GridH2Table;
-
- GridH2Table tbl = (GridH2Table)leftCol.getTable();
-
- if (!isAffinityKey(leftCol.getColumnId(), tbl))
- return null;
-
- GridH2RowDescriptor desc = tbl.rowDescriptor();
-
- IndexColumn affKeyCol = tbl.getAffinityKeyColumn();
-
- int colId = leftCol.getColumnId();
-
- if ((affKeyCol == null || colId != affKeyCol.column.getColumnId()) && !desc.isKeyColumn(colId))
- return null;
-
- if (rightConst != null) {
- int part = ctx.affinity().partition(tbl.cacheName(), rightConst.value().getObject());
-
- return new CacheQueryPartitionInfo(
- part,
- null,
- null,
- -1,
- -1
- );
- }
- else if (rightParam != null) {
- return new CacheQueryPartitionInfo(
- -1,
- tbl.cacheName(),
- tbl.getName(),
- leftCol.getType(),
- rightParam.index()
- );
- }
- else
- return null;
- }
-
- /**
- *
- * @param colId Column ID to check
- * @param tbl H2 Table
- * @return is affinity key or not
- */
- private static boolean isAffinityKey(int colId, GridH2Table tbl) {
- GridH2RowDescriptor desc = tbl.rowDescriptor();
-
- if (desc.isKeyColumn(colId))
- return true;
-
- IndexColumn affKeyCol = tbl.getAffinityKeyColumn();
-
- try {
- return affKeyCol != null && colId >= DEFAULT_COLUMNS_COUNT && desc.isColumnKeyProperty(colId - DEFAULT_COLUMNS_COUNT) && colId == affKeyCol.column.getColumnId();
- } catch(IllegalStateException e) {
- return false;
- }
- }
-
- /**
- * Merges two partition info arrays, removing duplicates
- *
- * @param a Partition info array.
- * @param b Partition info array.
- * @return Result.
- */
- private static CacheQueryPartitionInfo[] mergePartitionInfo(CacheQueryPartitionInfo[] a, CacheQueryPartitionInfo[] b) {
- assert a != null;
- assert b != null;
-
- if (a.length == 1 && b.length == 1) {
- if (a[0].equals(b[0]))
- return new CacheQueryPartitionInfo[] { a[0] };
-
- return new CacheQueryPartitionInfo[] { a[0], b[0] };
- }
-
- ArrayList<CacheQueryPartitionInfo> list = new ArrayList<>(a.length + b.length);
-
- Collections.addAll(list, a);
-
- for (CacheQueryPartitionInfo part: b) {
- int i = 0;
-
- while (i < list.size() && !list.get(i).equals(part))
- i++;
-
- if (i == list.size())
- list.add(part);
- }
-
- CacheQueryPartitionInfo[] result = new CacheQueryPartitionInfo[list.size()];
-
- for (int i = 0; i < list.size(); i++)
- result[i] = list.get(i);
-
- return result;
- }
-
- /**
* @param root Root model.
* @return Tree as a string.
*/
@@ -2602,33 +2297,6 @@ public class GridSqlQuerySplitter {
}
/**
- * Ensures all given queries have non-empty derived partitions and merges them.
- *
- * @param queries Collection of queries.
- * @return Derived partitions for all queries, or {@code null}.
- */
- private static CacheQueryPartitionInfo[] mergePartitionsFromMultipleQueries(List<GridCacheSqlQuery> queries) {
- CacheQueryPartitionInfo[] result = null;
-
- for (GridCacheSqlQuery qry : queries) {
- CacheQueryPartitionInfo[] partInfo = (CacheQueryPartitionInfo[])qry.derivedPartitions();
-
- if (partInfo == null) {
- result = null;
-
- break;
- }
-
- if (result == null)
- result = partInfo;
- else
- result = mergePartitionInfo(result, partInfo);
- }
-
- return result;
- }
-
- /**
* Simplified tree-like model for a query.
* - SELECT : All the children are list of joined query models in the FROM clause.
* - UNION : All the children are united left and right query models.