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 <voze...@gridgain.com> Authored: Wed May 24 15:38:14 2017 +0300 Committer: devozerov <voze...@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; + } +}