You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2017/06/12 15:33:48 UTC
[34/62] [abbrv] ignite git commit: IGNITE-5188: CREATE TABLE: added
affinity key support. This closes #2097.
IGNITE-5188: CREATE TABLE: added affinity key support. This closes #2097.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/bd5065b1
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/bd5065b1
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/bd5065b1
Branch: refs/heads/ignite-5239
Commit: bd5065b1b1a4babf7cd882b9753700e7a58a6094
Parents: 0a6b44c
Author: Alexander Paschenko <al...@gmail.com>
Authored: Thu Jun 8 12:07:38 2017 +0300
Committer: devozerov <vo...@gridgain.com>
Committed: Thu Jun 8 12:07:38 2017 +0300
----------------------------------------------------------------------
.../ignite/internal/binary/BinaryContext.java | 1 -
.../ignite/internal/binary/BinaryFieldEx.java | 5 +
.../ignite/internal/binary/BinaryFieldImpl.java | 5 +
.../query/DynamicTableAffinityKeyMapper.java | 92 ++++++++++
.../processors/query/GridQueryProcessor.java | 52 +++---
.../internal/processors/query/QueryUtils.java | 19 ++-
.../query/h2/ddl/DdlStatementsProcessor.java | 2 +-
.../processors/query/h2/opt/GridH2Table.java | 5 +-
.../query/h2/sql/GridSqlCreateTable.java | 17 ++
.../query/h2/sql/GridSqlQueryParser.java | 44 +++++
.../cache/index/H2DynamicTableSelfTest.java | 170 ++++++++++++++++++-
11 files changed, 375 insertions(+), 37 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
index 70bc2f9..be02ba1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryContext.java
@@ -116,7 +116,6 @@ import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.marshaller.MarshallerContext;
import org.apache.ignite.marshaller.MarshallerUtils;
-import org.apache.ignite.internal.marshaller.optimized.OptimizedMarshaller;
import org.jetbrains.annotations.Nullable;
import org.jsr166.ConcurrentHashMap8;
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
index 42aa282..c998022 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldEx.java
@@ -26,6 +26,11 @@ import org.apache.ignite.binary.BinaryObject;
*/
public interface BinaryFieldEx extends BinaryField {
/**
+ * @return Type ID this field relates to.
+ */
+ public int typeId();
+
+ /**
* Writes field value to the given byte buffer.
*
* @param obj Object from which the field should be extracted.
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
index c318397..59bd03d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryFieldImpl.java
@@ -109,6 +109,11 @@ public class BinaryFieldImpl implements BinaryFieldEx {
}
/** {@inheritDoc} */
+ @Override public int typeId() {
+ return typeId;
+ }
+
+ /** {@inheritDoc} */
@Override public boolean writeField(BinaryObject obj, ByteBuffer buf) {
BinaryObjectExImpl obj0 = (BinaryObjectExImpl)obj;
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DynamicTableAffinityKeyMapper.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DynamicTableAffinityKeyMapper.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DynamicTableAffinityKeyMapper.java
new file mode 100644
index 0000000..e49341a
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/DynamicTableAffinityKeyMapper.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query;
+
+import org.apache.ignite.binary.BinaryObject;
+import org.apache.ignite.binary.BinaryType;
+import org.apache.ignite.cache.affinity.AffinityKeyMapper;
+import org.apache.ignite.internal.binary.BinaryFieldEx;
+import org.apache.ignite.internal.binary.BinaryObjectEx;
+import org.apache.ignite.internal.util.typedef.F;
+
+/**
+ * Trivial mapper to take extract field value from binary object of specific type as affinity key.
+ */
+@SuppressWarnings("deprecation")
+public class DynamicTableAffinityKeyMapper implements AffinityKeyMapper {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Type name. */
+ private final String typeName;
+
+ /** Field name. */
+ private final String fieldName;
+
+ /** Type id for faster type checks. */
+ private transient volatile BinaryFieldEx field;
+
+ /**
+ * Constructor.
+ *
+ * @param typeName Type name.
+ * @param fieldName Field name.
+ */
+ DynamicTableAffinityKeyMapper(String typeName, String fieldName) {
+ this.typeName = typeName;
+ this.fieldName = fieldName;
+ }
+
+ /**
+ * @return Field name.
+ */
+ public String fieldName() {
+ return fieldName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object affinityKey(Object key) {
+ if (!(key instanceof BinaryObject))
+ return key;
+
+ assert key instanceof BinaryObjectEx;
+
+ BinaryObjectEx key0 = (BinaryObjectEx)key;
+
+ if (field == null) {
+ BinaryType type = key0.type();
+
+ if (!F.eq(type.typeName(), typeName))
+ return key;
+
+ field = (BinaryFieldEx)type.field(fieldName);
+ }
+
+ if (!F.eq(key0.typeId(), field.typeId()))
+ return key;
+
+ Object affKey = field.value(key0);
+
+ return affKey != null ? affKey : key;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void reset() {
+ // No-op.
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
index 48c7229..4311b12 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java
@@ -17,6 +17,25 @@
package org.apache.ignite.internal.processors.query;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.cache.Cache;
+import javax.cache.CacheException;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteDataStreamer;
import org.apache.ignite.IgniteException;
@@ -34,8 +53,8 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.events.CacheQueryExecutedEvent;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
-import org.apache.ignite.internal.managers.communication.GridMessageListener;
import org.apache.ignite.internal.NodeStoppingException;
+import org.apache.ignite.internal.managers.communication.GridMessageListener;
import org.apache.ignite.internal.processors.GridProcessorAdapter;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.CacheObject;
@@ -72,9 +91,9 @@ import org.apache.ignite.internal.util.lang.GridCloseableIterator;
import org.apache.ignite.internal.util.lang.GridClosureException;
import org.apache.ignite.internal.util.lang.IgniteOutClosureX;
import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.T3;
+import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.util.worker.GridWorker;
import org.apache.ignite.internal.util.worker.GridWorkerFuture;
@@ -88,26 +107,6 @@ import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;
-import javax.cache.Cache;
-import javax.cache.CacheException;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-
import static org.apache.ignite.events.EventType.EVT_CACHE_QUERY_EXECUTED;
import static org.apache.ignite.internal.GridTopic.TOPIC_SCHEMA;
import static org.apache.ignite.internal.IgniteComponentType.INDEXING;
@@ -1279,13 +1278,14 @@ public class GridQueryProcessor extends GridProcessorAdapter {
* @param schemaName Schema name to create table in.
* @param entity Entity to create table from.
* @param templateName Template name.
+ * @param affinityKey Affinity key column name.
* @param atomicityMode Atomicity mode.
* @param backups Backups.
* @param ifNotExists Quietly ignore this command if table already exists.
* @throws IgniteCheckedException If failed.
*/
@SuppressWarnings("unchecked")
- public void dynamicTableCreate(String schemaName, QueryEntity entity, String templateName,
+ public void dynamicTableCreate(String schemaName, QueryEntity entity, String templateName, String affinityKey,
@Nullable CacheAtomicityMode atomicityMode, int backups, boolean ifNotExists) throws IgniteCheckedException {
assert !F.isEmpty(templateName);
assert backups >= 0;
@@ -1316,6 +1316,12 @@ public class GridQueryProcessor extends GridProcessorAdapter {
ccfg.setSqlEscapeAll(true);
ccfg.setQueryEntities(Collections.singleton(entity));
+ if (affinityKey != null) {
+ assert entity.getFields().containsKey(affinityKey) && entity.getKeyFields().contains(affinityKey);
+
+ ccfg.setAffinityMapper(new DynamicTableAffinityKeyMapper(entity.getKeyType(), affinityKey));
+ }
+
boolean res;
try {
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
index fbd9afa..e0815fd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java
@@ -427,16 +427,19 @@ public class QueryUtils {
if (valCls != null)
altTypeId = new QueryTypeIdKey(cacheName, valCls);
- if (!cctx.customAffinityMapper() && qryEntity.findKeyType() != null) {
- // Need to setup affinity key for distributed joins.
- String affField = ctx.cacheObjects().affinityField(qryEntity.findKeyType());
+ String affField = null;
- if (affField != null) {
- if (!escape)
- affField = normalizeObjectName(affField, false);
+ // Need to setup affinity key for distributed joins.
+ if (!cctx.customAffinityMapper() && qryEntity.findKeyType() != null)
+ affField = ctx.cacheObjects().affinityField(qryEntity.findKeyType());
+ else if (cctx.config().getAffinityMapper() instanceof DynamicTableAffinityKeyMapper)
+ affField = ((DynamicTableAffinityKeyMapper)cctx.config().getAffinityMapper()).fieldName();
- desc.affinityKey(affField);
- }
+ if (affField != null) {
+ if (!escape)
+ affField = normalizeObjectName(affField, false);
+
+ desc.affinityKey(affField);
}
}
else {
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
index 5efc5c9..d6bcabd 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java
@@ -178,7 +178,7 @@ public class DdlStatementsProcessor {
throw err;
ctx.query().dynamicTableCreate(cmd.schemaName(), e, cmd.templateName(),
- cmd.atomicityMode(), cmd.backups(), cmd.ifNotExists());
+ cmd.affinityKey(), cmd.atomicityMode(), cmd.backups(), cmd.ifNotExists());
}
}
else if (stmt0 instanceof GridSqlDropTable) {
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
index d656cc3..4f3ef01 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
@@ -34,6 +34,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.KeyCacheObject;
import org.apache.ignite.internal.processors.cache.query.QueryTable;
import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.processors.query.DynamicTableAffinityKeyMapper;
import org.apache.ignite.internal.processors.query.h2.database.H2RowFactory;
import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
@@ -130,7 +131,9 @@ public class GridH2Table extends TableBase {
this.desc = desc;
this.cctx = cctx;
- if (desc != null && desc.context() != null && !desc.context().customAffinityMapper()) {
+ if (desc != null && desc.context() != null &&
+ (!desc.context().customAffinityMapper() ||
+ desc.context().config().getAffinityMapper() instanceof DynamicTableAffinityKeyMapper)) {
boolean affinityColExists = true;
String affKey = desc.type().affinityKey();
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
index 50348fe..edced6b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlCreateTable.java
@@ -53,6 +53,9 @@ public class GridSqlCreateTable extends GridSqlStatement {
/** Primary key columns. */
private LinkedHashSet<String> pkCols;
+ /** Name of the column that represents affinity key. */
+ private String affinityKey;
+
/** Extra WITH-params. */
private List<String> params;
@@ -127,6 +130,20 @@ public class GridSqlCreateTable extends GridSqlStatement {
}
/**
+ * @return Name of the column that represents affinity key.
+ */
+ public String affinityKey() {
+ return affinityKey;
+ }
+
+ /**
+ * @param affinityKey Name of the column that represents affinity key.
+ */
+ public void affinityKey(String affinityKey) {
+ this.affinityKey = affinityKey;
+ }
+
+ /**
* @return Schema name upon which this statement has been issued.
*/
public String schemaName() {
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index fdb908e..d8baed3 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -405,6 +405,9 @@ public class GridSqlQueryParser {
private static final String PARAM_ATOMICITY = "ATOMICITY";
/** */
+ private static final String PARAM_AFFINITY_KEY = "AFFINITYKEY";
+
+ /** */
private final IdentityHashMap<Object, Object> h2ObjToGridObj = new IdentityHashMap<>();
/** */
@@ -1048,6 +1051,47 @@ public class GridSqlQueryParser {
break;
+ case PARAM_AFFINITY_KEY:
+ ensureNotEmpty(name, val);
+
+ String affColName = null;
+
+ // Either strip column name off its quotes, or uppercase it.
+ if (val.startsWith("'")) {
+ if (val.length() == 1 || !val.endsWith("'"))
+ throw new IgniteSQLException("Affinity key column name does not have trailing quote: " + val,
+ IgniteQueryErrorCode.PARSING);
+
+ val = val.substring(1, val.length() - 1);
+
+ ensureNotEmpty(name, val);
+
+ affColName = val;
+ }
+ else {
+ for (String colName : res.columns().keySet()) {
+ if (val.equalsIgnoreCase(colName)) {
+ if (affColName != null)
+ throw new IgniteSQLException("Ambiguous affinity column name, use single quotes " +
+ "for case sensitivity: " + val, IgniteQueryErrorCode.PARSING);
+
+ affColName = colName;
+ }
+ }
+ }
+
+ if (affColName == null || !res.columns().containsKey(affColName))
+ throw new IgniteSQLException("Affinity key column with given name not found: " + val,
+ IgniteQueryErrorCode.PARSING);
+
+ if (!res.primaryKeyColumns().contains(affColName))
+ throw new IgniteSQLException("Affinity key column must be one of key columns: " + affColName,
+ IgniteQueryErrorCode.PARSING);
+
+ res.affinityKey(affColName);
+
+ break;
+
default:
throw new IgniteSQLException("Unsupported parameter: " + name, IgniteQueryErrorCode.PARSING);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/bd5065b1/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
index 699fdad..e4babba 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java
@@ -19,13 +19,15 @@ package org.apache.ignite.internal.processors.cache.index;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Random;
import java.util.concurrent.Callable;
-
import javax.cache.CacheException;
-
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteException;
import org.apache.ignite.Ignition;
@@ -41,6 +43,7 @@ import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.binary.BinaryMarshaller;
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
import org.apache.ignite.internal.processors.query.GridQueryProperty;
+import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryTypeDescriptorImpl;
import org.apache.ignite.internal.processors.query.QueryUtils;
@@ -401,7 +404,7 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest {
e.setKeyType("CityKey");
e.setValueType("City");
- queryProcessor(client()).dynamicTableCreate("PUBLIC", e, CacheMode.PARTITIONED.name(),
+ queryProcessor(client()).dynamicTableCreate("PUBLIC", e, CacheMode.PARTITIONED.name(), null,
CacheAtomicityMode.ATOMIC, 10, false);
return null;
@@ -427,6 +430,167 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest {
}
/**
+ * @throws Exception if failed.
+ */
+ public void testAffinityKey() throws Exception {
+ executeDdl("CREATE TABLE \"City\" (\"name\" varchar primary key, \"code\" int) WITH \"affinityKey='name'\"");
+
+ assertAffinityCacheConfiguration("City", "name");
+
+ executeDdl("INSERT INTO \"City\" (\"name\", \"code\") values ('A', 1), ('B', 2), ('C', 3)");
+
+ List<String> cityNames = Arrays.asList("A", "B", "C");
+
+ List<Integer> cityCodes = Arrays.asList(1, 2, 3);
+
+ // We need unique name for this table to avoid conflicts with existing binary metadata.
+ executeDdl("CREATE TABLE \"Person2\" (\"id\" int, \"city\" varchar," +
+ " \"name\" varchar, \"surname\" varchar, \"age\" int, PRIMARY KEY (\"id\", \"city\")) WITH " +
+ "\"template=cache,affinityKey='city'\"");
+
+ assertAffinityCacheConfiguration("Person2", "city");
+
+ Random r = new Random();
+
+ Map<Integer, Integer> personId2cityCode = new HashMap<>();
+
+ for (int i = 0; i < 100; i++) {
+ int cityIdx = r.nextInt(3);
+
+ String cityName = cityNames.get(cityIdx);
+
+ int cityCode = cityCodes.get(cityIdx);
+
+ personId2cityCode.put(i, cityCode);
+
+ queryProcessor(client()).querySqlFieldsNoCache(new SqlFieldsQuery("insert into \"Person2\"(\"id\", " +
+ "\"city\") values (?, ?)").setArgs(i, cityName), true).getAll();
+ }
+
+ List<List<?>> res = queryProcessor(client()).querySqlFieldsNoCache(new SqlFieldsQuery("select \"id\", " +
+ "c.\"code\" from \"Person2\" p left join \"City\" c on p.\"city\" = c.\"name\" where c.\"name\" " +
+ "is not null"), true).getAll();
+
+ assertEquals(100, res.size());
+
+ for (int i = 0; i < 100; i++) {
+ assertNotNull(res.get(i).get(0));
+
+ assertNotNull(res.get(i).get(1));
+
+ int id = (Integer)res.get(i).get(0);
+
+ int code = (Integer)res.get(i).get(1);
+
+ assertEquals((int)personId2cityCode.get(id), code);
+ }
+ }
+
+ /**
+ * Test various cases of affinity key column specification.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testAffinityKeyCaseSensitivity() {
+ executeDdl("CREATE TABLE \"A\" (\"name\" varchar primary key, \"code\" int) WITH \"affinityKey='name'\"");
+
+ assertAffinityCacheConfiguration("A", "name");
+
+ executeDdl("CREATE TABLE \"B\" (name varchar primary key, \"code\" int) WITH \"affinityKey=name\"");
+
+ assertAffinityCacheConfiguration("B", "NAME");
+
+ executeDdl("CREATE TABLE \"C\" (name varchar primary key, \"code\" int) WITH \"affinityKey=NamE\"");
+
+ assertAffinityCacheConfiguration("C", "NAME");
+
+ executeDdl("CREATE TABLE \"D\" (\"name\" varchar primary key, \"code\" int) WITH \"affinityKey=NAME\"");
+
+ assertAffinityCacheConfiguration("D", "name");
+
+ // Error arises because user has specified case sensitive affinity column name
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ executeDdl("CREATE TABLE \"E\" (name varchar primary key, \"code\" int) WITH \"affinityKey='Name'\"");
+
+ return null;
+ }
+ }, IgniteSQLException.class, "Affinity key column with given name not found: Name");
+
+ // Error arises because user declares case insensitive affinity column name while having two 'name'
+ // columns whose names are equal in ignore case.
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ executeDdl("CREATE TABLE \"E\" (\"name\" varchar, \"Name\" int, val int, primary key(\"name\", " +
+ "\"Name\")) WITH \"affinityKey=name\"");
+
+ return null;
+ }
+ }, IgniteSQLException.class, "Ambiguous affinity column name, use single quotes for case sensitivity: name");
+
+ executeDdl("CREATE TABLE \"E\" (\"name\" varchar, \"Name\" int, val int, primary key(\"name\", " +
+ "\"Name\")) WITH \"affinityKey='Name'\"");
+
+ assertAffinityCacheConfiguration("E", "Name");
+ }
+
+ /**
+ * Tests that attempting to specify an affinity key that actually is a value column yields an error.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testAffinityKeyNotKeyColumn() {
+ // Error arises because user has specified case sensitive affinity column name
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ executeDdl("CREATE TABLE \"E\" (name varchar primary key, \"code\" int) WITH \"affinityKey=code\"");
+
+ return null;
+ }
+ }, IgniteSQLException.class, "Affinity key column must be one of key columns: code");
+ }
+
+ /**
+ * Tests that attempting to specify an affinity key that actually is a value column yields an error.
+ */
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ public void testAffinityKeyNotFound() {
+ // Error arises because user has specified case sensitive affinity column name
+ GridTestUtils.assertThrows(null, new Callable<Object>() {
+ @Override public Object call() throws Exception {
+ executeDdl("CREATE TABLE \"E\" (name varchar primary key, \"code\" int) WITH \"affinityKey=missing\"");
+
+ return null;
+ }
+ }, IgniteSQLException.class, "Affinity key column with given name not found: missing");
+ }
+
+ /**
+ * Check that dynamic cache created with {@code CREATE TABLE} is correctly configured affinity wise.
+ * @param cacheName Cache name to check.
+ * @param affKeyFieldName Expected affinity key field name.
+ */
+ private void assertAffinityCacheConfiguration(String cacheName, String affKeyFieldName) {
+ String actualCacheName = cacheName(cacheName);
+
+ Collection<GridQueryTypeDescriptor> types = client().context().query().types(actualCacheName);
+
+ assertEquals(1, types.size());
+
+ GridQueryTypeDescriptor type = types.iterator().next();
+
+ assertTrue(type.name().startsWith(actualCacheName));
+ assertEquals(cacheName, type.tableName());
+ assertEquals(affKeyFieldName, type.affinityKey());
+
+ GridH2Table tbl = ((IgniteH2Indexing)queryProcessor(client()).getIndexing()).dataTable("PUBLIC", cacheName);
+
+ assertNotNull(tbl);
+
+ assertNotNull(tbl.getAffinityKeyColumn());
+
+ assertEquals(affKeyFieldName, tbl.getAffinityKeyColumn().columnName);
+ }
+
+ /**
* Execute {@code CREATE TABLE} w/given params.
* @param params Engine parameters.
*/