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 ceae2fd7d8352..0e381fdfbc754 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 @@ -1537,6 +1537,9 @@ else if (QueryUtils.TEMPLATE_REPLICATED.equalsIgnoreCase(templateName)) ccfg.setSqlEscapeAll(true); ccfg.setQueryEntities(Collections.singleton(entity)); + if (!QueryUtils.isCustomAffinityMapper(ccfg.getAffinityMapper())) + ccfg.setAffinityMapper(null); + if (affinityKey != null) ccfg.setKeyConfiguration(new CacheKeyConfiguration(entity.getKeyType(), affinityKey)); 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 939a082b823a6..e19247c0ffafa 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 @@ -1365,6 +1365,18 @@ public static void checkNotNullAllowed(CacheConfiguration cfg) { "is set.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION); } + /** + * Checks whether affinity key mapper is custom or default. + * + * @param affinityKeyMapper Affinity key mapper. + * @return {@code true} if affinity key mapper is custom. + */ + public static boolean isCustomAffinityMapper(AffinityKeyMapper affinityKeyMapper) { + return affinityKeyMapper != null && + !(affinityKeyMapper instanceof CacheDefaultBinaryAffinityKeyMapper) && + !(affinityKeyMapper instanceof GridCacheDefaultAffinityKeyMapper); + } + /** * Checks if given column can be removed from table using its {@link QueryEntity}. * diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlCreateTableTemplateTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlCreateTableTemplateTest.java new file mode 100644 index 0000000000000..ba0ef38560089 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlCreateTableTemplateTest.java @@ -0,0 +1,159 @@ +/* + * 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.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.binary.BinaryObjectBuilder; +import org.apache.ignite.cache.affinity.AffinityKeyMapper; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.Test; + +/** + * Ensures that SQL queries work for tables created dynamically based on a template. + */ +public class IgniteSqlCreateTableTemplateTest extends GridCommonAbstractTest { + /** {@inheritDoc} */ + @Override public IgniteConfiguration getConfiguration(String name) throws Exception { + IgniteConfiguration configuration = new IgniteConfiguration(); + configuration.setIgniteInstanceName(name); + + CacheConfiguration defaultCacheConfiguration = new CacheConfiguration(); + + defaultCacheConfiguration.setName("DEFAULT_TEMPLATE*"); + + CacheConfiguration customCacheConfiguration = new CacheConfiguration(); + + customCacheConfiguration.setName("CUSTOM_TEMPLATE*"); + + MockAffinityKeyMapper customAffinityMapper = new MockAffinityKeyMapper(); + + customCacheConfiguration.setAffinityMapper(customAffinityMapper); + + configuration.setCacheConfiguration(defaultCacheConfiguration, customCacheConfiguration); + + return configuration; + } + + /** {@inheritDoc} */ + @SuppressWarnings("deprecation") + @Override protected void beforeTestsStarted() throws Exception { + startGrid(); + } + + /** + * Tests select statement works on a table with BinaryObject as a primary key. + */ + @Test + public void testSelectForTableWithDataInsertedWithKeyValueAPI() { + Ignite ignite = grid(); + IgniteCache cache = ignite.getOrCreateCache("test"); + + createTable(cache, "PERSON", "DEFAULT_TEMPLATE"); + + createTable(cache, "ORGANIZATION", "DEFAULT_TEMPLATE"); + + BinaryObjectBuilder keyBuilder = ignite.binary().builder("PERSON_KEY"); + + keyBuilder.setField("ID", 1); + keyBuilder.setField("AFF_PERSON", 2); + + BinaryObjectBuilder valueBuilder = ignite.binary().builder("PERSON_VALUE"); + valueBuilder.setField("NAME", "test"); + + ignite.cache("PERSON_CACHE").withKeepBinary().put(keyBuilder.build(), valueBuilder.build()); + + keyBuilder = ignite.binary().builder("ORGANIZATION_KEY"); + + keyBuilder.setField("ID", 1); + keyBuilder.setField("AFF_ORGANIZATION", 2); + + valueBuilder = ignite.binary().builder("ORGANIZATION_VALUE"); + + valueBuilder.setField("NAME", "test"); + + ignite.cache("ORGANIZATION_CACHE").withKeepBinary().put(keyBuilder.build(), valueBuilder.build()); + + assertEquals(1, ignite.cache("PERSON_CACHE").query( + new SqlFieldsQuery("Select NAME from PERSON where ID = 1")).getAll().size() + ); + + assertEquals(1, ignite.cache("PERSON_CACHE").query( + new SqlFieldsQuery("Select NAME from PERSON where AFF_PERSON = 2")).getAll().size() + ); + + assertEquals(1, ignite.cache("ORGANIZATION_CACHE").query( + new SqlFieldsQuery("Select NAME from ORGANIZATION where AFF_ORGANIZATION = 2")).getAll().size() + ); + + } + + /** + * Creates table based on a template. + * + * @param cache Cache. + */ + private void createTable(IgniteCache cache, String tableName, String template) { + String sql = String.format( + "CREATE TABLE IF NOT EXISTS %1$s(\n" + + " ID INT NOT NULL,\n" + + " AFF_%1$s INT NOT NULL,\n" + + " NAME VARCHAR2(100),\n" + + " PRIMARY KEY (ID, AFF_%1$s)\n" + + ") with \"TEMPLATE=%2$s,KEY_TYPE=%1$s_KEY, AFFINITY_KEY=AFF_%1$s, CACHE_NAME=%1$s_CACHE, " + + "VALUE_TYPE=%1$s_VALUE, ATOMICITY=TRANSACTIONAL\";", tableName, template); + + cache.query(new SqlFieldsQuery(sql).setSchema("PUBLIC")); + } + + /** + * When template has custom affinity mapper. + * then cache created via CREATE TABLE command should have the same affinity mapper. + */ + @SuppressWarnings("unchecked") + @Test + public void testCustomAffinityKeyMapperIsNotOverwritten() { + Ignite ignite = grid(); + + IgniteCache cache = ignite.getOrCreateCache("test"); + + createTable(cache, "CUSTOM", "CUSTOM_TEMPLATE"); + + assertTrue(ignite.getOrCreateCache("CUSTOM_CACHE").getConfiguration( + CacheConfiguration.class).getAffinityMapper() instanceof MockAffinityKeyMapper); + } + + /** + * Mock affinity mapper implementation. + */ + @SuppressWarnings("deprecation") + private static class MockAffinityKeyMapper implements AffinityKeyMapper { + /** {@inheritDoc} */ + @Override public Object affinityKey(Object key) { + return null; + } + + /** {@inheritDoc} */ + @Override public void reset() { + // no-op + } + } +} \ No newline at end of file diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java index df4ca9a7c31b1..33b6273cfd701 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite2.java @@ -48,10 +48,7 @@ import org.apache.ignite.internal.processors.cache.query.ScanQueryOffheapExpiryPolicySelfTest; import org.apache.ignite.internal.processors.database.baseline.IgniteChangingBaselineCacheQueryNodeRestartSelfTest; import org.apache.ignite.internal.processors.database.baseline.IgniteStableBaselineCacheQueryNodeRestartsSelfTest; -import org.apache.ignite.internal.processors.query.IgniteCacheGroupsCompareQueryTest; -import org.apache.ignite.internal.processors.query.IgniteCacheGroupsSqlDistributedJoinSelfTest; -import org.apache.ignite.internal.processors.query.IgniteCacheGroupsSqlSegmentedIndexMultiNodeSelfTest; -import org.apache.ignite.internal.processors.query.IgniteCacheGroupsSqlSegmentedIndexSelfTest; +import org.apache.ignite.internal.processors.query.*; import org.apache.ignite.internal.processors.query.h2.twostep.CacheQueryMemoryLeakTest; import org.apache.ignite.internal.processors.query.h2.twostep.DisappearedCacheCauseRetryMessageSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.DisappearedCacheWasNotFoundMessageSelfTest; @@ -125,6 +122,7 @@ public static TestSuite suite() throws Exception { suite.addTest(new JUnit4TestAdapter(TableViewSubquerySelfTest.class)); suite.addTest(new JUnit4TestAdapter(IgniteCacheQueriesLoadTest1.class)); + suite.addTest(new JUnit4TestAdapter(IgniteSqlCreateTableTemplateTest.class)); return suite; }