You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/05/26 14:06:18 UTC
[18/39] ignite git commit: IGNITE-5284: Splitted IgniteH2Indexing
into several classes. This closes #1999.
IGNITE-5284: Splitted IgniteH2Indexing into several classes. This closes #1999.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/8c75e4de
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/8c75e4de
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/8c75e4de
Branch: refs/heads/ignite-5075-cc-debug
Commit: 8c75e4de89496e1b80d854fbaa64d3bffa8193bd
Parents: d8eeea8
Author: devozerov <vo...@gridgain.com>
Authored: Wed May 24 15:38:14 2017 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Wed May 24 15:38:14 2017 +0300
----------------------------------------------------------------------
.../query/h2/DmlStatementsProcessor.java | 39 +-
.../query/h2/GridH2ResultSetIterator.java | 191 --
.../query/h2/H2ConnectionWrapper.java | 67 +
.../processors/query/h2/H2DatabaseType.java | 161 ++
.../processors/query/h2/H2FieldsIterator.java | 50 +
.../processors/query/h2/H2KeyValueIterator.java | 48 +
.../query/h2/H2ResultSetIterator.java | 191 ++
.../processors/query/h2/H2RowDescriptor.java | 479 +++++
.../internal/processors/query/h2/H2Schema.java | 135 ++
.../processors/query/h2/H2SqlFieldMetadata.java | 111 +
.../processors/query/h2/H2StatementCache.java | 73 +
.../processors/query/h2/H2TableDescriptor.java | 345 ++++
.../processors/query/h2/H2TableEngine.java | 89 +
.../query/h2/H2TwoStepCachedQuery.java | 49 +
.../query/h2/H2TwoStepCachedQueryKey.java | 107 +
.../internal/processors/query/h2/H2Utils.java | 299 +++
.../processors/query/h2/IgniteH2Indexing.java | 1941 ++----------------
.../query/h2/sql/GridSqlQuerySplitter.java | 4 +-
.../query/h2/twostep/GridMapQueryExecutor.java | 6 +-
.../h2/twostep/GridReduceQueryExecutor.java | 21 +-
20 files changed, 2413 insertions(+), 1993 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
index e40c328..47b5ef4 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
@@ -125,13 +125,13 @@ public class DmlStatementsProcessor {
* @param cacheName Cache name.
*/
public void onCacheStop(String cacheName) {
- planCache.remove(cacheName);
+ planCache.remove(idx.schema(cacheName));
}
/**
* Execute DML statement, possibly with few re-attempts in case of concurrent data modifications.
*
- * @param cacheName Cache name.
+ * @param schema Schema.
* @param stmt JDBC statement.
* @param fieldsQry Original query.
* @param loc Query locality flag.
@@ -140,14 +140,13 @@ public class DmlStatementsProcessor {
* @return Update result (modified items count and failed keys).
* @throws IgniteCheckedException if failed.
*/
- private UpdateResult updateSqlFields(String cacheName, PreparedStatement stmt, SqlFieldsQuery fieldsQry,
- boolean loc, IndexingQueryFilter filters, GridQueryCancel cancel)
- throws IgniteCheckedException {
+ private UpdateResult updateSqlFields(String schema, PreparedStatement stmt, SqlFieldsQuery fieldsQry,
+ boolean loc, IndexingQueryFilter filters, GridQueryCancel cancel) throws IgniteCheckedException {
Object[] errKeys = null;
long items = 0;
- UpdatePlan plan = getPlanForStatement(cacheName, stmt, null);
+ UpdatePlan plan = getPlanForStatement(schema, stmt, null);
GridCacheContext<?, ?> cctx = plan.tbl.rowDescriptor().context();
@@ -195,7 +194,7 @@ public class DmlStatementsProcessor {
}
/**
- * @param cacheName Cache name.
+ * @param schema Schema.
* @param stmt Prepared statement.
* @param fieldsQry Initial query
* @param cancel Query cancel.
@@ -203,9 +202,9 @@ public class DmlStatementsProcessor {
* @throws IgniteCheckedException if failed.
*/
@SuppressWarnings("unchecked")
- QueryCursorImpl<List<?>> updateSqlFieldsDistributed(String cacheName, PreparedStatement stmt,
+ QueryCursorImpl<List<?>> updateSqlFieldsDistributed(String schema, PreparedStatement stmt,
SqlFieldsQuery fieldsQry, GridQueryCancel cancel) throws IgniteCheckedException {
- UpdateResult res = updateSqlFields(cacheName, stmt, fieldsQry, false, null, cancel);
+ UpdateResult res = updateSqlFields(schema, stmt, fieldsQry, false, null, cancel);
QueryCursorImpl<List<?>> resCur = (QueryCursorImpl<List<?>>)new QueryCursorImpl(Collections.singletonList
(Collections.singletonList(res.cnt)), null, false);
@@ -217,7 +216,8 @@ public class DmlStatementsProcessor {
/**
* Execute DML statement on local cache.
- * @param cacheName Cache name.
+ *
+ * @param schema Schema.
* @param stmt Prepared statement.
* @param fieldsQry Fields query.
* @param filters Cache name and key filter.
@@ -226,10 +226,10 @@ public class DmlStatementsProcessor {
* @throws IgniteCheckedException if failed.
*/
@SuppressWarnings("unchecked")
- GridQueryFieldsResult updateSqlFieldsLocal(String cacheName, PreparedStatement stmt,
+ GridQueryFieldsResult updateSqlFieldsLocal(String schema, PreparedStatement stmt,
SqlFieldsQuery fieldsQry, IndexingQueryFilter filters, GridQueryCancel cancel)
throws IgniteCheckedException {
- UpdateResult res = updateSqlFields(cacheName, stmt, fieldsQry, true, filters, cancel);
+ UpdateResult res = updateSqlFields(schema, stmt, fieldsQry, true, filters, cancel);
return new GridQueryFieldsResultAdapter(UPDATE_RESULT_META,
new IgniteSingletonIterator(Collections.singletonList(res.cnt)));
@@ -333,7 +333,7 @@ public class DmlStatementsProcessor {
Object[] failedKeys) throws IgniteCheckedException {
Integer errKeysPos = null;
- UpdatePlan plan = getPlanForStatement(cctx.name(), prepStmt, errKeysPos);
+ UpdatePlan plan = getPlanForStatement(idx.schema(cctx.name()), prepStmt, errKeysPos);
if (plan.fastUpdateArgs != null) {
assert F.isEmpty(failedKeys) && errKeysPos == null;
@@ -398,23 +398,22 @@ public class DmlStatementsProcessor {
/**
* Generate SELECT statements to retrieve data for modifications from and find fast UPDATE or DELETE args,
* if available.
- * @param cacheName Cache name.
+ *
+ * @param schema Schema.
* @param prepStmt JDBC statement.
* @return Update plan.
*/
@SuppressWarnings({"unchecked", "ConstantConditions"})
- private UpdatePlan getPlanForStatement(String cacheName, PreparedStatement prepStmt,
- @Nullable Integer errKeysPos) throws IgniteCheckedException {
+ private UpdatePlan getPlanForStatement(String schema, PreparedStatement prepStmt, @Nullable Integer errKeysPos)
+ throws IgniteCheckedException {
Prepared p = GridSqlQueryParser.prepared(prepStmt);
- cacheName = F.isEmpty(cacheName) ? "default" : cacheName;
-
- ConcurrentMap<String, UpdatePlan> cachePlans = planCache.get(cacheName);
+ ConcurrentMap<String, UpdatePlan> cachePlans = planCache.get(schema);
if (cachePlans == null) {
cachePlans = new GridBoundedConcurrentLinkedHashMap<>(PLAN_CACHE_SIZE);
- cachePlans = U.firstNotNull(planCache.putIfAbsent(cacheName, cachePlans), cachePlans);
+ cachePlans = U.firstNotNull(planCache.putIfAbsent(schema, cachePlans), cachePlans);
}
// getSQL returns field value, so it's fast
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/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
deleted file mode 100644
index fed292a..0000000
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/GridH2ResultSetIterator.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.processors.query.h2;
-
-import java.lang.reflect.Field;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.NoSuchElementException;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.internal.processors.cache.GridCacheContext;
-import org.apache.ignite.internal.processors.query.IgniteSQLException;
-import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
-import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
-import org.apache.ignite.internal.util.typedef.internal.S;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.h2.jdbc.JdbcResultSet;
-import org.h2.result.ResultInterface;
-import org.h2.value.Value;
-
-/**
- * Iterator over result set.
- */
-public abstract class GridH2ResultSetIterator<T> extends GridCloseableIteratorAdapter<T> {
- /** */
- private static final Field RESULT_FIELD;
-
- /**
- * Initialize.
- */
- static {
- try {
- RESULT_FIELD = JdbcResultSet.class.getDeclaredField("result");
-
- RESULT_FIELD.setAccessible(true);
- }
- catch (NoSuchFieldException e) {
- throw new IllegalStateException("Check H2 version in classpath.", e);
- }
- }
-
- /** */
- private static final long serialVersionUID = 0L;
-
- /** */
- private final ResultInterface res;
-
- /** */
- private final ResultSet data;
-
- /** */
- 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.
- * @param needCpy {@code True} if need copy cache object's value.
- * @throws IgniteCheckedException If failed.
- */
- protected GridH2ResultSetIterator(ResultSet data, boolean closeStmt, boolean needCpy) throws IgniteCheckedException {
- this.data = data;
- this.closeStmt = closeStmt;
-
- try {
- res = needCpy ? (ResultInterface)RESULT_FIELD.get(data) : null;
- }
- catch (IllegalAccessException e) {
- throw new IllegalStateException(e); // Must not happen.
- }
-
- if (data != null) {
- try {
- row = new Object[data.getMetaData().getColumnCount()];
- }
- catch (SQLException e) {
- throw new IgniteCheckedException(e);
- }
- }
- else
- row = null;
- }
-
- /**
- * @return {@code true} If next row was fetched successfully.
- */
- private boolean fetchNext() {
- if (data == null)
- return false;
-
- try {
- if (!data.next())
- return false;
-
- if (res != null) {
- Value[] values = res.currentRow();
-
- for (int c = 0; c < row.length; c++) {
- Value val = values[c];
-
- if (val instanceof GridH2ValueCacheObject) {
- GridH2ValueCacheObject valCacheObj = (GridH2ValueCacheObject)values[c];
-
- GridCacheContext cctx = valCacheObj.getCacheContext();
-
- row[c] = valCacheObj.getObject(cctx != null && cctx.needValueCopy());
- }
- else
- row[c] = val.getObject();
- }
- }
- else {
- for (int c = 0; c < row.length; c++)
- row[c] = data.getObject(c + 1);
- }
-
- return true;
- }
- catch (SQLException e) {
- throw new IgniteSQLException(e);
- }
- }
-
- /** {@inheritDoc} */
- @Override public boolean onHasNext() {
- return hasRow || (hasRow = fetchNext());
- }
-
- /** {@inheritDoc} */
- @SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException")
- @Override public T onNext() {
- if (!hasNext())
- throw new NoSuchElementException();
-
- hasRow = false;
-
- return createRow();
- }
-
- /**
- * @return Row.
- */
- protected abstract T createRow();
-
- /** {@inheritDoc} */
- @Override public void onRemove() {
- throw new UnsupportedOperationException();
- }
-
- /** {@inheritDoc} */
- @Override public void onClose() throws IgniteCheckedException {
- if (data == null)
- // Nothing to close.
- return;
-
- if (closeStmt) {
- try {
- U.closeQuiet(data.getStatement());
- }
- catch (SQLException e) {
- throw new IgniteCheckedException(e);
- }
- }
-
- U.closeQuiet(data);
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return S.toString(GridH2ResultSetIterator.class, this);
- }
-}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
new file mode 100644
index 0000000..e180c9c
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ConnectionWrapper.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+import java.sql.Connection;
+
+/**
+ * Wrapper to store connection and flag is schema set or not.
+ */
+public class H2ConnectionWrapper {
+ /** */
+ private Connection conn;
+
+ /** */
+ private volatile String schema;
+
+ /**
+ * @param conn Connection to use.
+ */
+ H2ConnectionWrapper(Connection conn) {
+ this.conn = conn;
+ }
+
+ /**
+ * @return Schema name if schema is set, null otherwise.
+ */
+ public String schema() {
+ return schema;
+ }
+
+ /**
+ * @param schema Schema name set on this connection.
+ */
+ public void schema(@Nullable String schema) {
+ this.schema = schema;
+ }
+
+ /**
+ * @return Connection.
+ */
+ public Connection connection() {
+ return conn;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2ConnectionWrapper.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DatabaseType.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DatabaseType.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DatabaseType.java
new file mode 100644
index 0000000..47c7eb9
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2DatabaseType.java
@@ -0,0 +1,161 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.h2.value.DataType;
+
+import java.math.BigDecimal;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Enum that helps to map java types to database types.
+ */
+public enum H2DatabaseType {
+ /** */
+ INT("INT"),
+
+ /** */
+ BOOL("BOOL"),
+
+ /** */
+ TINYINT("TINYINT"),
+
+ /** */
+ SMALLINT("SMALLINT"),
+
+ /** */
+ BIGINT("BIGINT"),
+
+ /** */
+ DECIMAL("DECIMAL"),
+
+ /** */
+ DOUBLE("DOUBLE"),
+
+ /** */
+ REAL("REAL"),
+
+ /** */
+ TIME("TIME"),
+
+ /** */
+ TIMESTAMP("TIMESTAMP"),
+
+ /** */
+ DATE("DATE"),
+
+ /** */
+ VARCHAR("VARCHAR"),
+
+ /** */
+ CHAR("CHAR"),
+
+ /** */
+ BINARY("BINARY"),
+
+ /** */
+ UUID("UUID"),
+
+ /** */
+ ARRAY("ARRAY"),
+
+ /** */
+ GEOMETRY("GEOMETRY"),
+
+ /** */
+ OTHER("OTHER");
+
+ /** Map of Class to enum. */
+ private static final Map<Class<?>, H2DatabaseType> map = new HashMap<>();
+
+ /**
+ * Initialize map of DB types.
+ */
+ static {
+ map.put(int.class, INT);
+ map.put(Integer.class, INT);
+ map.put(boolean.class, BOOL);
+ map.put(Boolean.class, BOOL);
+ map.put(byte.class, TINYINT);
+ map.put(Byte.class, TINYINT);
+ map.put(short.class, SMALLINT);
+ map.put(Short.class, SMALLINT);
+ map.put(long.class, BIGINT);
+ map.put(Long.class, BIGINT);
+ map.put(BigDecimal.class, DECIMAL);
+ map.put(double.class, DOUBLE);
+ map.put(Double.class, DOUBLE);
+ map.put(float.class, REAL);
+ map.put(Float.class, REAL);
+ map.put(Time.class, TIME);
+ map.put(Timestamp.class, TIMESTAMP);
+ map.put(java.util.Date.class, TIMESTAMP);
+ map.put(java.sql.Date.class, DATE);
+ map.put(String.class, VARCHAR);
+ map.put(java.util.UUID.class, UUID);
+ map.put(byte[].class, BINARY);
+ }
+
+ /** */
+ private final String dbType;
+
+ /**
+ * Constructs new instance.
+ *
+ * @param dbType DB type name.
+ */
+ H2DatabaseType(String dbType) {
+ this.dbType = dbType;
+ }
+
+ /**
+ * Resolves enum by class.
+ *
+ * @param cls Class.
+ * @return Enum value.
+ */
+ public static H2DatabaseType fromClass(Class<?> cls) {
+ H2DatabaseType res = map.get(cls);
+
+ if (res != null)
+ return res;
+
+ if (DataType.isGeometryClass(cls))
+ return GEOMETRY;
+
+ return cls.isArray() && !cls.getComponentType().isPrimitive() ? ARRAY : OTHER;
+ }
+
+ /**
+ * Gets DB type name.
+ *
+ * @return DB type name.
+ */
+ public String dBTypeAsString() {
+ return dbType;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2DatabaseType.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
new file mode 100644
index 0000000..f300c3f
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2FieldsIterator.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import org.apache.ignite.IgniteCheckedException;
+
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Special field set iterator based on database result set.
+ */
+public class H2FieldsIterator extends H2ResultSetIterator<List<?>> {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param data Data.
+ * @throws IgniteCheckedException If failed.
+ */
+ public H2FieldsIterator(ResultSet data) throws IgniteCheckedException {
+ super(data, false, true);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected List<?> createRow() {
+ ArrayList<Object> res = new ArrayList<>(row.length);
+
+ Collections.addAll(res, row);
+
+ return res;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
new file mode 100644
index 0000000..2088e44
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2KeyValueIterator.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.lang.IgniteBiTuple;
+
+import java.sql.ResultSet;
+
+/**
+ * Special key/value iterator based on database result set.
+ */
+public class H2KeyValueIterator<K, V> extends H2ResultSetIterator<IgniteBiTuple<K, V>> {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param data Data array.
+ * @throws IgniteCheckedException If failed.
+ */
+ protected H2KeyValueIterator(ResultSet data) throws IgniteCheckedException {
+ super(data, false, true);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override protected IgniteBiTuple<K, V> createRow() {
+ K key = (K)row[0];
+ V val = (V)row[1];
+
+ return new IgniteBiTuple<>(key, val);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
new file mode 100644
index 0000000..494f069
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2ResultSetIterator.java
@@ -0,0 +1,191 @@
+/*
+ * 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;
+
+import java.lang.reflect.Field;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.NoSuchElementException;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
+import org.apache.ignite.internal.util.GridCloseableIteratorAdapter;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.h2.jdbc.JdbcResultSet;
+import org.h2.result.ResultInterface;
+import org.h2.value.Value;
+
+/**
+ * Iterator over result set.
+ */
+public abstract class H2ResultSetIterator<T> extends GridCloseableIteratorAdapter<T> {
+ /** */
+ private static final Field RESULT_FIELD;
+
+ /**
+ * Initialize.
+ */
+ static {
+ try {
+ RESULT_FIELD = JdbcResultSet.class.getDeclaredField("result");
+
+ RESULT_FIELD.setAccessible(true);
+ }
+ catch (NoSuchFieldException e) {
+ throw new IllegalStateException("Check H2 version in classpath.", e);
+ }
+ }
+
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private final ResultInterface res;
+
+ /** */
+ private final ResultSet data;
+
+ /** */
+ 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.
+ * @param needCpy {@code True} if need copy cache object's value.
+ * @throws IgniteCheckedException If failed.
+ */
+ protected H2ResultSetIterator(ResultSet data, boolean closeStmt, boolean needCpy) throws IgniteCheckedException {
+ this.data = data;
+ this.closeStmt = closeStmt;
+
+ try {
+ res = needCpy ? (ResultInterface)RESULT_FIELD.get(data) : null;
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException(e); // Must not happen.
+ }
+
+ if (data != null) {
+ try {
+ row = new Object[data.getMetaData().getColumnCount()];
+ }
+ catch (SQLException e) {
+ throw new IgniteCheckedException(e);
+ }
+ }
+ else
+ row = null;
+ }
+
+ /**
+ * @return {@code true} If next row was fetched successfully.
+ */
+ private boolean fetchNext() {
+ if (data == null)
+ return false;
+
+ try {
+ if (!data.next())
+ return false;
+
+ if (res != null) {
+ Value[] values = res.currentRow();
+
+ for (int c = 0; c < row.length; c++) {
+ Value val = values[c];
+
+ if (val instanceof GridH2ValueCacheObject) {
+ GridH2ValueCacheObject valCacheObj = (GridH2ValueCacheObject)values[c];
+
+ GridCacheContext cctx = valCacheObj.getCacheContext();
+
+ row[c] = valCacheObj.getObject(cctx != null && cctx.needValueCopy());
+ }
+ else
+ row[c] = val.getObject();
+ }
+ }
+ else {
+ for (int c = 0; c < row.length; c++)
+ row[c] = data.getObject(c + 1);
+ }
+
+ return true;
+ }
+ catch (SQLException e) {
+ throw new IgniteSQLException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean onHasNext() {
+ return hasRow || (hasRow = fetchNext());
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("IteratorNextCanNotThrowNoSuchElementException")
+ @Override public T onNext() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ hasRow = false;
+
+ return createRow();
+ }
+
+ /**
+ * @return Row.
+ */
+ protected abstract T createRow();
+
+ /** {@inheritDoc} */
+ @Override public void onRemove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void onClose() throws IgniteCheckedException {
+ if (data == null)
+ // Nothing to close.
+ return;
+
+ if (closeStmt) {
+ try {
+ U.closeQuiet(data.getStatement());
+ }
+ catch (SQLException e) {
+ throw new IgniteCheckedException(e);
+ }
+ }
+
+ U.closeQuiet(data);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2ResultSetIterator.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java
new file mode 100644
index 0000000..6f5ce3e
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2RowDescriptor.java
@@ -0,0 +1,479 @@
+/*
+ * 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;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.CacheObject;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.cache.KeyCacheObject;
+import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+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.GridH2KeyValueRowOffheap;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2ValueCacheObject;
+import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeGuard;
+import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
+import org.h2.message.DbException;
+import org.h2.result.SearchRow;
+import org.h2.result.SimpleRow;
+import org.h2.value.DataType;
+import org.h2.value.Value;
+import org.h2.value.ValueArray;
+import org.h2.value.ValueBoolean;
+import org.h2.value.ValueByte;
+import org.h2.value.ValueBytes;
+import org.h2.value.ValueDate;
+import org.h2.value.ValueDecimal;
+import org.h2.value.ValueDouble;
+import org.h2.value.ValueFloat;
+import org.h2.value.ValueGeometry;
+import org.h2.value.ValueInt;
+import org.h2.value.ValueJavaObject;
+import org.h2.value.ValueLong;
+import org.h2.value.ValueNull;
+import org.h2.value.ValueShort;
+import org.h2.value.ValueString;
+import org.h2.value.ValueTime;
+import org.h2.value.ValueTimestamp;
+import org.h2.value.ValueUuid;
+import org.jetbrains.annotations.Nullable;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.DEFAULT_COLUMNS_COUNT;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VAL_COL;
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.VER_COL;
+
+/**
+ * Row descriptor.
+ */
+public class H2RowDescriptor implements GridH2RowDescriptor {
+ /** Indexing SPI. */
+ private final IgniteH2Indexing idx;
+
+ /** */
+ private final GridQueryTypeDescriptor type;
+
+ /** */
+ private final String[] fields;
+
+ /** */
+ private final int[] fieldTypes;
+
+ /** */
+ private final int keyType;
+
+ /** */
+ private final int valType;
+
+ /** */
+ private final H2Schema schema;
+
+ /** */
+ private final GridUnsafeGuard guard;
+
+ /** */
+ private final boolean snapshotableIdx;
+
+ /** */
+ private final GridQueryProperty[] props;
+
+ /** Id of user-defined key column */
+ private final int keyAliasColumnId;
+
+ /** Id of user-defined value column */
+ private final int valueAliasColumnId;
+
+ /**
+ * @param type Type descriptor.
+ * @param schema Schema.
+ */
+ H2RowDescriptor(IgniteH2Indexing idx, GridQueryTypeDescriptor type, H2Schema schema) {
+ assert type != null;
+ assert schema != null;
+
+ this.idx = idx;
+ this.type = type;
+ this.schema = schema;
+
+ guard = schema.offheap() == null ? null : new GridUnsafeGuard();
+
+ Map<String, Class<?>> allFields = new LinkedHashMap<>();
+
+ allFields.putAll(type.fields());
+
+ fields = allFields.keySet().toArray(new String[allFields.size()]);
+
+ fieldTypes = new int[fields.length];
+
+ Class[] classes = allFields.values().toArray(new Class[fields.length]);
+
+ for (int i = 0; i < fieldTypes.length; i++)
+ fieldTypes[i] = DataType.getTypeFromClass(classes[i]);
+
+ 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;
+ }
+
+ final List<String> fieldsList = Arrays.asList(fields);
+ keyAliasColumnId = (type.keyFieldName() != null) ? DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.keyFieldName()) : -1;
+ valueAliasColumnId = (type.valueFieldName() != null) ? DEFAULT_COLUMNS_COUNT + fieldsList.indexOf(type.valueFieldName()) : -1;
+
+ // Index is not snapshotable in db-x.
+ snapshotableIdx = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IgniteH2Indexing indexing() {
+ return idx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridQueryTypeDescriptor type() {
+ return type;
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridCacheContext<?, ?> context() {
+ return schema.cacheContext();
+ }
+
+ /** {@inheritDoc} */
+ @Override public CacheConfiguration configuration() {
+ return schema.cacheContext().config();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridUnsafeGuard guard() {
+ return guard;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void cache(GridH2Row row) {
+ long ptr = row.pointer();
+
+ assert ptr > 0 : ptr;
+
+ schema.rowCache().put(ptr, row);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void uncache(long ptr) {
+ schema.rowCache().remove(ptr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridUnsafeMemory memory() {
+ return schema.offheap();
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("ConstantConditions")
+ @Override public Value wrap(Object obj, int type) throws IgniteCheckedException {
+ assert obj != null;
+
+ if (obj instanceof CacheObject) { // Handle cache object.
+ CacheObject co = (CacheObject)obj;
+
+ if (type == Value.JAVA_OBJECT)
+ return new GridH2ValueCacheObject(idx.cacheContext(schema.cacheName()), co);
+
+ obj = co.value(idx.objectContext(schema.cacheName()), false);
+ }
+
+ switch (type) {
+ case Value.BOOLEAN:
+ return ValueBoolean.get((Boolean)obj);
+ case Value.BYTE:
+ return ValueByte.get((Byte)obj);
+ case Value.SHORT:
+ return ValueShort.get((Short)obj);
+ case Value.INT:
+ return ValueInt.get((Integer)obj);
+ case Value.FLOAT:
+ return ValueFloat.get((Float)obj);
+ case Value.LONG:
+ return ValueLong.get((Long)obj);
+ case Value.DOUBLE:
+ return ValueDouble.get((Double)obj);
+ case Value.UUID:
+ UUID uuid = (UUID)obj;
+ return ValueUuid.get(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
+ case Value.DATE:
+ return ValueDate.get((Date)obj);
+ case Value.TIME:
+ return ValueTime.get((Time)obj);
+ case Value.TIMESTAMP:
+ if (obj instanceof java.util.Date && !(obj instanceof Timestamp))
+ obj = new Timestamp(((java.util.Date)obj).getTime());
+
+ return ValueTimestamp.get((Timestamp)obj);
+ case Value.DECIMAL:
+ return ValueDecimal.get((BigDecimal)obj);
+ case Value.STRING:
+ return ValueString.get(obj.toString());
+ case Value.BYTES:
+ return ValueBytes.get((byte[])obj);
+ case Value.JAVA_OBJECT:
+ return ValueJavaObject.getNoCopy(obj, null, null);
+ case Value.ARRAY:
+ Object[] arr = (Object[])obj;
+
+ Value[] valArr = new Value[arr.length];
+
+ for (int i = 0; i < arr.length; i++) {
+ Object o = arr[i];
+
+ valArr[i] = o == null ? ValueNull.INSTANCE : wrap(o, DataType.getTypeFromClass(o.getClass()));
+ }
+
+ return ValueArray.get(valArr);
+
+ case Value.GEOMETRY:
+ return ValueGeometry.getFromGeometry(obj);
+ }
+
+ throw new IgniteCheckedException("Failed to wrap value[type=" + type + ", value=" + obj + "]");
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridH2Row createRow(KeyCacheObject key, int partId, @Nullable CacheObject val,
+ GridCacheVersion ver,
+ long expirationTime) throws IgniteCheckedException {
+ GridH2Row row;
+
+ try {
+ if (val == null) // Only can happen for remove operation, can create simple search row.
+ row = GridH2RowFactory.create(wrap(key, keyType));
+ else
+ row = schema.offheap() == null ?
+ new GridH2KeyValueRowOnheap(this, key, keyType, val, valType, ver, expirationTime) :
+ new GridH2KeyValueRowOffheap(this, key, keyType, val, valType, ver, expirationTime);
+ }
+ catch (ClassCastException e) {
+ throw new IgniteCheckedException("Failed to convert key to SQL type. " +
+ "Please make sure that you always store each value type with the same key type " +
+ "or configure key type as common super class for all actual keys for this value type.", e);
+ }
+
+ GridCacheContext cctx = idx.cacheContext(schema.cacheName());
+
+ if (cctx.offheapIndex()) {
+ row.ver = ver;
+
+ row.key = key;
+ row.val = val;
+ row.partId = partId;
+ }
+
+ return row;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int valueType() {
+ return valType;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int fieldsCount() {
+ return fields.length;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int fieldType(int col) {
+ return fieldTypes[col];
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object columnValue(Object key, Object val, int col) {
+ try {
+ return props[col].value(key, val);
+ }
+ catch (IgniteCheckedException e) {
+ throw DbException.convert(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void setColumnValue(Object key, Object val, Object colVal, int col) {
+ try {
+ props[col].setValue(key, val, colVal);
+ }
+ catch (IgniteCheckedException e) {
+ throw DbException.convert(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isColumnKeyProperty(int col) {
+ return props[col].key();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridH2KeyValueRowOffheap createPointer(long ptr) {
+ GridH2KeyValueRowOffheap row = (GridH2KeyValueRowOffheap)schema.rowCache().get(ptr);
+
+ if (row != null) {
+ assert row.pointer() == ptr : ptr + " " + row.pointer();
+
+ return row;
+ }
+
+ return new GridH2KeyValueRowOffheap(this, ptr);
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridH2Row cachedRow(long link) {
+ return schema.rowCache().get(link);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean snapshotableIndex() {
+ return snapshotableIdx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isKeyColumn(int columnId) {
+ assert columnId >= 0;
+ return columnId == KEY_COL || columnId == keyAliasColumnId;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean isValueColumn(int columnId) {
+ assert columnId >= 0;
+ return columnId == VAL_COL || columnId == valueAliasColumnId;
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("RedundantIfStatement")
+ @Override public boolean isKeyValueOrVersionColumn(int columnId) {
+ assert columnId >= 0;
+
+ if (columnId < DEFAULT_COLUMNS_COUNT)
+ return true;
+
+ if (columnId == keyAliasColumnId)
+ return true;
+
+ if (columnId == valueAliasColumnId)
+ return true;
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean checkKeyIndexCondition(int masks[], int mask) {
+ assert masks != null;
+ assert masks.length > 0;
+
+ if (keyAliasColumnId < 0)
+ return (masks[KEY_COL] & mask) != 0;
+ else
+ return (masks[KEY_COL] & mask) != 0 || (masks[keyAliasColumnId] & mask) != 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void initValueCache(Value valCache[], Value key, Value value, Value version) {
+ assert valCache != null;
+ assert valCache.length > 0;
+
+ valCache[KEY_COL] = key;
+ valCache[VAL_COL] = value;
+ valCache[VER_COL] = version;
+
+ if (keyAliasColumnId > 0)
+ valCache[keyAliasColumnId] = key;
+
+ if (valueAliasColumnId > 0)
+ valCache[valueAliasColumnId] = value;
+ }
+
+ /** {@inheritDoc} */
+ @Override public SearchRow prepareProxyIndexRow(SearchRow row) {
+ if (row == null)
+ return null;
+
+ Value[] data = new Value[row.getColumnCount()];
+ for (int idx = 0; idx < data.length; idx++)
+ data[idx] = row.getValue(idx);
+
+ copyAliasColumnData(data, KEY_COL, keyAliasColumnId);
+ copyAliasColumnData(data, VAL_COL, valueAliasColumnId);
+
+ return new SimpleRow(data);
+ }
+
+ /**
+ * Copies data between original and alias columns
+ *
+ * @param data Array of values.
+ * @param colId Original column id.
+ * @param aliasColId Alias column id.
+ */
+ private void copyAliasColumnData(Value[] data, int colId, int aliasColId) {
+ if (aliasColId <= 0)
+ return;
+
+ if (data[aliasColId] == null && data[colId] != null)
+ data[aliasColId] = data[colId];
+
+ if (data[colId] == null && data[aliasColId] != null)
+ data[colId] = data[aliasColId];
+ }
+
+ /** {@inheritDoc} */
+ @Override public int getAlternativeColumnId(int colId) {
+ if (keyAliasColumnId > 0) {
+ if (colId == KEY_COL)
+ return keyAliasColumnId;
+ else if (colId == keyAliasColumnId)
+ return KEY_COL;
+ }
+ if (valueAliasColumnId > 0) {
+ if (colId == VAL_COL)
+ return valueAliasColumnId;
+ else if (colId == valueAliasColumnId)
+ return VAL_COL;
+ }
+
+ return colId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
new file mode 100644
index 0000000..603a0c1
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Schema.java
@@ -0,0 +1,135 @@
+/*
+ * 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;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Row;
+import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
+import org.h2.mvstore.cache.CacheLongKeyLIRS;
+import org.jsr166.ConcurrentHashMap8;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Database schema object.
+ */
+public class H2Schema {
+ /** */
+ private final String cacheName;
+
+ /** */
+ private final String schemaName;
+
+ /** */
+ private final GridUnsafeMemory offheap = null;
+
+ /** */
+ private final ConcurrentMap<String, H2TableDescriptor> tbls = new ConcurrentHashMap8<>();
+
+ /** Cache for deserialized offheap rows. */
+ private final CacheLongKeyLIRS<GridH2Row> rowCache;
+
+ /** */
+ private final GridCacheContext<?, ?> cctx;
+
+ /** */
+ private final CacheConfiguration<?, ?> ccfg;
+
+ /**
+ * @param cacheName Cache name.
+ * @param schemaName Schema name.
+ * @param cctx Cache context.
+ * @param ccfg Cache configuration.
+ */
+ H2Schema(String cacheName, String schemaName, GridCacheContext<?, ?> cctx,
+ CacheConfiguration<?, ?> ccfg) {
+ this.cacheName = cacheName;
+ this.cctx = cctx;
+ this.schemaName = schemaName;
+ this.ccfg = ccfg;
+
+ rowCache = null;
+ }
+
+ /**
+ * @return Cache context.
+ */
+ public GridCacheContext cacheContext() {
+ return cctx;
+ }
+
+ /**
+ * @return Cache name.
+ */
+ public String cacheName() {
+ return cacheName;
+ }
+
+ /**
+ * @return Schema name.
+ */
+ public String schemaName() {
+ return schemaName;
+ }
+
+ /**
+ * @return Unsafe memory.
+ */
+ public GridUnsafeMemory offheap() {
+ return offheap;
+ }
+
+ /**
+ * @return Row cache.
+ */
+ public CacheLongKeyLIRS<GridH2Row> rowCache() {
+ return rowCache;
+ }
+
+ /**
+ * @return Tables.
+ */
+ public Map<String, H2TableDescriptor> tables() {
+ return tbls;
+ }
+
+ /**
+ * @param tbl Table descriptor.
+ */
+ public void add(H2TableDescriptor tbl) {
+ if (tbls.putIfAbsent(tbl.typeName(), tbl) != null)
+ throw new IllegalStateException("Table already registered: " + tbl.fullTableName());
+ }
+
+ /**
+ * @return Escape all.
+ */
+ public boolean escapeAll() {
+ return ccfg.isSqlEscapeAll();
+ }
+
+ /**
+ * Called after the schema was dropped.
+ */
+ public void onDrop() {
+ for (H2TableDescriptor tblDesc : tbls.values())
+ tblDesc.onDrop();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java
new file mode 100644
index 0000000..46aa1fc
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Field descriptor.
+ */
+public class H2SqlFieldMetadata implements GridQueryFieldMetadata {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Schema name. */
+ private String schemaName;
+
+ /** Type name. */
+ private String typeName;
+
+ /** Name. */
+ private String name;
+
+ /** Type. */
+ private String type;
+
+ /**
+ * Required by {@link Externalizable}.
+ */
+ public H2SqlFieldMetadata() {
+ // No-op
+ }
+
+ /**
+ * @param schemaName Schema name.
+ * @param typeName Type name.
+ * @param name Name.
+ * @param type Type.
+ */
+ H2SqlFieldMetadata(@Nullable String schemaName, @Nullable String typeName, String name, String type) {
+ assert name != null && type != null : schemaName + " | " + typeName + " | " + name + " | " + type;
+
+ this.schemaName = schemaName;
+ this.typeName = typeName;
+ this.name = name;
+ this.type = type;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String schemaName() {
+ return schemaName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String typeName() {
+ return typeName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String fieldName() {
+ return name;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String fieldTypeName() {
+ return type;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeExternal(ObjectOutput out) throws IOException {
+ U.writeString(out, schemaName);
+ U.writeString(out, typeName);
+ U.writeString(out, name);
+ U.writeString(out, type);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ schemaName = U.readString(in);
+ typeName = U.readString(in);
+ name = U.readString(in);
+ type = U.readString(in);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2SqlFieldMetadata.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
new file mode 100644
index 0000000..d395112
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2StatementCache.java
@@ -0,0 +1,73 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+import java.sql.PreparedStatement;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Statement cache.
+ */
+public class H2StatementCache extends LinkedHashMap<String, PreparedStatement> {
+ /** */
+ private int size;
+
+ /** Last usage. */
+ private volatile long lastUsage;
+
+ /**
+ * @param size Size.
+ */
+ H2StatementCache(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;
+ }
+
+ /**
+ * The timestamp of the last usage of the cache.
+ *
+ * @return last usage timestamp
+ */
+ public long lastUsage() {
+ return lastUsage;
+ }
+
+ /**
+ * Updates the {@link #lastUsage} timestamp by current time.
+ */
+ public void updateLastUsage() {
+ lastUsage = U.currentTimeMillis();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
new file mode 100644
index 0000000..a9548aa
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableDescriptor.java
@@ -0,0 +1,345 @@
+/*
+ * 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;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.cache.QueryIndexType;
+import org.apache.ignite.internal.processors.cache.GridCacheContext;
+import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
+import org.apache.ignite.internal.processors.query.h2.database.H2PkHashIndex;
+import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2IndexBase;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2SystemIndexFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.apache.ignite.internal.processors.query.h2.opt.GridLuceneIndex;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.h2.index.Index;
+import org.h2.result.SortOrder;
+import org.h2.table.Column;
+import org.h2.table.IndexColumn;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.apache.ignite.internal.processors.query.h2.opt.GridH2AbstractKeyValueRow.KEY_COL;
+
+/**
+ * Information about table in database.
+ */
+public class H2TableDescriptor implements GridH2SystemIndexFactory {
+ /** Indexing. */
+ private final IgniteH2Indexing idx;
+
+ /** */
+ private final String fullTblName;
+
+ /** */
+ private final GridQueryTypeDescriptor type;
+
+ /** */
+ private final H2Schema schema;
+
+ /** */
+ private GridH2Table tbl;
+
+ /** */
+ private GridLuceneIndex luceneIdx;
+
+ /** */
+ private H2PkHashIndex pkHashIdx;
+
+ /**
+ * Constructor.
+ *
+ * @param idx Indexing.
+ * @param schema Schema.
+ * @param type Type descriptor.
+ */
+ H2TableDescriptor(IgniteH2Indexing idx, H2Schema schema, GridQueryTypeDescriptor type) {
+ this.idx = idx;
+ this.type = type;
+ this.schema = schema;
+
+ String tblName = H2Utils.escapeName(type.tableName(), schema.escapeAll());
+
+ fullTblName = schema.schemaName() + "." + tblName;
+ }
+
+ /**
+ * @return Primary key hash index.
+ */
+ H2PkHashIndex primaryKeyHashIndex() {
+ return pkHashIdx;
+ }
+
+ /**
+ * @return Table.
+ */
+ public GridH2Table table() {
+ return tbl;
+ }
+
+ /**
+ * @param tbl Table.
+ */
+ public void table(GridH2Table tbl) {
+ this.tbl = tbl;
+ }
+
+ /**
+ * @return Schema.
+ */
+ public H2Schema schema() {
+ return schema;
+ }
+
+ /**
+ * @return Schema name.
+ */
+ public String schemaName() {
+ return schema.schemaName();
+ }
+
+ /**
+ * @return Database full table name.
+ */
+ String fullTableName() {
+ return fullTblName;
+ }
+
+ /**
+ * @return type name.
+ */
+ String typeName() {
+ return type.name();
+ }
+
+ /**
+ * @return Type.
+ */
+ GridQueryTypeDescriptor type() {
+ return type;
+ }
+
+ /**
+ * @return Lucene index.
+ */
+ GridLuceneIndex luceneIndex() {
+ return luceneIdx;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2TableDescriptor.class, this);
+ }
+
+ /**
+ * Create H2 row factory.
+ *
+ * @param rowDesc Row descriptor.
+ * @return H2 row factory.
+ */
+ H2RowFactory rowFactory(GridH2RowDescriptor rowDesc) {
+ GridCacheContext cctx = schema.cacheContext();
+
+ if (cctx.affinityNode() && cctx.offheapIndex())
+ return new H2RowFactory(rowDesc, cctx);
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public ArrayList<Index> createSystemIndexes(GridH2Table tbl) {
+ ArrayList<Index> idxs = new ArrayList<>();
+
+ IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
+ IndexColumn affCol = tbl.getAffinityKeyColumn();
+
+ if (affCol != null && H2Utils.equals(affCol, keyCol))
+ affCol = null;
+
+ GridH2RowDescriptor desc = tbl.rowDescriptor();
+
+ Index hashIdx = createHashIndex(
+ schema,
+ tbl,
+ "_key_PK_hash",
+ H2Utils.treeIndexColumns(desc, new ArrayList<IndexColumn>(2), keyCol, affCol)
+ );
+
+ if (hashIdx != null)
+ idxs.add(hashIdx);
+
+ // Add primary key index.
+ Index pkIdx = idx.createSortedIndex(
+ schema,
+ "_key_PK",
+ tbl,
+ true,
+ H2Utils.treeIndexColumns(desc, new ArrayList<IndexColumn>(2), keyCol, affCol),
+ -1
+ );
+
+ idxs.add(pkIdx);
+
+ if (type().valueClass() == String.class) {
+ try {
+ luceneIdx = new GridLuceneIndex(idx.kernalContext(), schema.offheap(), schema.cacheName(), type);
+ }
+ catch (IgniteCheckedException e1) {
+ throw new IgniteException(e1);
+ }
+ }
+
+ boolean affIdxFound = false;
+
+ GridQueryIndexDescriptor textIdx = type.textIndex();
+
+ if (textIdx != null) {
+ try {
+ luceneIdx = new GridLuceneIndex(idx.kernalContext(), schema.offheap(), schema.cacheName(), type);
+ }
+ catch (IgniteCheckedException e1) {
+ throw new IgniteException(e1);
+ }
+ }
+
+ // Locate index where affinity column is first (if any).
+ if (affCol != null) {
+ for (GridQueryIndexDescriptor idxDesc : type.indexes().values()) {
+ if (idxDesc.type() != QueryIndexType.SORTED)
+ continue;
+
+ String firstField = idxDesc.fields().iterator().next();
+
+ String firstFieldName =
+ schema.escapeAll() ? firstField : H2Utils.escapeName(firstField, false).toUpperCase();
+
+ Column col = tbl.getColumn(firstFieldName);
+
+ IndexColumn idxCol = tbl.indexColumn(col.getColumnId(),
+ idxDesc.descending(firstField) ? SortOrder.DESCENDING : SortOrder.ASCENDING);
+
+ affIdxFound |= H2Utils.equals(idxCol, affCol);
+ }
+ }
+
+ // Add explicit affinity key index if nothing alike was found.
+ if (affCol != null && !affIdxFound) {
+ idxs.add(idx.createSortedIndex(schema, "AFFINITY_KEY", tbl, false,
+ H2Utils.treeIndexColumns(desc, new ArrayList<IndexColumn>(2), affCol, keyCol), -1));
+ }
+
+ return idxs;
+ }
+
+ /**
+ * Get collection of user indexes.
+ *
+ * @return User indexes.
+ */
+ public Collection<GridH2IndexBase> createUserIndexes() {
+ assert tbl != null;
+
+ ArrayList<GridH2IndexBase> res = new ArrayList<>();
+
+ for (GridQueryIndexDescriptor idxDesc : type.indexes().values()) {
+ GridH2IndexBase idx = createUserIndex(idxDesc);
+
+ res.add(idx);
+ }
+
+ return res;
+ }
+
+ /**
+ * Create user index.
+ *
+ * @param idxDesc Index descriptor.
+ * @return Index.
+ */
+ public GridH2IndexBase createUserIndex(GridQueryIndexDescriptor idxDesc) {
+ String name = schema.escapeAll() ? idxDesc.name() : H2Utils.escapeName(idxDesc.name(), false).toUpperCase();
+
+ IndexColumn keyCol = tbl.indexColumn(KEY_COL, SortOrder.ASCENDING);
+ IndexColumn affCol = tbl.getAffinityKeyColumn();
+
+ List<IndexColumn> cols = new ArrayList<>(idxDesc.fields().size() + 2);
+
+ boolean escapeAll = schema.escapeAll();
+
+ for (String field : idxDesc.fields()) {
+ String fieldName = escapeAll ? field : H2Utils.escapeName(field, false).toUpperCase();
+
+ Column col = tbl.getColumn(fieldName);
+
+ cols.add(tbl.indexColumn(col.getColumnId(),
+ idxDesc.descending(field) ? SortOrder.DESCENDING : SortOrder.ASCENDING));
+ }
+
+ GridH2RowDescriptor desc = tbl.rowDescriptor();
+ if (idxDesc.type() == QueryIndexType.SORTED) {
+ cols = H2Utils.treeIndexColumns(desc, cols, keyCol, affCol);
+ return idx.createSortedIndex(schema, name, tbl, false, cols, idxDesc.inlineSize());
+ }
+ else if (idxDesc.type() == QueryIndexType.GEOSPATIAL) {
+ return H2Utils.createSpatialIndex(tbl, name, cols.toArray(new IndexColumn[cols.size()]));
+ }
+
+ throw new IllegalStateException("Index type: " + idxDesc.type());
+ }
+
+ /**
+ * Create hash index.
+ *
+ * @param schema Schema.
+ * @param tbl Table.
+ * @param idxName Index name.
+ * @param cols Columns.
+ * @return Index.
+ */
+ private Index createHashIndex(H2Schema schema, GridH2Table tbl, String idxName, List<IndexColumn> cols) {
+ GridCacheContext cctx = schema.cacheContext();
+
+ if (cctx.affinityNode() && cctx.offheapIndex()) {
+ assert pkHashIdx == null : pkHashIdx;
+
+ pkHashIdx = new H2PkHashIndex(cctx, tbl, idxName, cols);
+
+ return pkHashIdx;
+ }
+
+ return null;
+ }
+
+ /**
+ * Handle drop.
+ */
+ void onDrop() {
+ idx.removeDataTable(tbl);
+
+ tbl.destroy();
+
+ U.closeQuiet(luceneIdx);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java
new file mode 100644
index 0000000..4cf5166
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TableEngine.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor;
+import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table;
+import org.h2.api.TableEngine;
+import org.h2.command.ddl.CreateTableData;
+import org.h2.table.TableBase;
+import org.jetbrains.annotations.Nullable;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * H2 Table engine.
+ */
+public class H2TableEngine implements TableEngine {
+ /** */
+ private static GridH2RowDescriptor rowDesc0;
+
+ /** */
+ private static H2RowFactory rowFactory0;
+
+ /** */
+ private static H2TableDescriptor tblDesc0;
+
+ /** */
+ private static GridH2Table resTbl0;
+
+ /**
+ * Creates table using given connection, DDL clause for given type descriptor and list of indexes.
+ *
+ * @param conn Connection.
+ * @param sql DDL clause.
+ * @param rowDesc Row descriptor.
+ * @param rowFactory Row factory.
+ * @param tblDesc Table descriptor.
+ * @throws SQLException If failed.
+ * @return Created table.
+ */
+ public static synchronized GridH2Table createTable(Connection conn, String sql,
+ @Nullable GridH2RowDescriptor rowDesc, H2RowFactory rowFactory, H2TableDescriptor tblDesc)
+ throws SQLException {
+ rowDesc0 = rowDesc;
+ rowFactory0 = rowFactory;
+ tblDesc0 = tblDesc;
+
+ try {
+ try (Statement s = conn.createStatement()) {
+ s.execute(sql + " engine \"" + H2TableEngine.class.getName() + "\"");
+ }
+
+ tblDesc.table(resTbl0);
+
+ return resTbl0;
+ }
+ finally {
+ resTbl0 = null;
+ tblDesc0 = null;
+ rowFactory0 = null;
+ rowDesc0 = null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public TableBase createTable(CreateTableData createTblData) {
+ resTbl0 = new GridH2Table(createTblData, rowDesc0, rowFactory0, tblDesc0, tblDesc0.schema().cacheName());
+
+ return resTbl0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQuery.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQuery.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQuery.java
new file mode 100644
index 0000000..dd1b44c
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQuery.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery;
+import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata;
+import org.apache.ignite.internal.util.typedef.internal.S;
+
+import java.util.List;
+
+/**
+ * Cached two-step query.
+ */
+public class H2TwoStepCachedQuery {
+ /** */
+ final List<GridQueryFieldMetadata> meta;
+
+ /** */
+ final GridCacheTwoStepQuery twoStepQry;
+
+ /**
+ * @param meta Fields metadata.
+ * @param twoStepQry Query.
+ */
+ public H2TwoStepCachedQuery(List<GridQueryFieldMetadata> meta, GridCacheTwoStepQuery twoStepQry) {
+ this.meta = meta;
+ this.twoStepQry = twoStepQry;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(H2TwoStepCachedQuery.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/8c75e4de/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQueryKey.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQueryKey.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQueryKey.java
new file mode 100644
index 0000000..1452a83
--- /dev/null
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2TwoStepCachedQueryKey.java
@@ -0,0 +1,107 @@
+/*
+ * 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;
+
+/**
+ * Key for cached two-step query.
+ */
+public class H2TwoStepCachedQueryKey {
+ /** */
+ private final String cacheName;
+
+ /** */
+ private final String sql;
+
+ /** */
+ private final boolean grpByCollocated;
+
+ /** */
+ private final boolean distributedJoins;
+
+ /** */
+ private final boolean enforceJoinOrder;
+
+ /** */
+ private final boolean isLocal;
+
+ /**
+ * @param cacheName Cache name.
+ * @param sql Sql.
+ * @param grpByCollocated Collocated GROUP BY.
+ * @param distributedJoins Distributed joins enabled.
+ * @param enforceJoinOrder Enforce join order of tables.
+ * @param isLocal Query is local flag.
+ */
+ H2TwoStepCachedQueryKey(String cacheName,
+ String sql,
+ boolean grpByCollocated,
+ boolean distributedJoins,
+ boolean enforceJoinOrder,
+ boolean isLocal) {
+ this.cacheName = cacheName;
+ this.sql = sql;
+ this.grpByCollocated = grpByCollocated;
+ this.distributedJoins = distributedJoins;
+ this.enforceJoinOrder = enforceJoinOrder;
+ this.isLocal = isLocal;
+ }
+
+ /**
+ * @return Cache name.
+ */
+ public String cacheName() {
+ return cacheName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ H2TwoStepCachedQueryKey that = (H2TwoStepCachedQueryKey)o;
+
+ if (grpByCollocated != that.grpByCollocated)
+ return false;
+
+ if (distributedJoins != that.distributedJoins)
+ return false;
+
+ if (enforceJoinOrder != that.enforceJoinOrder)
+ return false;
+
+ if (cacheName != null ? !cacheName.equals(that.cacheName) : that.cacheName != null)
+ return false;
+
+ return isLocal == that.isLocal && sql.equals(that.sql);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int res = cacheName != null ? cacheName.hashCode() : 0;
+ res = 31 * res + sql.hashCode();
+ res = 31 * res + (grpByCollocated ? 1 : 0);
+ res = res + (distributedJoins ? 2 : 0);
+ res = res + (enforceJoinOrder ? 4 : 0);
+ res = res + (isLocal ? 8 : 0);
+
+ return res;
+ }
+}