| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ignite.internal.processors.cache; |
| |
| import java.io.Serializable; |
| import java.math.BigDecimal; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.UUID; |
| import java.util.concurrent.Callable; |
| import javax.cache.CacheException; |
| import com.google.common.collect.ImmutableMap; |
| import org.apache.ignite.IgniteCache; |
| import org.apache.ignite.cache.CacheAtomicityMode; |
| import org.apache.ignite.cache.CacheMode; |
| import org.apache.ignite.cache.CacheRebalanceMode; |
| import org.apache.ignite.cache.CacheWriteSynchronizationMode; |
| import org.apache.ignite.cache.QueryEntity; |
| import org.apache.ignite.cache.affinity.AffinityKey; |
| import org.apache.ignite.cache.query.QueryCursor; |
| import org.apache.ignite.cache.query.SqlFieldsQuery; |
| import org.apache.ignite.cache.query.annotations.QuerySqlField; |
| import org.apache.ignite.configuration.CacheConfiguration; |
| import org.apache.ignite.configuration.IgniteConfiguration; |
| import org.apache.ignite.internal.IgniteKernal; |
| import org.apache.ignite.internal.binary.BinaryMarshaller; |
| import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree; |
| import org.apache.ignite.internal.processors.cache.query.GridCacheSqlIndexMetadata; |
| import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata; |
| import org.apache.ignite.internal.processors.datastructures.GridCacheAtomicLongValue; |
| import org.apache.ignite.internal.processors.datastructures.GridCacheInternalKeyImpl; |
| import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata; |
| import org.apache.ignite.internal.processors.query.GridQueryProcessor; |
| import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.apache.ignite.internal.util.typedef.X; |
| import org.apache.ignite.testframework.GridTestUtils; |
| import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; |
| import org.junit.Test; |
| |
| import static org.apache.ignite.cache.CacheAtomicityMode.TRANSACTIONAL; |
| import static org.apache.ignite.cache.CacheMode.PARTITIONED; |
| import static org.apache.ignite.cache.CacheMode.REPLICATED; |
| |
| /** |
| * Tests for fields queries. |
| */ |
| public abstract class IgniteCacheAbstractFieldsQuerySelfTest extends GridCommonAbstractTest { |
| /** */ |
| private static IgniteCache<String, Organization> orgCache; |
| |
| /** */ |
| private static IgniteCache<AffinityKey<String>, Person> personCache; |
| |
| /** */ |
| private static IgniteCache<String, String> strCache; |
| |
| /** */ |
| protected static IgniteCache<Integer, Integer> intCache; |
| |
| /** */ |
| protected static IgniteCache<?, ?> noOpCache; |
| |
| /** Flag indicating if starting node should have cache. */ |
| protected boolean hasCache; |
| |
| /** Whether BinaryMarshaller is set. */ |
| protected boolean binaryMarshaller; |
| |
| /** {@inheritDoc} */ |
| @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { |
| IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); |
| |
| cfg.setPeerClassLoadingEnabled(false); |
| |
| if (hasCache) |
| cfg.setCacheConfiguration(cacheConfiguration()); |
| else |
| cfg.setCacheConfiguration(); |
| |
| return cfg; |
| } |
| |
| /** |
| * @return Cache configuration. |
| */ |
| protected CacheConfiguration cacheConfiguration() { |
| CacheConfiguration ccfg = defaultCacheConfiguration(); |
| |
| ccfg.setCacheMode(cacheMode()); |
| ccfg.setAtomicityMode(atomicityMode()); |
| ccfg.setWriteSynchronizationMode(CacheWriteSynchronizationMode.FULL_SYNC); |
| ccfg.setRebalanceMode(CacheRebalanceMode.SYNC); |
| |
| if (cacheMode() == PARTITIONED) |
| ccfg.setBackups(1); |
| |
| return ccfg; |
| } |
| |
| /** |
| * @param clsK Class k. |
| * @param clsV Class v. |
| */ |
| protected <K, V> IgniteCache<K, V> jcache(Class<K> clsK, Class<V> clsV) { |
| return jcache(grid(0), cacheConfiguration(), clsK, clsV); |
| } |
| |
| /** |
| * @param name Name. |
| * @param clsK Class k. |
| * @param clsV Class v. |
| */ |
| protected <K, V> IgniteCache<K, V> jcache(String name, Class<K> clsK, Class<V> clsV) { |
| return jcache(grid(0), cacheConfiguration(), name, clsK, clsV); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected void beforeTestsStarted() throws Exception { |
| hasCache = true; |
| |
| startGridsMultiThreaded(gridCount()); |
| |
| hasCache = false; |
| |
| startGrid(gridCount()); |
| |
| orgCache = jcache(String.class, Organization.class); |
| |
| assert orgCache != null; |
| |
| orgCache.put("o1", new Organization(1, "A")); |
| orgCache.put("o2", new Organization(2, "B")); |
| |
| IgniteCache<?, ?> c = jcache(AffinityKey.class, Person.class); |
| personCache = (IgniteCache<AffinityKey<String>, Person>)c; |
| |
| assert personCache != null; |
| |
| personCache.put(new AffinityKey<>("p1", "o1"), new Person("John White", 25, 1)); |
| personCache.put(new AffinityKey<>("p2", "o1"), new Person("Joe Black", 35, 1)); |
| personCache.put(new AffinityKey<>("p3", "o2"), new Person("Mike Green", 40, 2)); |
| |
| strCache = jcache(String.class, String.class); |
| |
| assert strCache != null; |
| |
| strCache.put("key", "val"); |
| |
| intCache = jcache(Integer.class, Integer.class); |
| |
| assert intCache != null; |
| |
| for (int i = 0; i < 200; i++) |
| intCache.put(i, i); |
| |
| noOpCache = grid(0).getOrCreateCache("noop"); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected void beforeTest() throws Exception { |
| binaryMarshaller = grid(0).configuration().getMarshaller() instanceof BinaryMarshaller; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected void afterTestsStopped() throws Exception { |
| orgCache = null; |
| personCache = null; |
| strCache = null; |
| intCache = null; |
| noOpCache = null; |
| } |
| |
| /** @return cache mode. */ |
| protected abstract CacheMode cacheMode(); |
| |
| /** @return Cache atomicity mode. */ |
| protected CacheAtomicityMode atomicityMode() { |
| return TRANSACTIONAL; |
| } |
| |
| /** @return Number of grids to start. */ |
| protected abstract int gridCount(); |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testCacheMetaData() throws Exception { |
| // Put internal key to test filtering of internal objects. |
| |
| for (String cacheName : grid(0).cacheNames()) { |
| ((IgniteKernal)grid(0)).getCache(cacheName).getAndPut( |
| new GridCacheInternalKeyImpl("LONG", ""), |
| new GridCacheAtomicLongValue(0) |
| ); |
| } |
| |
| try { |
| Collection<GridCacheSqlMetadata> metas = |
| ((IgniteKernal)grid(0)).getCache(intCache.getName()).context().queries().sqlMetadata(); |
| |
| assert metas != null; |
| |
| for (GridCacheSqlMetadata meta : metas) { |
| Collection<String> types = meta.types(); |
| |
| assertNotNull(types); |
| |
| if (personCache.getName().equals(meta.cacheName())) { |
| assertEquals("Invalid types size", 1, types.size()); |
| assert types.contains("Person"); |
| |
| if (binaryMarshaller) { |
| assert Object.class.getName().equals(meta.keyClass("Person")); |
| assert Object.class.getName().equals(meta.valueClass("Person")); |
| } |
| else { |
| assert AffinityKey.class.getName().equals(meta.keyClass("Person")); |
| assert Person.class.getName().equals(meta.valueClass("Person")); |
| } |
| |
| Map<String, String> fields = meta.fields("Person"); |
| |
| assert fields != null; |
| assert fields.size() == 3; |
| |
| if (binaryMarshaller) { |
| assert Integer.class.getName().equals(fields.get("AGE")); |
| assert Integer.class.getName().equals(fields.get("ORGID")); |
| } |
| else { |
| assert int.class.getName().equals(fields.get("AGE")); |
| assert int.class.getName().equals(fields.get("ORGID")); |
| } |
| |
| assert String.class.getName().equals(fields.get("NAME")); |
| |
| Collection<GridCacheSqlIndexMetadata> indexes = meta.indexes("Person"); |
| |
| assertNotNull("Indexes should be defined", indexes); |
| assertEquals(2, indexes.size()); |
| |
| Set<String> idxFields = new HashSet<>(); |
| |
| Iterator<GridCacheSqlIndexMetadata> it = indexes.iterator(); |
| |
| Collection<String> indFlds = it.next().fields(); |
| |
| assertNotNull("Fields for first index should be defined", indFlds); |
| assertEquals("First index should have one field", indFlds.size(), 1); |
| |
| Iterator<String> indFldIt = indFlds.iterator(); |
| |
| idxFields.add(indFldIt.next()); |
| |
| indFlds = it.next().fields(); |
| |
| assertNotNull("Fields for second index should be defined", indFlds); |
| assertEquals("Second index should have one field", indFlds.size(), 1); |
| |
| indFldIt = indFlds.iterator(); |
| |
| idxFields.add(indFldIt.next()); |
| |
| assertTrue(idxFields.contains("AGE")); |
| assertTrue(idxFields.contains("ORGID")); |
| } |
| else if (orgCache.getName().equals(meta.cacheName())) { |
| assertEquals("Invalid types size", 1, types.size()); |
| assert types.contains("Organization"); |
| |
| if (binaryMarshaller) |
| assert Object.class.getName().equals(meta.valueClass("Organization")); |
| else |
| assert Organization.class.getName().equals(meta.valueClass("Organization")); |
| |
| assert String.class.getName().equals(meta.keyClass("Organization")); |
| |
| Map<String, String> fields = meta.fields("Organization"); |
| |
| assert fields != null; |
| assertEquals("Fields: " + fields, 2, fields.size()); |
| |
| if (binaryMarshaller) { |
| assert Integer.class.getName().equals(fields.get("ID")); |
| } |
| else { |
| assert int.class.getName().equals(fields.get("ID")); |
| } |
| |
| assert String.class.getName().equals(fields.get("NAME")); |
| } |
| else if (intCache.getName().equals(meta.cacheName())) { |
| assertEquals("Invalid types size", 1, types.size()); |
| assert types.contains("Integer"); |
| |
| assert Integer.class.getName().equals(meta.valueClass("Integer")); |
| assert Integer.class.getName().equals(meta.keyClass("Integer")); |
| |
| Map<String, String> fields = meta.fields("Integer"); |
| |
| assert fields != null; |
| assert fields.size() == 2; |
| assert Integer.class.getName().equals(fields.get("_KEY")); |
| assert Integer.class.getName().equals(fields.get("_VAL")); |
| } |
| else if (strCache.getName().equals(meta.cacheName())) { |
| assertEquals("Invalid types size", 1, types.size()); |
| assert types.contains("String"); |
| |
| assert String.class.getName().equals(meta.valueClass("String")); |
| assert String.class.getName().equals(meta.keyClass("String")); |
| |
| Map<String, String> fields = meta.fields("String"); |
| |
| assert fields != null; |
| assert fields.size() == 2; |
| assert String.class.getName().equals(fields.get("_KEY")); |
| assert String.class.getName().equals(fields.get("_VAL")); |
| } |
| else if (DEFAULT_CACHE_NAME.equals(meta.cacheName()) || noOpCache.getName().equals(meta.cacheName())) |
| assertTrue("Invalid types size", types.isEmpty()); |
| else if (!"cacheWithCustomKeyPrecision".equalsIgnoreCase(meta.cacheName()) && |
| !"cacheWithDecimalPrecisionAndScale".equalsIgnoreCase(meta.cacheName())) |
| fail("Unknown cache: " + meta.cacheName()); |
| } |
| } |
| finally { |
| ((IgniteKernal)grid(0)).getCache(intCache.getName()).remove(new GridCacheInternalKeyImpl("LONG", "")); |
| } |
| } |
| |
| /** |
| * |
| */ |
| @Test |
| public void testExplain() { |
| List<List<?>> res = grid(0).cache(personCache.getName()).query(sqlFieldsQuery( |
| String.format("explain select p.age, p.name, o.name " + |
| "from \"%s\".Person p, \"%s\".Organization o where p.orgId = o.id", |
| personCache.getName(), orgCache.getName()))).getAll(); |
| |
| for (List<?> row : res) |
| X.println("____ : " + row); |
| |
| if (cacheMode() == PARTITIONED) { |
| assertEquals(2, res.size()); |
| |
| assertTrue(((String)res.get(1).get(0)).contains(GridSqlQuerySplitter.mergeTableIdentifier(0))); |
| } |
| else |
| assertEquals(1, res.size()); |
| } |
| |
| /** @throws Exception If failed. */ |
| @SuppressWarnings("unchecked") |
| @Test |
| public void testExecuteWithMetaDataAndPrecision() throws Exception { |
| QueryEntity qeWithPrecision = new QueryEntity() |
| .setKeyType("java.lang.Long") |
| .setValueType("TestType") |
| .addQueryField("strField", "java.lang.String", "strField") |
| .setFieldsPrecision(ImmutableMap.of("strField", 999)); |
| |
| grid(0).getOrCreateCache(cacheConfiguration() |
| .setName("cacheWithPrecision") |
| .setQueryEntities(Collections.singleton(qeWithPrecision))); |
| |
| GridQueryProcessor qryProc = grid(0).context().query(); |
| |
| qryProc.querySqlFields( |
| new SqlFieldsQuery("INSERT INTO TestType(_KEY, strField) VALUES(?, ?)") |
| .setSchema("cacheWithPrecision") |
| .setArgs(1, "ABC"), true); |
| |
| qryProc.querySqlFields( |
| new SqlFieldsQuery("INSERT INTO TestType(_KEY, strField) VALUES(?, ?)") |
| .setSchema("cacheWithPrecision") |
| .setArgs(2, "DEF"), true); |
| |
| QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)qryProc.querySqlFields( |
| new SqlFieldsQuery("SELECT _KEY, strField FROM TestType") |
| .setSchema("cacheWithPrecision"), true); |
| |
| List<GridQueryFieldMetadata> fieldsMeta = cursor.fieldsMeta(); |
| |
| for (GridQueryFieldMetadata meta : fieldsMeta) { |
| if (!meta.fieldName().equalsIgnoreCase("strField")) |
| continue; |
| |
| assertEquals(999, meta.precision()); |
| } |
| } |
| |
| /** |
| * Test that scale and precision returned correctly for Decimal column in result set: |
| * |
| * 1. Start node; |
| * 2. Create table with Decimal(3,0) column; |
| * 3. Insert a new row into the table; |
| * 4. Execute select with decimal row; |
| * 5. Check that selected decimal column has precision 3 and scale 0. |
| * |
| * @throws Exception If failed. |
| */ |
| @Test |
| public void testDecimalColumnScaleAndPrecision() throws Exception { |
| QueryEntity qeWithPrecision = new QueryEntity() |
| .setKeyType("java.lang.Long") |
| .setValueType("TestType") |
| .addQueryField("age", "java.math.BigDecimal", "age") |
| .setFieldsPrecision(ImmutableMap.of("age", 3)) |
| .setFieldsScale(ImmutableMap.of("age", 0)); |
| |
| grid(0).getOrCreateCache(cacheConfiguration() |
| .setName("cacheWithDecimalPrecisionAndScale") |
| .setQueryEntities(Collections.singleton(qeWithPrecision))); |
| |
| GridQueryProcessor qryProc = grid(0).context().query(); |
| |
| qryProc.querySqlFields( |
| new SqlFieldsQuery("INSERT INTO TestType(_key, age) VALUES(?, ?)") |
| .setSchema("cacheWithDecimalPrecisionAndScale") |
| .setArgs(1, new BigDecimal(160)), true); |
| |
| QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)qryProc.querySqlFields( |
| new SqlFieldsQuery("SELECT age FROM TestType") |
| .setSchema("cacheWithDecimalPrecisionAndScale"), true); |
| |
| List<GridQueryFieldMetadata> fieldsMeta = cursor.fieldsMeta(); |
| |
| assertEquals(1, fieldsMeta.size()); |
| |
| GridQueryFieldMetadata meta = fieldsMeta.get(0); |
| |
| assertEquals(3, meta.precision()); |
| assertEquals(0, meta.scale()); |
| } |
| |
| /** */ |
| @Test |
| public void testExecuteWithMetaDataAndCustomKeyPrecision() throws Exception { |
| QueryEntity qeWithPrecision = new QueryEntity() |
| .setKeyType("java.lang.String") |
| .setKeyFieldName("my_key") |
| .setValueType("CustomKeyType") |
| .addQueryField("my_key", "java.lang.String", "my_key") |
| .addQueryField("strField", "java.lang.String", "strField") |
| .setFieldsPrecision(ImmutableMap.of("strField", 999, "my_key", 777)); |
| |
| grid(0).getOrCreateCache(cacheConfiguration() |
| .setName("cacheWithCustomKeyPrecision") |
| .setQueryEntities(Collections.singleton(qeWithPrecision))); |
| |
| GridQueryProcessor qryProc = grid(0).context().query(); |
| |
| qryProc.querySqlFields( |
| new SqlFieldsQuery("INSERT INTO CustomKeyType(my_key, strField) VALUES(?, ?)") |
| .setSchema("cacheWithCustomKeyPrecision") |
| .setArgs("1", "ABC"), true); |
| |
| qryProc.querySqlFields( |
| new SqlFieldsQuery("INSERT INTO CustomKeyType(my_key, strField) VALUES(?, ?)") |
| .setSchema("cacheWithCustomKeyPrecision") |
| .setArgs("2", "DEF"), true); |
| |
| QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)qryProc.querySqlFields( |
| new SqlFieldsQuery("SELECT my_key, strField FROM CustomKeyType") |
| .setSchema("cacheWithCustomKeyPrecision"), true); |
| |
| List<GridQueryFieldMetadata> fieldsMeta = cursor.fieldsMeta(); |
| |
| int fldCnt = 0; |
| |
| for (GridQueryFieldMetadata meta : fieldsMeta) { |
| switch (meta.fieldName()) { |
| case "STRFIELD": |
| assertEquals(999, meta.precision()); |
| |
| fldCnt++; |
| |
| break; |
| |
| case "MY_KEY": |
| assertEquals(777, meta.precision()); |
| |
| fldCnt++; |
| |
| break; |
| default: |
| fail("Unknown field - " + meta.fieldName()); |
| } |
| } |
| |
| assertEquals("Metadata for all fields should be returned.", 2, fldCnt); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testExecuteWithMetaData() throws Exception { |
| QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)personCache.query(sqlFieldsQuery( |
| String.format("select p._KEY, p.name, p.age, o.name " + |
| "from \"%s\".Person p, \"%s\".Organization o where p.orgId = o.id", |
| personCache.getName(), orgCache.getName()))); |
| |
| Collection<GridQueryFieldMetadata> meta = cursor.fieldsMeta(); |
| |
| assertNotNull(meta); |
| assertEquals(4, meta.size()); |
| |
| Iterator<GridQueryFieldMetadata> metaIt = meta.iterator(); |
| |
| assertNotNull(metaIt); |
| assert metaIt.hasNext(); |
| |
| GridQueryFieldMetadata field = metaIt.next(); |
| |
| assertNotNull(field); |
| assertEquals(personCache.getName(), field.schemaName()); |
| assertEquals("PERSON", field.typeName()); |
| assertEquals("_KEY", field.fieldName()); |
| assertEquals(Object.class.getName(), field.fieldTypeName()); |
| |
| assert metaIt.hasNext(); |
| |
| field = metaIt.next(); |
| |
| assertNotNull(field); |
| assertEquals(personCache.getName(), field.schemaName()); |
| assertEquals("PERSON", field.typeName()); |
| assertEquals("NAME", field.fieldName()); |
| assertEquals(String.class.getName(), field.fieldTypeName()); |
| |
| assert metaIt.hasNext(); |
| |
| field = metaIt.next(); |
| |
| assertNotNull(field); |
| assertEquals(personCache.getName(), field.schemaName()); |
| assertEquals("PERSON", field.typeName()); |
| assertEquals("AGE", field.fieldName()); |
| assertEquals(Integer.class.getName(), field.fieldTypeName()); |
| |
| assert metaIt.hasNext(); |
| |
| field = metaIt.next(); |
| |
| assert field != null; |
| assertNotNull(field); |
| assertEquals(orgCache.getName(), field.schemaName()); |
| assertEquals("ORGANIZATION", field.typeName()); |
| assertEquals("NAME", field.fieldName()); |
| assertEquals(String.class.getName(), field.fieldTypeName()); |
| |
| assert !metaIt.hasNext(); |
| |
| List<List<?>> res = cursor.getAll(); |
| |
| dedup(res); |
| |
| assertEquals(3, res.size()); |
| |
| Collections.sort(res, new Comparator<List<?>>() { |
| @Override public int compare(List<?> row1, List<?> row2) { |
| return ((Integer)row1.get(2)).compareTo((Integer)row2.get(2)); |
| } |
| }); |
| |
| int cnt = 0; |
| |
| for (List<?> row : res) { |
| assert row.size() == 4; |
| |
| if (cnt == 0) { |
| assertEquals(new AffinityKey<>("p1", "o1"), row.get(0)); |
| assertEquals("John White", row.get(1)); |
| assertEquals(25, row.get(2)); |
| assertEquals("A", row.get(3)); |
| } |
| else if (cnt == 1) { |
| assertEquals(new AffinityKey<>("p2", "o1"), row.get(0)); |
| assertEquals("Joe Black", row.get(1)); |
| assertEquals(35, row.get(2)); |
| assertEquals("A", row.get(3)); |
| } |
| if (cnt == 2) { |
| assertEquals(new AffinityKey<>("p3", "o2"), row.get(0)); |
| assertEquals("Mike Green", row.get(1)); |
| assertEquals(40, row.get(2)); |
| assertEquals("B", row.get(3)); |
| } |
| |
| cnt++; |
| } |
| |
| assertEquals(3, cnt); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testExecute() throws Exception { |
| doTestExecute(personCache, sqlFieldsQuery("select _KEY, name, age from Person")); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testExecuteNoOpCache() throws Exception { |
| doTestExecute(noOpCache, sqlFieldsQuery("select _KEY, name, age from \"AffinityKey-Person\".Person")); |
| } |
| |
| /** |
| * Execute given query and check results. |
| * @param cache Cache to run query on. |
| * @param fldsQry Query. |
| * @throws Exception if failed. |
| */ |
| private void doTestExecute(IgniteCache<?, ?> cache, SqlFieldsQuery fldsQry) throws Exception { |
| QueryCursor<List<?>> qry = cache.query(fldsQry); |
| |
| List<List<?>> res = new ArrayList<>(qry.getAll()); |
| |
| assertNotNull(res); |
| |
| dedup(res); |
| |
| assertEquals(res.size(), 3); |
| |
| Collections.sort(res, new Comparator<List<?>>() { |
| @Override public int compare(List<?> row1, List<?> row2) { |
| return ((Integer)row1.get(2)).compareTo((Integer)row2.get(2)); |
| } |
| }); |
| |
| int cnt = 0; |
| |
| for (List<?> row : res) { |
| assertEquals(3, row.size()); |
| |
| if (cnt == 0) { |
| assertEquals(new AffinityKey<>("p1", "o1"), row.get(0)); |
| assertEquals("John White", row.get(1)); |
| assertEquals(25, row.get(2)); |
| } |
| else if (cnt == 1) { |
| assertEquals(new AffinityKey<>("p2", "o1"), row.get(0)); |
| assertEquals("Joe Black", row.get(1)); |
| assertEquals(35, row.get(2)); |
| } |
| if (cnt == 2) { |
| assertEquals(new AffinityKey<>("p3", "o2"), row.get(0)); |
| assertEquals("Mike Green", row.get(1)); |
| assertEquals(40, row.get(2)); |
| } |
| |
| cnt++; |
| } |
| |
| assertEquals(3, cnt); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testExecuteWithArguments() throws Exception { |
| QueryCursor<List<?>> qry = personCache |
| .query(sqlFieldsQuery("select _KEY, name, age from Person where age > ?").setArgs(30)); |
| |
| List<List<?>> res = new ArrayList<>(qry.getAll()); |
| |
| dedup(res); |
| |
| assertEquals(2, res.size()); |
| |
| Collections.sort(res, new Comparator<List<?>>() { |
| @Override public int compare(List<?> row1, List<?> row2) { |
| return ((Integer)row1.get(2)).compareTo((Integer)row2.get(2)); |
| } |
| }); |
| |
| int cnt = 0; |
| |
| for (List<?> row : res) { |
| assertEquals(3, row.size()); |
| |
| if (cnt == 0) { |
| assertEquals(new AffinityKey<>("p2", "o1"), row.get(0)); |
| assertEquals("Joe Black", row.get(1)); |
| assertEquals(35, row.get(2)); |
| } |
| if (cnt == 1) { |
| assertEquals(new AffinityKey<>("p3", "o2"), row.get(0)); |
| assertEquals("Mike Green", row.get(1)); |
| assertEquals(40, row.get(2)); |
| } |
| |
| cnt++; |
| } |
| |
| assert cnt == 2; |
| } |
| |
| /** */ |
| protected boolean isReplicatedOnly() { |
| return false; |
| } |
| |
| /** */ |
| private SqlFieldsQuery sqlFieldsQuery(String sql) { |
| SqlFieldsQuery qry = new SqlFieldsQuery(sql); |
| |
| if (isReplicatedOnly()) |
| qry.setReplicatedOnly(true); |
| |
| return qry; |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testSelectAllJoined() throws Exception { |
| QueryCursor<List<?>> qry = personCache.query(sqlFieldsQuery(String.format( |
| "select p._key, p._val, p.*, o._key, o._val, o.* from \"%s\".Person p, \"%s\".Organization o where p.orgId = o.id", |
| personCache.getName(), |
| orgCache.getName() |
| ))); |
| |
| List<List<?>> res = new ArrayList<>(qry.getAll()); |
| |
| dedup(res); |
| |
| assertEquals(3, res.size()); |
| |
| Collections.sort(res, new Comparator<List<?>>() { |
| @Override public int compare(List<?> row1, List<?> row2) { |
| return ((Integer)row1.get(3)).compareTo((Integer)row2.get(3)); |
| } |
| }); |
| |
| int cnt = 0; |
| |
| for (List<?> row : res) { |
| assertEquals(9, row.size()); |
| |
| if (cnt == 0) { |
| assert new AffinityKey<>("p1", "o1").equals(row.get(0)); |
| assert Person.class.getName().equals(row.get(1).getClass().getName()); |
| assert "John White".equals(row.get(2)); |
| assert row.get(3).equals(25); |
| assert row.get(4).equals(1); |
| assert "o1".equals(row.get(5)); |
| assert Organization.class.getName().equals(row.get(6).getClass().getName()); |
| assert row.get(7).equals(1); |
| assert "A".equals(row.get(8)); |
| } |
| else if (cnt == 1) { |
| assert new AffinityKey<>("p2", "o1").equals(row.get(0)); |
| assert Person.class.getName().equals(row.get(1).getClass().getName()); |
| assert "Joe Black".equals(row.get(2)); |
| assert row.get(3).equals(35); |
| assert row.get(4).equals(1); |
| assert "o1".equals(row.get(5)); |
| assert Organization.class.getName().equals(row.get(6).getClass().getName()); |
| assert row.get(7).equals(1); |
| assert "A".equals(row.get(8)); |
| } |
| if (cnt == 2) { |
| assert new AffinityKey<>("p3", "o2").equals(row.get(0)); |
| assert Person.class.getName().equals(row.get(1).getClass().getName()); |
| assert "Mike Green".equals(row.get(2)); |
| assert row.get(3).equals(40); |
| assert row.get(4).equals(2); |
| assert "o2".equals(row.get(5)); |
| assert Organization.class.getName().equals(row.get(6).getClass().getName()); |
| assert row.get(7).equals(2); |
| assert "B".equals(row.get(8)); |
| } |
| |
| cnt++; |
| } |
| |
| assert cnt == 3; |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testEmptyResult() throws Exception { |
| QueryCursor<List<?>> qry = |
| personCache.query(sqlFieldsQuery("select name from Person where age = 0")); |
| |
| Collection<List<?>> res = qry.getAll(); |
| |
| assertNotNull(res); |
| assertTrue(res.isEmpty()); |
| } |
| |
| /** |
| * Verifies that exactly one record is found when we have equality comparison in where clause (which is supposed |
| * to use {@link BPlusTree#findOne(Object, Object)} instead of {@link BPlusTree#find(Object, Object, Object)}. |
| * |
| * @throws Exception If failed. |
| */ |
| @Test |
| public void testSingleResultUsesFindOne() throws Exception { |
| QueryCursor<List<?>> qry = |
| intCache.query(sqlFieldsQuery("select _val from Integer where _key = 25")); |
| |
| List<List<?>> res = qry.getAll(); |
| |
| assertNotNull(res); |
| assertEquals(1, res.size()); |
| assertEquals(1, res.get(0).size()); |
| assertEquals(25, res.get(0).get(0)); |
| } |
| |
| /** |
| * Verifies that zero records are found when we have equality comparison in where clause (which is supposed |
| * to use {@link BPlusTree#findOne(Object, Object)} instead of {@link BPlusTree#find(Object, Object, Object)} |
| * and the key is not in the cache. |
| * |
| * @throws Exception If failed. |
| */ |
| @Test |
| public void testEmptyResultUsesFindOne() throws Exception { |
| QueryCursor<List<?>> qry = |
| intCache.query(sqlFieldsQuery("select _val from Integer where _key = -10")); |
| |
| List<List<?>> res = qry.getAll(); |
| |
| assertNotNull(res); |
| assertEquals(0, res.size()); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testQueryString() throws Exception { |
| QueryCursor<List<?>> qry = strCache.query(sqlFieldsQuery("select * from String")); |
| |
| Collection<List<?>> res = qry.getAll(); |
| |
| assert res != null; |
| assert res.size() == 1; |
| |
| for (List<?> row : res) { |
| assert row != null; |
| assert row.size() == 2; |
| assert "key".equals(row.get(0)); |
| assert "val".equals(row.get(1)); |
| } |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testQueryIntegersWithJoin() throws Exception { |
| QueryCursor<List<?>> qry = intCache.query(sqlFieldsQuery( |
| "select i._KEY, i._VAL, j._KEY, j._VAL from Integer i join Integer j where i._VAL >= 100")); |
| |
| Collection<List<?>> res = qry.getAll(); |
| |
| assert res != null; |
| |
| assert res.size() <= 20000; |
| |
| for (List<?> row : res) { |
| assert (Integer)row.get(0) >= 100; |
| assert (Integer)row.get(1) >= 100; |
| assert (Integer)row.get(2) >= 0; |
| assert (Integer)row.get(3) >= 0; |
| } |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testPagination() throws Exception { |
| // Query with page size 20. |
| QueryCursor<List<?>> qry = |
| intCache.query(sqlFieldsQuery("select * from Integer").setPageSize(20)); |
| |
| List<List<?>> res = new ArrayList<>(qry.getAll()); |
| |
| dedup(res); |
| |
| Collections.sort(res, new Comparator<List<?>>() { |
| @Override public int compare(List<?> r1, List<?> r2) { |
| return ((Integer)r1.get(0)).compareTo((Integer)r2.get(0)); |
| } |
| }); |
| |
| assertEquals(200, res.size()); |
| |
| for (List<?> row : res) |
| assertEquals("Wrong row size: " + row, 2, row.size()); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testNamedCache() throws Exception { |
| try { |
| IgniteCache<Integer, Integer> cache = jcache("tmp_int", Integer.class, Integer.class); |
| |
| for (int i = 0; i < 200; i++) |
| cache.put(i, i); |
| |
| QueryCursor<List<?>> qry = cache.query(sqlFieldsQuery("select * from Integer")); |
| |
| Collection<List<?>> res = qry.getAll(); |
| |
| assert res != null; |
| assert res.size() == 200; |
| } |
| finally { |
| grid(0).destroyCache("tmp_int"); |
| } |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testNoPrimitives() throws Exception { |
| try { |
| final IgniteCache<Object, Object> cache = grid(0).getOrCreateCache("tmp_without_index"); |
| |
| cache.put("key", "val"); |
| |
| GridTestUtils.assertThrows(log, new Callable<Object>() { |
| @Override public Object call() throws Exception { |
| return cache.query(sqlFieldsQuery("select * from String")); |
| } |
| }, CacheException.class, null); |
| } |
| finally { |
| grid(0).destroyCache("tmp_without_index"); |
| } |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testComplexKeys() throws Exception { |
| IgniteCache<PersonKey, Person> cache = jcache(PersonKey.class, Person.class); |
| |
| UUID id = UUID.randomUUID(); |
| |
| PersonKey key = new PersonKey(id); |
| Person val = new Person("John", 20, 1); |
| |
| cache.put(key, val); |
| |
| Collection<List<?>> res = cache.query(sqlFieldsQuery("select _key, _val, * from Person")).getAll(); |
| |
| assertEquals(1, res.size()); |
| |
| for (Collection<?> row : res) { |
| int cnt = 0; |
| |
| for (Object fieldVal : row) { |
| if (cnt == 0) |
| assertEquals(key, fieldVal); |
| else if (cnt == 1) |
| assertEquals(val, fieldVal); |
| else if (cnt == 2) |
| assertEquals(id, fieldVal); |
| else if (cnt == 3) |
| assertEquals("John", fieldVal); |
| else if (cnt == 4) |
| assertEquals(20, fieldVal); |
| else if (cnt == 5) |
| assertEquals(1, fieldVal); |
| |
| cnt++; |
| } |
| } |
| |
| cache.removeAll(); |
| } |
| |
| /** |
| * @throws Exception If failed. |
| */ |
| @Test |
| public void testPaginationIterator() throws Exception { |
| QueryCursor<List<?>> qry = |
| intCache.query(sqlFieldsQuery("select _key, _val from Integer").setPageSize(10)); |
| |
| int cnt = 0; |
| |
| for (List<?> row : qry) { |
| assertEquals(2, row.size()); |
| assertEquals(row.get(0), row.get(1)); |
| assertTrue((Integer)row.get(0) >= 0 && (Integer)row.get(0) < 200); |
| |
| cnt++; |
| } |
| |
| int size = 200; |
| |
| assertEquals(size, cnt); |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testPaginationIteratorKeepAll() throws Exception { |
| QueryCursor<List<?>> qry = |
| intCache.query(sqlFieldsQuery("select _key, _val from Integer").setPageSize(10)); |
| |
| int cnt = 0; |
| |
| for (List<?> row : qry) { |
| assertEquals(2, row.size()); |
| assertEquals(row.get(0), row.get(1)); |
| assertTrue((Integer)row.get(0) >= 0 && (Integer)row.get(0) < 200); |
| |
| cnt++; |
| } |
| |
| int size = 200; |
| |
| assertEquals(size, cnt); |
| |
| qry = intCache.query(sqlFieldsQuery("select _key, _val from Integer").setPageSize(10)); |
| |
| List<List<?>> list = new ArrayList<>(qry.getAll()); |
| |
| dedup(list); |
| |
| Collections.sort(list, new Comparator<List<?>>() { |
| @Override public int compare(List<?> r1, List<?> r2) { |
| return ((Integer)r1.get(0)).compareTo((Integer)r2.get(0)); |
| } |
| }); |
| |
| for (int i = 0; i < 200; i++) { |
| List<?> r = list.get(i); |
| |
| assertEquals(i, r.get(0)); |
| assertEquals(i, r.get(1)); |
| } |
| } |
| |
| /** |
| * @throws Exception If failed. |
| */ |
| @Test |
| public void testPaginationGet() throws Exception { |
| QueryCursor<List<?>> qry = |
| intCache.query(sqlFieldsQuery("select _key, _val from Integer").setPageSize(10)); |
| |
| List<List<?>> list = new ArrayList<>(qry.getAll()); |
| |
| dedup(list); |
| |
| Collections.sort(list, new Comparator<List<?>>() { |
| @Override public int compare(List<?> r1, List<?> r2) { |
| return ((Integer)r1.get(0)).compareTo((Integer)r2.get(0)); |
| } |
| }); |
| |
| for (int i = 0; i < 200; i++) { |
| List<?> row = list.get(i); |
| |
| assertEquals(i, row.get(0)); |
| assertEquals(i, row.get(1)); |
| } |
| } |
| |
| /** @throws Exception If failed. */ |
| @Test |
| public void testEmptyGrid() throws Exception { |
| QueryCursor<List<?>> qry = personCache |
| .query(sqlFieldsQuery("select name, age from Person where age = 25")); |
| |
| List<?> res = F.first(qry.getAll()); |
| |
| assert res != null; |
| assert res.size() == 2; |
| assert "John White".equals(res.get(0)); |
| assert res.get(1).equals(25); |
| } |
| |
| /** |
| * Dedups result. |
| * |
| * @param res Result. |
| * @throws Exception In case of error. |
| */ |
| private void dedup(Collection<List<?>> res) throws Exception { |
| assert res != null; |
| |
| if (cacheMode() != REPLICATED) |
| return; |
| |
| Collection<List<?>> res0 = new ArrayList<>(res.size()); |
| |
| Collection<Object> keys = new HashSet<>(); |
| |
| for (List<?> row : res) { |
| Object key = row.get(0); |
| |
| if (!keys.contains(key)) { |
| res0.add(row); |
| keys.add(key); |
| } |
| } |
| |
| res.clear(); |
| |
| res.addAll(res0); |
| } |
| |
| /** |
| * Person key. |
| */ |
| private static class PersonKey implements Serializable { |
| /** ID. */ |
| @QuerySqlField |
| private final UUID id; |
| |
| /** @param id ID. */ |
| private PersonKey(UUID id) { |
| assert id != null; |
| |
| this.id = id; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean equals(Object o) { |
| if (this == o) |
| return true; |
| |
| if (o == null || getClass() != o.getClass()) |
| return false; |
| |
| PersonKey key = (PersonKey)o; |
| |
| return id.equals(key.id); |
| |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int hashCode() { |
| return id.hashCode(); |
| } |
| } |
| |
| /** |
| * Person. |
| */ |
| private static class Person implements Serializable { |
| /** Name. */ |
| @QuerySqlField(index = false) |
| private final String name; |
| |
| /** Age. */ |
| @QuerySqlField(index = true) |
| private final int age; |
| |
| /** Organization ID. */ |
| @QuerySqlField(index = true) |
| private final int orgId; |
| |
| /** |
| * @param name Name. |
| * @param age Age. |
| * @param orgId Organization ID. |
| */ |
| private Person(String name, int age, int orgId) { |
| assert !F.isEmpty(name); |
| assert age > 0; |
| assert orgId > 0; |
| |
| this.name = name; |
| this.age = age; |
| this.orgId = orgId; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean equals(Object o) { |
| if (this == o) |
| return true; |
| |
| if (o == null || getClass() != o.getClass()) |
| return false; |
| |
| Person person = (Person)o; |
| |
| return age == person.age && orgId == person.orgId && name.equals(person.name); |
| |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int hashCode() { |
| int res = name.hashCode(); |
| |
| res = 31 * res + age; |
| res = 31 * res + orgId; |
| |
| return res; |
| } |
| } |
| |
| /** |
| * Organization. |
| */ |
| private static class Organization implements Serializable { |
| /** ID. */ |
| @QuerySqlField |
| private final int id; |
| |
| /** Name. */ |
| @QuerySqlField(index = false) |
| private final String name; |
| |
| /** |
| * @param id ID. |
| * @param name Name. |
| */ |
| private Organization(int id, String name) { |
| assert id > 0; |
| assert !F.isEmpty(name); |
| |
| this.id = id; |
| this.name = name; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean equals(Object o) { |
| if (this == o) |
| return true; |
| |
| if (o == null || getClass() != o.getClass()) |
| return false; |
| |
| Organization that = (Organization)o; |
| |
| return id == that.id && name.equals(that.name); |
| |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int hashCode() { |
| int res = id; |
| |
| res = 31 * res + name.hashCode(); |
| |
| return res; |
| } |
| } |
| } |