Queries optimizations: - cache prepared statements and two-step queries - skip reduce query if possible - do not use index snapshots by default
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/4af461a7 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/4af461a7 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/4af461a7 Branch: refs/heads/ignite-1282 Commit: 4af461a7f403fc0e4df13d23d0789eee49200b26 Parents: 3c42da8 Author: sboikov <sboi...@gridgain.com> Authored: Thu Nov 26 09:30:10 2015 +0300 Committer: sboikov <sboi...@gridgain.com> Committed: Thu Nov 26 09:30:10 2015 +0300 ---------------------------------------------------------------------- .../configuration/CacheConfiguration.java | 30 +++ .../cache/query/GridCacheSqlQuery.java | 42 ++++ .../cache/query/GridCacheTwoStepQuery.java | 35 ++- .../processors/query/GridQueryProcessor.java | 40 +--- .../processors/query/GridQueryProperty.java | 45 ++++ .../query/GridQueryTypeDescriptor.java | 6 + .../query/h2/GridH2ResultSetIterator.java | 19 +- .../processors/query/h2/IgniteH2Indexing.java | 227 ++++++++++++++++--- .../query/h2/opt/GridH2RowDescriptor.java | 5 + .../processors/query/h2/opt/GridH2Table.java | 69 +++++- .../query/h2/opt/GridH2TreeIndex.java | 98 +++++--- .../query/h2/sql/GridSqlQuerySplitter.java | 73 ++++-- .../processors/query/h2/sql/GridSqlSelect.java | 27 +++ .../query/h2/twostep/GridMapQueryExecutor.java | 17 +- .../query/h2/twostep/GridMergeIndex.java | 7 + .../h2/twostep/GridMergeIndexUnsorted.java | 14 ++ .../h2/twostep/GridReduceQueryExecutor.java | 117 +++++++--- .../cache/IgniteCacheAbstractQuerySelfTest.java | 69 +++--- ...PartitionedSnapshotEnabledQuerySelfTest.java | 28 +++ .../h2/GridIndexingSpiAbstractSelfTest.java | 19 ++ .../IgniteCacheQuerySelfTestSuite.java | 2 + 21 files changed, 776 insertions(+), 213 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java index e87346f..e2bf912 100644 --- a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java +++ b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java @@ -373,6 +373,9 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { /** */ private int sqlOnheapRowCacheSize = DFLT_SQL_ONHEAP_ROW_CACHE_SIZE; + /** */ + private boolean snapshotableIdx; + /** Copy on read flag. */ private boolean cpOnRead = DFLT_COPY_ON_READ; @@ -463,6 +466,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { rebalancePoolSize = cc.getRebalanceThreadPoolSize(); rebalanceTimeout = cc.getRebalanceTimeout(); rebalanceThrottle = cc.getRebalanceThrottle(); + snapshotableIdx = cc.isSnapshotableIndex(); sqlEscapeAll = cc.isSqlEscapeAll(); sqlFuncCls = cc.getSqlFunctionClasses(); sqlOnheapRowCacheSize = cc.getSqlOnheapRowCacheSize(); @@ -1900,6 +1904,32 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> { } /** + * Gets flag indicating whether SQL indexes should support snapshots. + * + * @return {@code True} if SQL indexes should support snapshots. + */ + public boolean isSnapshotableIndex() { + return snapshotableIdx; + } + + /** + * Sets flag indicating whether SQL indexes should support snapshots. + * <p> + * Default value is {@code false}. + * <p> + * <b>Note</b> that this flag is ignored if indexes are stored in offheap memory, + * for offheap indexes snapshots are always enabled. + * + * @param snapshotableIdx {@code True} if SQL indexes should support snapshots. + * @return {@code this} for chaining. + */ + public CacheConfiguration<K, V> setSnapshotableIndex(boolean snapshotableIdx) { + this.snapshotableIdx = snapshotableIdx; + + return this; + } + + /** * Gets array of cache plugin configurations. * * @return Cache plugin configurations. http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java index be7bbe9..e56e445 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheSqlQuery.java @@ -55,6 +55,16 @@ public class GridCacheSqlQuery implements Message { /** */ @GridToStringInclude @GridDirectTransient + private int[] paramIdxs; + + /** */ + @GridToStringInclude + @GridDirectTransient + private int paramsSize; + + /** */ + @GridToStringInclude + @GridDirectTransient private LinkedHashMap<String, ?> cols; /** Field kept for backward compatibility. */ @@ -77,6 +87,14 @@ public class GridCacheSqlQuery implements Message { this.qry = qry; this.params = F.isEmpty(params) ? EMPTY_PARAMS : params; + paramsSize = this.params.length; + } + + /** + * @param paramIdxs Parameter indexes. + */ + public void parameterIndexes(int[] paramIdxs) { + this.paramIdxs = paramIdxs; } /** @@ -222,4 +240,28 @@ public class GridCacheSqlQuery implements Message { @Override public byte fieldsCount() { return 3; } + + /** + * @param args Arguments. + * @return Copy. + */ + public GridCacheSqlQuery copy(Object[] args) { + GridCacheSqlQuery cp = new GridCacheSqlQuery(); + + cp.qry = qry; + cp.cols = cols; + cp.paramIdxs = paramIdxs; + cp.paramsSize = paramsSize; + + if (F.isEmpty(args)) + cp.params = EMPTY_PARAMS; + else { + cp.params = new Object[paramsSize]; + + for (int paramIdx : paramIdxs) + cp.params[paramIdx] = args[paramIdx]; + } + + return cp; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java index 4b5fe22..da59c18 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/query/GridCacheTwoStepQuery.java @@ -47,16 +47,27 @@ public class GridCacheTwoStepQuery { /** */ private Set<String> spaces; + /** */ + private final boolean skipMergeTbl; + /** * @param spaces All spaces accessed in query. * @param rdc Reduce query. + * @param skipMergeTbl {@code True} if reduce query can skip merge table creation and + * get data directly from merge index. */ - public GridCacheTwoStepQuery(Set<String> spaces, GridCacheSqlQuery rdc) { + public GridCacheTwoStepQuery(Set<String> spaces, GridCacheSqlQuery rdc, boolean skipMergeTbl) { assert rdc != null; this.spaces = spaces; - this.rdc = rdc; + this.skipMergeTbl = skipMergeTbl; + } + /** + * @return {@code True} if reduce query can skip merge table creation and get data directly from merge index. + */ + public boolean skipMergeTable() { + return skipMergeTbl; } /** @@ -89,9 +100,12 @@ public class GridCacheTwoStepQuery { /** * @param qry SQL Query. + * @return {@code this}. */ - public void addMapQuery(GridCacheSqlQuery qry) { + public GridCacheTwoStepQuery addMapQuery(GridCacheSqlQuery qry) { mapQrys.add(qry); + + return this; } /** @@ -122,6 +136,21 @@ public class GridCacheTwoStepQuery { this.spaces = spaces; } + /** + * @param args New arguments to copy with. + * @return Copy. + */ + public GridCacheTwoStepQuery copy(Object[] args) { + assert !explain; + + GridCacheTwoStepQuery cp = new GridCacheTwoStepQuery(spaces, rdc.copy(args), skipMergeTbl); + cp.pageSize = pageSize; + for (int i = 0; i < mapQrys.size(); i++) + cp.mapQrys.add(mapQrys.get(i).copy(args)); + + return cp; + } + /** {@inheritDoc} */ @Override public String toString() { return S.toString(GridCacheTwoStepQuery.class, this); http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java index d0eeeb1..814229c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java @@ -1672,34 +1672,9 @@ public class GridQueryProcessor extends GridProcessorAdapter { } /** - * - */ - private abstract static class Property { - /** - * Gets this property value from the given object. - * - * @param key Key. - * @param val Value. - * @return Property value. - * @throws IgniteCheckedException If failed. - */ - public abstract Object value(Object key, Object val) throws IgniteCheckedException; - - /** - * @return Property name. - */ - public abstract String name(); - - /** - * @return Class member type. - */ - public abstract Class<?> type(); - } - - /** * Description of type property. */ - private static class ClassProperty extends Property { + private static class ClassProperty extends GridQueryProperty { /** */ private final Member member; @@ -1794,7 +1769,7 @@ public class GridQueryProcessor extends GridProcessorAdapter { /** * */ - private class PortableProperty extends Property { + private class PortableProperty extends GridQueryProperty { /** Property name. */ private String propName; @@ -1938,7 +1913,7 @@ public class GridQueryProcessor extends GridProcessorAdapter { /** */ @GridToStringExclude - private final Map<String, Property> props = new HashMap<>(); + private final Map<String, GridQueryProperty> props = new HashMap<>(); /** */ @GridToStringInclude @@ -1993,11 +1968,16 @@ public class GridQueryProcessor extends GridProcessorAdapter { } /** {@inheritDoc} */ + @Override public GridQueryProperty property(String name) { + return props.get(name); + } + + /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> T value(String field, Object key, Object val) throws IgniteCheckedException { assert field != null; - Property prop = props.get(field); + GridQueryProperty prop = props.get(field); if (prop == null) throw new IgniteCheckedException("Failed to find field '" + field + "' in type '" + name + "'."); @@ -2096,7 +2076,7 @@ public class GridQueryProcessor extends GridProcessorAdapter { * @param failOnDuplicate Fail on duplicate flag. * @throws IgniteCheckedException In case of error. */ - public void addProperty(Property prop, boolean failOnDuplicate) throws IgniteCheckedException { + public void addProperty(GridQueryProperty prop, boolean failOnDuplicate) throws IgniteCheckedException { String name = prop.name(); if (props.put(name, prop) != null && failOnDuplicate) http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java new file mode 100644 index 0000000..d623d25 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java @@ -0,0 +1,45 @@ +/* + * 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; + +import org.apache.ignite.IgniteCheckedException; + +/** + * Description and access method for query entity field. + */ +public abstract class GridQueryProperty { + /** + * Gets this property value from the given object. + * + * @param key Key. + * @param val Value. + * @return Property value. + * @throws IgniteCheckedException If failed. + */ + public abstract Object value(Object key, Object val) throws IgniteCheckedException; + + /** + * @return Property name. + */ + public abstract String name(); + + /** + * @return Class member type. + */ + public abstract Class<?> type(); +} http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java index b05e1d8..45919ef 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java @@ -50,6 +50,12 @@ public interface GridQueryTypeDescriptor { public <T> T value(String field, Object key, Object val) throws IgniteCheckedException; /** + * @param name Property name. + * @return Property. + */ + public GridQueryProperty property(String name); + + /** * Gets indexes for this type. * * @return Indexes for this type. http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/GridH2ResultSetIterator.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/GridH2ResultSetIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/GridH2ResultSetIterator.java index 2c67638..3603bb5 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/GridH2ResultSetIterator.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/GridH2ResultSetIterator.java @@ -41,14 +41,19 @@ public abstract class GridH2ResultSetIterator<T> extends GridCloseableIteratorAd protected final Object[] row; /** */ + private final boolean closeStmt; + + /** */ private boolean hasRow; /** * @param data Data array. + * @param closeStmt If {@code true} closes result set statement when iterator is closed. * @throws IgniteCheckedException If failed. */ - protected GridH2ResultSetIterator(ResultSet data) throws IgniteCheckedException { + protected GridH2ResultSetIterator(ResultSet data, boolean closeStmt) throws IgniteCheckedException { this.data = data; + this.closeStmt = closeStmt; if (data != null) { try { @@ -115,11 +120,13 @@ public abstract class GridH2ResultSetIterator<T> extends GridCloseableIteratorAd // Nothing to close. return; - try { - U.closeQuiet(data.getStatement()); - } - catch (SQLException e) { - throw new IgniteCheckedException(e); + if (closeStmt) { + try { + U.closeQuiet(data.getStatement()); + } + catch (SQLException e) { + throw new IgniteCheckedException(e); + } } U.closeQuiet(data); http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/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 d5efebf..cc452c1 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 @@ -79,6 +79,7 @@ import org.apache.ignite.internal.processors.query.GridQueryFieldsResult; import org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter; import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; import org.apache.ignite.internal.processors.query.GridQueryIndexing; +import org.apache.ignite.internal.processors.query.GridQueryProperty; import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor; import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase; import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOffheap; @@ -93,12 +94,14 @@ import org.apache.ignite.internal.processors.query.h2.opt.GridLuceneIndex; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter; import org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor; import org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor; +import org.apache.ignite.internal.util.GridBoundedConcurrentLinkedHashMap; import org.apache.ignite.internal.util.GridEmptyCloseableIterator; import org.apache.ignite.internal.util.GridSpinBusyLock; import org.apache.ignite.internal.util.lang.GridCloseableIterator; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; import org.apache.ignite.internal.util.typedef.internal.SB; @@ -176,6 +179,12 @@ public class IgniteH2Indexing implements GridQueryIndexing { ";DEFAULT_LOCK_TIMEOUT=10000;FUNCTIONS_IN_SCHEMA=true;OPTIMIZE_REUSE_RESULTS=0;QUERY_CACHE_SIZE=0;" + "RECOMPILE_ALWAYS=1;MAX_OPERATION_MEMORY=0"; + /** */ + private static final int PREPARED_STMT_CACHE_SIZE = 256; + + /** */ + private static final int TWO_STEP_QRY_CACHE_SIZE = 1024; + /** Field name for key. */ public static final String KEY_FIELD_NAME = "_key"; @@ -190,6 +199,8 @@ public class IgniteH2Indexing implements GridQueryIndexing { */ static { try { + System.setProperty("h2.objectCache", "false"); + COMMAND_FIELD = JdbcPreparedStatement.class.getDeclaredField("command"); COMMAND_FIELD.setAccessible(true); @@ -242,6 +253,9 @@ public class IgniteH2Indexing implements GridQueryIndexing { c = initialValue(); set(c); + + // Reset statement cache when new connection is created. + stmtCache.get().clear(); } return c; @@ -266,6 +280,17 @@ public class IgniteH2Indexing implements GridQueryIndexing { /** */ private volatile GridKernalContext ctx; + /** */ + private final ThreadLocal<StatementCache> stmtCache = new ThreadLocal<StatementCache>() { + @Override protected StatementCache initialValue() { + return new StatementCache(PREPARED_STMT_CACHE_SIZE); + } + }; + + /** */ + private final GridBoundedConcurrentLinkedHashMap<T3<String, String, Boolean>, TwoStepCachedQuery> twoStepCache = + new GridBoundedConcurrentLinkedHashMap<>(TWO_STEP_QRY_CACHE_SIZE); + /** * @param space Space. * @return Connection. @@ -280,6 +305,35 @@ public class IgniteH2Indexing implements GridQueryIndexing { } /** + * @param c Connection. + * @param sql SQL. + * @param useStmtCache If {@code true} uses statement cache. + * @return Prepared statement. + * @throws SQLException If failed. + */ + private PreparedStatement prepareStatement(Connection c, String sql, boolean useStmtCache) throws SQLException { + if (useStmtCache) { + StatementCache cache = stmtCache.get(); + + PreparedStatement stmt = cache.get(sql); + + if (stmt != null && !stmt.isClosed()) { + assert stmt.getConnection() == c; + + return stmt; + } + + stmt = c.prepareStatement(sql); + + cache.put(sql, stmt); + + return stmt; + } + else + return c.prepareStatement(sql); + } + + /** * Gets DB connection. * * @param schema Whether to set schema for connection or not. @@ -648,7 +702,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { try { Connection conn = connectionForThread(schema(spaceName)); - ResultSet rs = executeSqlQueryWithTimer(spaceName, conn, qry, params); + ResultSet rs = executeSqlQueryWithTimer(spaceName, conn, qry, params, true); List<GridQueryFieldMetadata> meta = null; @@ -710,15 +764,16 @@ public class IgniteH2Indexing implements GridQueryIndexing { * @param conn Connection,. * @param sql Sql query. * @param params Parameters. + * @param useStmtCache If {@code true} uses statement cache. * @return Result. * @throws IgniteCheckedException If failed. */ - private ResultSet executeSqlQuery(Connection conn, String sql, Collection<Object> params) + private ResultSet executeSqlQuery(Connection conn, String sql, Collection<Object> params, boolean useStmtCache) throws IgniteCheckedException { PreparedStatement stmt; try { - stmt = conn.prepareStatement(sql); + stmt = prepareStatement(conn, sql, useStmtCache); } catch (SQLException e) { throw new IgniteCheckedException("Failed to parse SQL query: " + sql, e); @@ -747,18 +802,23 @@ public class IgniteH2Indexing implements GridQueryIndexing { /** * Executes sql query and prints warning if query is too slow.. * + * @param space Space name. * @param conn Connection,. * @param sql Sql query. * @param params Parameters. + * @param useStmtCache If {@code true} uses statement cache. * @return Result. * @throws IgniteCheckedException If failed. */ - public ResultSet executeSqlQueryWithTimer(String space, Connection conn, String sql, - @Nullable Collection<Object> params) throws IgniteCheckedException { + public ResultSet executeSqlQueryWithTimer(String space, + Connection conn, + String sql, + @Nullable Collection<Object> params, + boolean useStmtCache) throws IgniteCheckedException { long start = U.currentTimeMillis(); try { - ResultSet rs = executeSqlQuery(conn, sql, params); + ResultSet rs = executeSqlQuery(conn, sql, params, useStmtCache); long time = U.currentTimeMillis() - start; @@ -767,7 +827,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { if (time > longQryExecTimeout) { String msg = "Query execution is too long (" + time + " ms): " + sql; - ResultSet plan = executeSqlQuery(conn, "EXPLAIN " + sql, params); + ResultSet plan = executeSqlQuery(conn, "EXPLAIN " + sql, params, false); plan.next(); @@ -803,7 +863,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { String sql = generateQuery(qry, tbl); - return executeSqlQueryWithTimer(space, conn, sql, params); + return executeSqlQueryWithTimer(space, conn, sql, params, true); } /** @@ -924,41 +984,52 @@ public class IgniteH2Indexing implements GridQueryIndexing { /** {@inheritDoc} */ @Override public QueryCursor<List<?>> queryTwoStep(GridCacheContext<?,?> cctx, SqlFieldsQuery qry) { - String space = cctx.name(); - String sqlQry = qry.getSql(); + final String space = cctx.name(); + final String sqlQry = qry.getSql(); Connection c = connectionForSpace(space); - PreparedStatement stmt; + GridCacheTwoStepQuery twoStepQry; + List<GridQueryFieldMetadata> meta; - try { - stmt = c.prepareStatement(sqlQry); - } - catch (SQLException e) { - throw new CacheException("Failed to parse query: " + sqlQry, e); - } + final T3<String, String, Boolean> cachedQryKey = new T3<>(space, sqlQry, qry.isCollocated()); + TwoStepCachedQuery cachedQry = twoStepCache.get(cachedQryKey); - try { - bindParameters(stmt, F.asList(qry.getArgs())); - } - catch (IgniteCheckedException e) { - throw new CacheException("Failed to bind parameters: [qry=" + sqlQry + ", params=" + - Arrays.deepToString(qry.getArgs()) + "]", e); + if (cachedQry != null) { + twoStepQry = cachedQry.twoStepQry.copy(qry.getArgs()); + meta = cachedQry.meta; } + else { + PreparedStatement stmt; - GridCacheTwoStepQuery twoStepQry; - List<GridQueryFieldMetadata> meta; + try { + // Do not cache this statement because the whole two step query object will be cached later on. + stmt = prepareStatement(c, sqlQry, false); + } + catch (SQLException e) { + throw new CacheException("Failed to parse query: " + sqlQry, e); + } + try { + try { + bindParameters(stmt, F.asList(qry.getArgs())); + } + catch (IgniteCheckedException e) { + throw new CacheException("Failed to bind parameters: [qry=" + sqlQry + ", params=" + + Arrays.deepToString(qry.getArgs()) + "]", e); + } - try { - twoStepQry = GridSqlQuerySplitter.split((JdbcPreparedStatement)stmt, qry.getArgs(), qry.isCollocated()); + try { + twoStepQry = GridSqlQuerySplitter.split((JdbcPreparedStatement)stmt, qry.getArgs(), qry.isCollocated()); - meta = meta(stmt.getMetaData()); - } - catch (SQLException e) { - throw new CacheException(e); - } - finally { - U.close(stmt, log); + meta = meta(stmt.getMetaData()); + } + catch (SQLException e) { + throw new CacheException(e); + } + } + finally { + U.close(stmt, log); + } } if (log.isDebugEnabled()) @@ -970,6 +1041,11 @@ public class IgniteH2Indexing implements GridQueryIndexing { cursor.fieldsMeta(meta); + if (cachedQry == null && !twoStepQry.explain()) { + cachedQry = new TwoStepCachedQuery(meta, twoStepQry.copy(null)); + twoStepCache.putIfAbsent(cachedQryKey, cachedQry); + } + return cursor; } @@ -1542,6 +1618,31 @@ public class IgniteH2Indexing implements GridQueryIndexing { } /** + * Cached two-step query. + */ + private static final class TwoStepCachedQuery { + /** */ + final List<GridQueryFieldMetadata> meta; + + /** */ + final GridCacheTwoStepQuery twoStepQry; + + /** + * @param meta Fields metadata. + * @param twoStepQry Query. + */ + public TwoStepCachedQuery(List<GridQueryFieldMetadata> meta, GridCacheTwoStepQuery twoStepQry) { + this.meta = meta; + this.twoStepQry = twoStepQry; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(TwoStepCachedQuery.class, this); + } + } + + /** * Wrapper to store connection and flag is schema set or not. */ private static class ConnectionWrapper { @@ -1889,7 +1990,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { * @throws IgniteCheckedException If failed. */ protected FieldsIterator(ResultSet data) throws IgniteCheckedException { - super(data); + super(data, false); } /** {@inheritDoc} */ @@ -1914,7 +2015,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { * @throws IgniteCheckedException If failed. */ protected KeyValIterator(ResultSet data) throws IgniteCheckedException { - super(data); + super(data, false); } /** {@inheritDoc} */ @@ -2091,6 +2192,12 @@ public class IgniteH2Indexing implements GridQueryIndexing { /** */ private final boolean preferSwapVal; + /** */ + private final boolean snapshotableIdx; + + /** */ + private final GridQueryProperty[] props; + /** * @param type Type descriptor. * @param schema Schema. @@ -2120,7 +2227,18 @@ public class IgniteH2Indexing implements GridQueryIndexing { keyType = DataType.getTypeFromClass(type.keyClass()); valType = DataType.getTypeFromClass(type.valueClass()); + props = new GridQueryProperty[fields.length]; + + for (int i = 0; i < fields.length; i++) { + GridQueryProperty p = type.property(fields[i]); + + assert p != null : fields[i]; + + props[i] = p; + } + preferSwapVal = schema.ccfg.getMemoryMode() == CacheMemoryMode.OFFHEAP_TIERED; + snapshotableIdx = schema.ccfg.isSnapshotableIndex() || schema.offheap != null; } /** {@inheritDoc} */ @@ -2274,7 +2392,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { /** {@inheritDoc} */ @Override public Object columnValue(Object key, Object val, int col) { try { - return type.value(fields[col], key, val); + return props[col].value(key, val); } catch (IgniteCheckedException e) { throw DbException.convert(e); @@ -2298,5 +2416,40 @@ public class IgniteH2Indexing implements GridQueryIndexing { @Override public boolean preferSwapValue() { return preferSwapVal; } + + /** {@inheritDoc} */ + @Override public boolean snapshotableIndex() { + return snapshotableIdx; + } + } + + /** + * Statement cache. + */ + private static class StatementCache extends LinkedHashMap<String, PreparedStatement> { + /** */ + private int size; + + /** + * @param size Size. + */ + private StatementCache(int size) { + super(size, (float)0.75, true); + + this.size = size; + } + + /** {@inheritDoc} */ + @Override protected boolean removeEldestEntry(Map.Entry<String, PreparedStatement> eldest) { + boolean rmv = size() > size; + + if (rmv) { + PreparedStatement stmt = eldest.getValue(); + + U.closeQuiet(stmt); + } + + return rmv; + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java index ed3ff7a..80dcfcb 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2RowDescriptor.java @@ -116,4 +116,9 @@ public interface GridH2RowDescriptor extends GridOffHeapSmartPointerFactory<Grid * @return {@code True} if should check swap value before offheap. */ public boolean preferSwapValue(); + + /** + * @return {@code True} if index should support snapshots. + */ + public boolean snapshotableIndex(); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java index bf318b2..f1e5b16 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java @@ -54,6 +54,7 @@ import org.h2.table.TableFilter; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; import org.jsr166.ConcurrentHashMap8; +import org.jsr166.LongAdder8; import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VAL_COL; @@ -79,6 +80,12 @@ public class GridH2Table extends TableBase { /** */ private volatile Object[] actualSnapshot; + /** */ + private final LongAdder8 size = new LongAdder8(); + + /** */ + private final boolean snapshotEnabled; + /** * Creates table. * @@ -101,10 +108,12 @@ public class GridH2Table extends TableBase { assert idxs != null; assert idxs.size() >= 1; - lock = new ReentrantReadWriteLock(); + lock = new ReentrantReadWriteLock(); // Add scan index at 0 which is required by H2. idxs.add(0, new ScanIndex(index(0))); + + snapshotEnabled = desc == null || desc.snapshotableIndex(); } /** {@inheritDoc} */ @@ -164,7 +173,7 @@ public class GridH2Table extends TableBase { GridUnsafeMemory mem = desc.memory(); - lock.readLock().lock(); + readLock(); if (mem != null) desc.guard().begin(); @@ -183,7 +192,7 @@ public class GridH2Table extends TableBase { return true; } finally { - lock.readLock().unlock(); + readUnlock(); if (mem != null) desc.guard().end(); @@ -209,6 +218,9 @@ public class GridH2Table extends TableBase { } } + if (!snapshotEnabled) + return; + Object[] snapshot; for (long waitTime = 100;; waitTime *= 2) { // Increase wait time to avoid starvation. @@ -297,16 +309,14 @@ public class GridH2Table extends TableBase { * Closes table and releases resources. */ public void close() { - Lock l = lock.writeLock(); - - l.lock(); + writeLock(); try { for (int i = 1, len = idxs.size(); i < len; i++) index(i).close(null); } finally { - l.unlock(); + writeUnlock(); } } @@ -363,7 +373,7 @@ public class GridH2Table extends TableBase { // getting updated from different threads with different rows with the same key is impossible. GridUnsafeMemory mem = desc == null ? null : desc.memory(); - lock.readLock().lock(); + readLock(); if (mem != null) desc.guard().begin(); @@ -379,6 +389,8 @@ public class GridH2Table extends TableBase { kvOld.onUnswap(kvOld.getValue(VAL_COL), true); } + else if (old == null) + size.increment(); int len = idxs.size(); @@ -414,6 +426,8 @@ public class GridH2Table extends TableBase { } if (old != null) { + size.decrement(); + // Remove row from all indexes. // Start from 2 because 0 - Scan (don't need to update), 1 - PK (already updated). for (int i = 2, len = idxs.size(); i < len; i++) { @@ -432,7 +446,7 @@ public class GridH2Table extends TableBase { return true; } finally { - lock.readLock().unlock(); + readUnlock(); if (mem != null) desc.guard().end(); @@ -469,6 +483,9 @@ public class GridH2Table extends TableBase { * Rebuilds all indexes of this table. */ public void rebuildIndexes() { + if (!snapshotEnabled) + return; + GridUnsafeMemory memory = desc == null ? null : desc.memory(); lock.writeLock().lock(); @@ -579,7 +596,7 @@ public class GridH2Table extends TableBase { /** {@inheritDoc} */ @Override public long getRowCountApproximation() { - return getUniqueIndex().getRowCountApproximation(); + return size.longValue(); } /** {@inheritDoc} */ @@ -605,6 +622,38 @@ public class GridH2Table extends TableBase { } /** + * + */ + private void readLock() { + if (snapshotEnabled) + lock.readLock().lock(); + } + + /** + * + */ + private void readUnlock() { + if (snapshotEnabled) + lock.readLock().unlock(); + } + + /** + * + */ + private void writeLock() { + if (snapshotEnabled) + lock.writeLock().lock(); + } + + /** + * + */ + private void writeUnlock() { + if (snapshotEnabled) + lock.writeLock().unlock(); + } + + /** * H2 Table engine. */ @SuppressWarnings({"PublicInnerClass", "FieldAccessedSynchronizedAndUnsynchronized"}) http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java index 387c58b..28adeee 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java @@ -23,6 +23,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.NavigableMap; import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; import org.apache.ignite.internal.util.GridEmptyIterator; import org.apache.ignite.internal.util.offheap.unsafe.GridOffHeapSnapTreeMap; import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard; @@ -51,8 +52,10 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS protected final ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> tree; /** */ - private final ThreadLocal<ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row>> snapshot = - new ThreadLocal<>(); + private final ThreadLocal<ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row>> snapshot = new ThreadLocal<>(); + + /** */ + private final boolean snapshotEnabled; /** * Constructor with index initialization. @@ -81,40 +84,68 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS final GridH2RowDescriptor desc = tbl.rowDescriptor(); - tree = desc == null || desc.memory() == null ? new SnapTreeMap<GridSearchRowPointer, GridH2Row>(this) { - @Override protected void afterNodeUpdate_nl(Node<GridSearchRowPointer, GridH2Row> node, Object val) { - if (val != null) - node.key = (GridSearchRowPointer)val; - } + if (desc == null || desc.memory() == null) { + snapshotEnabled = desc == null || desc.snapshotableIndex(); - @Override protected Comparable<? super GridSearchRowPointer> comparable(Object key) { - if (key instanceof ComparableRow) - return (Comparable<? super SearchRow>)key; + if (snapshotEnabled) { + tree = new SnapTreeMap<GridSearchRowPointer, GridH2Row>(this) { + @Override protected void afterNodeUpdate_nl(Node<GridSearchRowPointer, GridH2Row> node, Object val) { + if (val != null) + node.key = (GridSearchRowPointer)val; + } - return super.comparable(key); + @Override protected Comparable<? super GridSearchRowPointer> comparable(Object key) { + if (key instanceof ComparableRow) + return (Comparable<? super SearchRow>)key; + + return super.comparable(key); + } + }; } - } : new GridOffHeapSnapTreeMap<GridSearchRowPointer, GridH2Row>(desc, desc, desc.memory(), desc.guard(), this) { - @Override protected void afterNodeUpdate_nl(long node, GridH2Row val) { - final long oldKey = keyPtr(node); + else { + tree = new ConcurrentSkipListMap<>( + new Comparator<GridSearchRowPointer>() { + @Override public int compare(GridSearchRowPointer o1, GridSearchRowPointer o2) { + if (o1 instanceof ComparableRow) + return ((ComparableRow)o1).compareTo(o2); - if (val != null) { - key(node, val); + if (o2 instanceof ComparableRow) + return -((ComparableRow)o2).compareTo(o1); - guard.finalizeLater(new Runnable() { - @Override public void run() { - desc.createPointer(oldKey).decrementRefCount(); + return compareRows(o1, o2); + } } - }); - } + ); } + } + else { + assert desc.snapshotableIndex() : desc; - @Override protected Comparable<? super GridSearchRowPointer> comparable(Object key) { - if (key instanceof ComparableRow) - return (Comparable<? super SearchRow>)key; + snapshotEnabled = true; - return super.comparable(key); - } - }; + tree = new GridOffHeapSnapTreeMap<GridSearchRowPointer, GridH2Row>(desc, desc, desc.memory(), desc.guard(), this) { + @Override protected void afterNodeUpdate_nl(long node, GridH2Row val) { + final long oldKey = keyPtr(node); + + if (val != null) { + key(node, val); + + guard.finalizeLater(new Runnable() { + @Override public void run() { + desc.createPointer(oldKey).decrementRefCount(); + } + }); + } + } + + @Override protected Comparable<? super GridSearchRowPointer> comparable(Object key) { + if (key instanceof ComparableRow) + return (Comparable<? super SearchRow>)key; + + return super.comparable(key); + } + }; + } } /** @@ -133,6 +164,9 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS */ @SuppressWarnings("unchecked") @Override public Object takeSnapshot(@Nullable Object s) { + if (!snapshotEnabled) + return null; + assert snapshot.get() == null; if (s == null) @@ -148,6 +182,9 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS * Releases snapshot for current thread. */ @Override public void releaseSnapshot() { + if (!snapshotEnabled) + return; + ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> s = snapshot.get(); snapshot.remove(); @@ -160,6 +197,9 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS * @return Snapshot for current thread if there is one. */ private ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> treeForRead() { + if (!snapshotEnabled) + return tree; + ConcurrentNavigableMap<GridSearchRowPointer, GridH2Row> res = snapshot.get(); if (res == null) @@ -199,7 +239,7 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS /** {@inheritDoc} */ @Override public long getRowCountApproximation() { - return tree.size(); + return table.getRowCountApproximation(); } /** {@inheritDoc} */ @@ -372,7 +412,7 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS * Comparable row with bias. Will be used for queries to have correct bounds (in case of multicolumn index * and query on few first columns we will multiple equal entries in tree). */ - private class ComparableRow implements GridSearchRowPointer, Comparable<SearchRow> { + private final class ComparableRow implements GridSearchRowPointer, Comparable<SearchRow> { /** */ private final SearchRow row; http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/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 7b89824..0c9c8fe 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 @@ -28,6 +28,7 @@ import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery; import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; import org.apache.ignite.internal.util.typedef.F; import org.h2.jdbc.JdbcPreparedStatement; +import org.h2.util.IntArray; import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.AVG; @@ -224,13 +225,23 @@ public class GridSqlQuerySplitter { rdcQry.distinct(true); } - // Build resulting two step query. - GridCacheTwoStepQuery res = new GridCacheTwoStepQuery(spaces, new GridCacheSqlQuery(rdcQry.getSQL(), - findParams(rdcQry, params, new ArrayList<>()).toArray())); + IntArray paramIdxs = new IntArray(params.length); + + GridCacheSqlQuery rdc = new GridCacheSqlQuery(rdcQry.getSQL(), + findParams(rdcQry, params, new ArrayList<>(), paramIdxs).toArray()); + + rdc.parameterIndexes(toIntArray(paramIdxs)); + + paramIdxs = new IntArray(params.length); - res.addMapQuery(new GridCacheSqlQuery(mapQry.getSQL(), - findParams(mapQry, params, new ArrayList<>(params.length)).toArray()) - .columns(collectColumns(mapExps))); + GridCacheSqlQuery map = new GridCacheSqlQuery(mapQry.getSQL(), + findParams(mapQry, params, new ArrayList<>(params.length), paramIdxs).toArray()) + .columns(collectColumns(mapExps)); + + map.parameterIndexes(toIntArray(paramIdxs)); + + // Build resulting two step query. + GridCacheTwoStepQuery res = new GridCacheTwoStepQuery(spaces, rdc, rdcQry.simpleQuery()).addMapQuery(map); res.explain(explain); @@ -238,6 +249,16 @@ public class GridSqlQuerySplitter { } /** + * @param arr Integer array. + * @return Primitive int array. + */ + private static int[] toIntArray(IntArray arr) { + int[] res = new int[arr.size()]; + arr.toArray(res); + return res; + } + + /** * @param cols Columns from SELECT clause. * @return Map of columns with types. */ @@ -341,19 +362,21 @@ public class GridSqlQuerySplitter { * @param qry Select. * @param params Parameters. * @param target Extracted parameters. + * @param paramIdxs Parameter indexes. * @return Extracted parameters list. */ - private static List<Object> findParams(GridSqlQuery qry, Object[] params, ArrayList<Object> target) { + private static List<Object> findParams(GridSqlQuery qry, Object[] params, ArrayList<Object> target, + IntArray paramIdxs) { if (qry instanceof GridSqlSelect) - return findParams((GridSqlSelect)qry, params, target); + return findParams((GridSqlSelect)qry, params, target, paramIdxs); GridSqlUnion union = (GridSqlUnion)qry; - findParams(union.left(), params, target); - findParams(union.right(), params, target); + findParams(union.left(), params, target, paramIdxs); + findParams(union.right(), params, target, paramIdxs); - findParams(qry.limit(), params, target); - findParams(qry.offset(), params, target); + findParams(qry.limit(), params, target, paramIdxs); + findParams(qry.offset(), params, target, paramIdxs); return target; } @@ -362,22 +385,24 @@ public class GridSqlQuerySplitter { * @param qry Select. * @param params Parameters. * @param target Extracted parameters. + * @param paramIdxs Parameter indexes. * @return Extracted parameters list. */ - private static List<Object> findParams(GridSqlSelect qry, Object[] params, ArrayList<Object> target) { + private static List<Object> findParams(GridSqlSelect qry, Object[] params, ArrayList<Object> target, + IntArray paramIdxs) { if (params.length == 0) return target; for (GridSqlElement el : qry.columns(false)) - findParams(el, params, target); + findParams(el, params, target, paramIdxs); - findParams(qry.from(), params, target); - findParams(qry.where(), params, target); + findParams(qry.from(), params, target, paramIdxs); + findParams(qry.where(), params, target, paramIdxs); // Don't search in GROUP BY and HAVING since they expected to be in select list. - findParams(qry.limit(), params, target); - findParams(qry.offset(), params, target); + findParams(qry.limit(), params, target, paramIdxs); + findParams(qry.offset(), params, target, paramIdxs); return target; } @@ -386,15 +411,17 @@ public class GridSqlQuerySplitter { * @param el Element. * @param params Parameters. * @param target Extracted parameters. + * @param paramIdxs Parameter indexes. */ - private static void findParams(@Nullable GridSqlElement el, Object[] params, ArrayList<Object> target) { + private static void findParams(@Nullable GridSqlElement el, Object[] params, ArrayList<Object> target, + IntArray paramIdxs) { if (el == null) return; if (el instanceof GridSqlParameter) { // H2 Supports queries like "select ?5" but first 4 non-existing parameters are need to be set to any value. // Here we will set them to NULL. - int idx = ((GridSqlParameter)el).index(); + final int idx = ((GridSqlParameter)el).index(); while (target.size() < idx) target.add(null); @@ -409,12 +436,14 @@ public class GridSqlQuerySplitter { target.add(param); else target.set(idx, param); + + paramIdxs.add(idx); } else if (el instanceof GridSqlSubquery) - findParams(((GridSqlSubquery)el).select(), params, target); + findParams(((GridSqlSubquery)el).select(), params, target, paramIdxs); else for (GridSqlElement child : el) - findParams(child, params, target); + findParams(child, params, target, paramIdxs); } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java index e537ace..e190c87 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java @@ -103,6 +103,33 @@ public class GridSqlSelect extends GridSqlQuery { } /** + * @return {@code True} if this simple SQL query like 'SELECT A, B, C from SOME_TABLE' without any conditions + * and expressions. + */ + public boolean simpleQuery() { + boolean simple = !distinct && + from instanceof GridSqlTable && + where == null && + grpCols == null && + havingCol < 0 && + sort.isEmpty() && + limit == null && + offset == null; + + if (simple) { + for (GridSqlElement expression : columns(true)) { + if (expression instanceof GridSqlAlias) + expression = expression.child(); + + if (!(expression instanceof GridSqlColumn)) + return false; + } + } + + return simple; + } + + /** * @param buff Statement builder. * @param expression Alias expression. */ http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java index b4e1932..21541ec 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java @@ -451,8 +451,11 @@ public class GridMapQueryExecutor { int i = 0; for (GridCacheSqlQuery qry : qrys) { - ResultSet rs = h2.executeSqlQueryWithTimer(req.space(), h2.connectionForSpace(req.space()), qry.query(), - F.asList(qry.parameters())); + ResultSet rs = h2.executeSqlQueryWithTimer(req.space(), + h2.connectionForSpace(req.space()), + qry.query(), + F.asList(qry.parameters()), + true); if (ctx.event().isRecordable(EVT_CACHE_QUERY_EXECUTED)) { ctx.event().record(new CacheQueryExecutedEvent<>( @@ -820,17 +823,7 @@ public class GridMapQueryExecutor { closed = true; - Statement stmt; - - try { - stmt = rs.getStatement(); - } - catch (SQLException e) { - throw new IllegalStateException(e); // Must not happen. - } - U.close(rs, log); - U.close(stmt, log); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java index 7f8caed..12c2240 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndex.java @@ -80,6 +80,13 @@ public abstract class GridMergeIndex extends BaseIndex { } /** + * + */ + protected GridMergeIndex() { + // No-op. + } + + /** * @return Return source nodes for this merge index. */ public Set<UUID> sources() { http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java index 5f5eb25..501480a 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMergeIndexUnsorted.java @@ -47,6 +47,20 @@ public class GridMergeIndexUnsorted extends GridMergeIndex { super(tbl, name, IndexType.createScan(false), IndexColumn.wrap(tbl.getColumns())); } + /** + * @return Dummy index instance. + */ + public static GridMergeIndexUnsorted createDummy() { + return new GridMergeIndexUnsorted(); + } + + /** + * + */ + private GridMergeIndexUnsorted() { + // No-op. + } + /** {@inheritDoc} */ @Override protected void addPage0(GridResultPage page) { assert page.rowsInPage() > 0 || page.isLast() || page.isFail(); http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java index f515a78..1d4fa30 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridReduceQueryExecutor.java @@ -79,10 +79,12 @@ import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.plugin.extensions.communication.Message; import org.h2.command.ddl.CreateTableData; import org.h2.engine.Session; +import org.h2.index.Cursor; import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcResultSet; import org.h2.jdbc.JdbcStatement; import org.h2.result.ResultInterface; +import org.h2.result.Row; import org.h2.table.Column; import org.h2.util.IntArray; import org.h2.value.Value; @@ -184,8 +186,8 @@ public class GridReduceQueryExecutor { UUID nodeId = ((DiscoveryEvent)evt).eventNode().id(); for (QueryRun r : runs.values()) { - for (GridMergeTable tbl : r.tbls) { - if (tbl.getScanIndex(null).hasSource(nodeId)) { + for (GridMergeIndex idx : r.idxs) { + if (idx.hasSource(nodeId)) { handleNodeLeft(r, nodeId); break; @@ -270,7 +272,7 @@ public class GridReduceQueryExecutor { final int pageSize = r.pageSize; - GridMergeIndex idx = r.tbls.get(msg.query()).getScanIndex(null); + GridMergeIndex idx = r.idxs.get(msg.query()); GridResultPage page; @@ -468,7 +470,7 @@ public class GridReduceQueryExecutor { r.pageSize = qry.pageSize() <= 0 ? GridCacheTwoStepQuery.DFLT_PAGE_SIZE : qry.pageSize(); - r.tbls = new ArrayList<>(qry.mapQueries().size()); + r.idxs = new ArrayList<>(qry.mapQueries().size()); String space = cctx.name(); @@ -501,7 +503,7 @@ public class GridReduceQueryExecutor { assert !nodes.isEmpty(); if (cctx.isReplicated() || qry.explain()) { - assert qry.explain() || !nodes.contains(ctx.cluster().get().localNode()) : + assert qry.explain() || !nodes.contains(ctx.discovery().localNode()) : "We must be on a client node."; // Select random data node to run query on a replicated data or get EXPLAIN PLAN from a single node. @@ -510,27 +512,35 @@ public class GridReduceQueryExecutor { int tblIdx = 0; + final boolean skipMergeTbl = !qry.explain() && qry.skipMergeTable(); + for (GridCacheSqlQuery mapQry : qry.mapQueries()) { - GridMergeTable tbl; + GridMergeIndex idx; - try { - tbl = createMergeTable(r.conn, mapQry, qry.explain()); - } - catch (IgniteCheckedException e) { - throw new IgniteException(e); - } + if (!skipMergeTbl) { + GridMergeTable tbl; + + try { + tbl = createMergeTable(r.conn, mapQry, qry.explain()); + } + catch (IgniteCheckedException e) { + throw new IgniteException(e); + } - GridMergeIndex idx = tbl.getScanIndex(null); + idx = tbl.getScanIndex(null); + + fakeTable(r.conn, tblIdx++).setInnerTable(tbl); + } + else + idx = GridMergeIndexUnsorted.createDummy(); for (ClusterNode node : nodes) idx.addSource(node.id()); - r.tbls.add(tbl); - - fakeTable(r.conn, tblIdx++).setInnerTable(tbl); + r.idxs.add(idx); } - r.latch = new CountDownLatch(r.tbls.size() * nodes.size()); + r.latch = new CountDownLatch(r.idxs.size() * nodes.size()); runs.put(qryReqId, r); @@ -586,19 +596,52 @@ public class GridReduceQueryExecutor { else // Send failed. retry = true; - ResultSet res = null; + Iterator<List<?>> resIter = null; if (!retry) { if (qry.explain()) return explainPlan(r.conn, space, qry); - GridCacheSqlQuery rdc = qry.reduceQuery(); + if (skipMergeTbl) { + List<List<?>> res = new ArrayList<>(); + + assert r.idxs.size() == 1 : r.idxs; - res = h2.executeSqlQueryWithTimer(space, r.conn, rdc.query(), F.asList(rdc.parameters())); + GridMergeIndex idx = r.idxs.get(0); + + Cursor cur = idx.findInStream(null, null); + + while (cur.next()) { + Row row = cur.get(); + + int cols = row.getColumnCount(); + + List<Object> resRow = new ArrayList<>(cols); + + for (int c = 0; c < cols; c++) + resRow.add(row.getValue(c).getObject()); + + res.add(resRow); + } + + resIter = res.iterator(); + } + else { + GridCacheSqlQuery rdc = qry.reduceQuery(); + + // Statement caching is prohibited here because we can't guarantee correct merge index reuse. + ResultSet res = h2.executeSqlQueryWithTimer(space, + r.conn, + rdc.query(), + F.asList(rdc.parameters()), + false); + + resIter = new Iter(res); + } } - for (GridMergeTable tbl : r.tbls) { - if (!tbl.getScanIndex(null).fetchedAll()) // We have to explicitly cancel queries on remote nodes. + for (GridMergeIndex idx : r.idxs) { + if (!idx.fetchedAll()) // We have to explicitly cancel queries on remote nodes. send(nodes, new GridQueryCancelRequest(qryReqId), null); } @@ -609,7 +652,7 @@ public class GridReduceQueryExecutor { continue; } - return new GridQueryCacheObjectsIterator(new Iter(res), cctx, keepPortable); + return new GridQueryCacheObjectsIterator(resIter, cctx, keepPortable); } catch (IgniteCheckedException | RuntimeException e) { U.closeQuiet(r.conn); @@ -633,8 +676,10 @@ public class GridReduceQueryExecutor { if (!runs.remove(qryReqId, r)) U.warn(log, "Query run was already removed: " + qryReqId); - for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++) - fakeTable(null, i).setInnerTable(null); // Drop all merge tables. + if (!skipMergeTbl) { + for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++) + fakeTable(null, i).setInnerTable(null); // Drop all merge tables. + } } } } @@ -941,7 +986,7 @@ public class GridReduceQueryExecutor { List<List<?>> lists = new ArrayList<>(); for (int i = 0, mapQrys = qry.mapQueries().size(); i < mapQrys; i++) { - ResultSet rs = h2.executeSqlQueryWithTimer(space, c, "SELECT PLAN FROM " + table(i), null); + ResultSet rs = h2.executeSqlQueryWithTimer(space, c, "SELECT PLAN FROM " + table(i), null, false); lists.add(F.asList(getPlan(rs))); } @@ -956,7 +1001,11 @@ public class GridReduceQueryExecutor { GridCacheSqlQuery rdc = qry.reduceQuery(); - ResultSet rs = h2.executeSqlQueryWithTimer(space, c, "EXPLAIN " + rdc.query(), F.asList(rdc.parameters())); + ResultSet rs = h2.executeSqlQueryWithTimer(space, + c, + "EXPLAIN " + rdc.query(), + F.asList(rdc.parameters()), + false); lists.add(F.asList(getPlan(rs))); @@ -1013,7 +1062,7 @@ public class GridReduceQueryExecutor { } if (locNodeFound) // Local node goes the last to allow parallel execution. - h2.mapQueryExecutor().onMessage(ctx.localNodeId(), copy(msg, ctx.cluster().get().localNode(), partsMap)); + h2.mapQueryExecutor().onMessage(ctx.localNodeId(), copy(msg, ctx.discovery().localNode(), partsMap)); return ok; } @@ -1120,7 +1169,7 @@ public class GridReduceQueryExecutor { */ private static class QueryRun { /** */ - private List<GridMergeTable> tbls; + private List<GridMergeIndex> idxs; /** */ private CountDownLatch latch; @@ -1148,8 +1197,8 @@ public class GridReduceQueryExecutor { while (latch.getCount() != 0) // We don't need to wait for all nodes to reply. latch.countDown(); - for (GridMergeTable tbl : tbls) // Fail all merge indexes. - tbl.getScanIndex(null).fail(nodeId); + for (GridMergeIndex idx : idxs) // Fail all merge indexes. + idx.fail(nodeId); } /** @@ -1162,8 +1211,8 @@ public class GridReduceQueryExecutor { while (latch.getCount() != 0) // We don't need to wait for all nodes to reply. latch.countDown(); - for (GridMergeTable tbl : tbls) // Fail all merge indexes. - tbl.getScanIndex(null).fail(e); + for (GridMergeIndex idx : idxs) // Fail all merge indexes. + idx.fail(e); } } @@ -1179,7 +1228,7 @@ public class GridReduceQueryExecutor { * @throws IgniteCheckedException If failed. */ protected Iter(ResultSet data) throws IgniteCheckedException { - super(data); + super(data, true); } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java index f3fbf15..c510600 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractQuerySelfTest.java @@ -156,36 +156,38 @@ public abstract class IgniteCacheAbstractQuerySelfTest extends GridCommonAbstrac if (i > 0) cc.setName("c" + i); - cc.setCacheMode(cacheMode()); - cc.setAtomicityMode(atomicityMode()); - cc.setNearConfiguration(nearCacheConfiguration()); - cc.setWriteSynchronizationMode(FULL_SYNC); - cc.setCacheStoreFactory(new StoreFactory()); - cc.setReadThrough(true); - cc.setWriteThrough(true); - cc.setLoadPreviousValue(true); - cc.setRebalanceMode(SYNC); - cc.setSwapEnabled(true); - cc.setSqlFunctionClasses(SqlFunctions.class); - cc.setIndexedTypes( - BadHashKeyObject.class, Byte.class, - ObjectValue.class, Long.class, - Integer.class, Integer.class, - Integer.class, String.class, - Integer.class, ObjectValue.class, - String.class, ObjectValueOther.class, - Integer.class, ArrayObject.class, - Key.class, GridCacheQueryTestValue.class, - UUID.class, Person.class, - IgniteCacheReplicatedQuerySelfTest.CacheKey.class, IgniteCacheReplicatedQuerySelfTest.CacheValue.class - ); - - if (cacheMode() != CacheMode.LOCAL) - cc.setAffinity(new RendezvousAffinityFunction()); - - // Explicitly set number of backups equal to number of grids. - if (cacheMode() == CacheMode.PARTITIONED) - cc.setBackups(gridCount()); + cc.setCacheMode(cacheMode()); + cc.setAtomicityMode(atomicityMode()); + cc.setNearConfiguration(nearCacheConfiguration()); + cc.setWriteSynchronizationMode(FULL_SYNC); + cc.setCacheStoreFactory(new StoreFactory()); + cc.setReadThrough(true); + cc.setWriteThrough(true); + cc.setLoadPreviousValue(true); + cc.setRebalanceMode(SYNC); + cc.setSwapEnabled(true); + cc.setSqlFunctionClasses(SqlFunctions.class); + cc.setIndexedTypes( + BadHashKeyObject.class, Byte.class, + ObjectValue.class, Long.class, + Integer.class, Integer.class, + Integer.class, String.class, + Integer.class, ObjectValue.class, + String.class, ObjectValueOther.class, + Integer.class, ArrayObject.class, + Key.class, GridCacheQueryTestValue.class, + UUID.class, Person.class, + IgniteCacheReplicatedQuerySelfTest.CacheKey.class, IgniteCacheReplicatedQuerySelfTest.CacheValue.class + ); + + if (cacheMode() != CacheMode.LOCAL) + cc.setAffinity(new RendezvousAffinityFunction()); + + // Explicitly set number of backups equal to number of grids. + if (cacheMode() == CacheMode.PARTITIONED) + cc.setBackups(gridCount()); + + cc.setSnapshotableIndex(snapshotableIndex()); ccs[i] = cc; } @@ -198,6 +200,13 @@ public abstract class IgniteCacheAbstractQuerySelfTest extends GridCommonAbstrac return c; } + /** + * @return {@code True} if index snapshot is enabled. + */ + protected boolean snapshotableIndex() { + return false; + } + /** {@inheritDoc} */ @Override protected void beforeTest() throws Exception { ignite = startGridsMultiThreaded(gridCount()); http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCachePartitionedSnapshotEnabledQuerySelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCachePartitionedSnapshotEnabledQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCachePartitionedSnapshotEnabledQuerySelfTest.java new file mode 100644 index 0000000..9a68b16 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/distributed/near/IgniteCachePartitionedSnapshotEnabledQuerySelfTest.java @@ -0,0 +1,28 @@ +/* + * 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.distributed.near; + +/** + * + */ +public class IgniteCachePartitionedSnapshotEnabledQuerySelfTest extends IgniteCachePartitionedQuerySelfTest { + /** {@inheritDoc} */ + @Override protected boolean snapshotableIndex() { + return true; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java index 194fb82..c027b26 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/GridIndexingSpiAbstractSelfTest.java @@ -34,6 +34,7 @@ import org.apache.ignite.internal.processors.cache.CacheObjectContext; import org.apache.ignite.internal.processors.query.GridQueryFieldsResult; import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; import org.apache.ignite.internal.processors.query.GridQueryIndexType; +import org.apache.ignite.internal.processors.query.GridQueryProperty; import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; @@ -505,6 +506,24 @@ public abstract class GridIndexingSpiAbstractSelfTest extends GridCommonAbstract } /** {@inheritDoc} */ + @Override public GridQueryProperty property(final String name) { + return new GridQueryProperty() { + @Override public Object value(Object key, Object val) throws IgniteCheckedException { + return TypeDesc.this.value(name, key, val); + } + + @Override public String name() { + return name; + } + + @Override + public Class<?> type() { + return Object.class; + } + }; + } + + /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public <T> T value(String field, Object key, Object val) throws IgniteSpiException { assert !F.isEmpty(field); http://git-wip-us.apache.org/repos/asf/ignite/blob/4af461a7/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java index 6cc2599..0745cd7 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java @@ -55,6 +55,7 @@ import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheP import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCachePartitionedFieldsQuerySelfTest; import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCachePartitionedQueryP2PDisabledSelfTest; import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCachePartitionedQuerySelfTest; +import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCachePartitionedSnapshotEnabledQuerySelfTest; import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartSelfTest; import org.apache.ignite.internal.processors.cache.distributed.near.IgniteCacheQueryNodeRestartSelfTest2; import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedFieldsQueryP2PEnabledSelfTest; @@ -118,6 +119,7 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite { suite.addTestSuite(IgniteCacheReplicatedQuerySelfTest.class); suite.addTestSuite(IgniteCacheReplicatedQueryP2PDisabledSelfTest.class); suite.addTestSuite(IgniteCachePartitionedQuerySelfTest.class); + suite.addTestSuite(IgniteCachePartitionedSnapshotEnabledQuerySelfTest.class); suite.addTestSuite(IgniteCacheAtomicQuerySelfTest.class); suite.addTestSuite(IgniteCacheAtomicNearEnabledQuerySelfTest.class); suite.addTestSuite(IgniteCachePartitionedQueryP2PDisabledSelfTest.class);