Repository: ignite Updated Branches: refs/heads/master 04f074023 -> a6e97d146
IGNITE-9519: SQL: Allow complex object inline into indexes. This closes #4724. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/a6e97d14 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/a6e97d14 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/a6e97d14 Branch: refs/heads/master Commit: a6e97d146db68dc510616ef4bd14281d3bba0a8e Parents: 04f0740 Author: Stanislav Lukyanov <slukya...@gridgain.com> Authored: Thu Sep 20 17:19:17 2018 +0300 Committer: devozerov <voze...@gridgain.com> Committed: Thu Sep 20 17:19:17 2018 +0300 ---------------------------------------------------------------------- .../internal/binary/BinaryObjectImpl.java | 2 +- .../query/h2/database/InlineIndexHelper.java | 32 +- .../query/h2/opt/GridH2ValueCacheObject.java | 6 + .../processors/cache/index/BasicIndexTest.java | 820 +++++++++++++++++++ .../h2/database/InlineIndexHelperTest.java | 148 +++- .../IgniteCacheQuerySelfTestSuite.java | 3 + 6 files changed, 981 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/a6e97d14/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java index 7ab8b35..65fb349 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/binary/BinaryObjectImpl.java @@ -205,7 +205,7 @@ public final class BinaryObjectImpl extends BinaryObjectExImpl implements Extern /** * @return Detached binary object. */ - public BinaryObject detach() { + public BinaryObjectImpl detach() { if (!detachAllowed || detached()) return this; http://git-wip-us.apache.org/repos/asf/ignite/blob/a6e97d14/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java index 05d5c46..0299033 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelper.java @@ -35,6 +35,7 @@ import org.h2.value.ValueDate; import org.h2.value.ValueDouble; import org.h2.value.ValueFloat; 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; @@ -70,7 +71,8 @@ public class InlineIndexHelper { Value.STRING, Value.STRING_FIXED, Value.STRING_IGNORECASE, - Value.BYTES + Value.BYTES, + Value.JAVA_OBJECT ); /** */ @@ -153,6 +155,7 @@ public class InlineIndexHelper { case Value.STRING_FIXED: case Value.STRING_IGNORECASE: case Value.BYTES: + case Value.JAVA_OBJECT: this.size = -1; break; @@ -298,6 +301,9 @@ public class InlineIndexHelper { case Value.BYTES: return ValueBytes.get(readBytes(pageAddr, off)); + case Value.JAVA_OBJECT: + return ValueJavaObject.getNoCopy(null, readBytes(pageAddr, off), null); + default: throw new UnsupportedOperationException("no get operation for fast index type " + type); } @@ -327,6 +333,7 @@ public class InlineIndexHelper { case Value.STRING_FIXED: case Value.STRING_IGNORECASE: case Value.BYTES: + case Value.JAVA_OBJECT: return (PageUtils.getShort(pageAddr, off + 1) & 0x8000) == 0; default: @@ -417,6 +424,7 @@ public class InlineIndexHelper { return compareAsUUID(pageAddr, off, v, type); case Value.BYTES: + case Value.JAVA_OBJECT: return compareAsBytes(pageAddr, off, v); } @@ -900,23 +908,30 @@ public class InlineIndexHelper { } } - case Value.BYTES: { + case Value.BYTES: + case Value.JAVA_OBJECT: + { short size; PageUtils.putByte(pageAddr, off, (byte)val.getType()); - if (val.getBytes().length + 3 <= maxSize) { - size = (short)val.getBytes().length; + byte[] bytes = val.getBytes(); + + if (bytes.length + 3 <= maxSize) { + size = (short)bytes.length; PageUtils.putShort(pageAddr, off + 1, size); - PageUtils.putBytes(pageAddr, off + 3, val.getBytes()); + PageUtils.putBytes(pageAddr, off + 3, bytes); + + return size + 3; } else { size = (short)((maxSize - 3) | 0x8000); PageUtils.putShort(pageAddr, off + 1, size); - PageUtils.putBytes(pageAddr, off + 3, Arrays.copyOfRange(val.getBytes(), 0, maxSize - 3)); + PageUtils.putBytes(pageAddr, off + 3, Arrays.copyOfRange(bytes, 0, maxSize - 3)); + + return maxSize; } - return size + 3; } default: @@ -959,6 +974,7 @@ public class InlineIndexHelper { case Value.STRING_FIXED: case Value.STRING_IGNORECASE: case Value.BYTES: + case Value.JAVA_OBJECT: if (shortVal.getType() == Value.NULL || v2.getType() == Value.NULL) return true; @@ -968,7 +984,7 @@ public class InlineIndexHelper { int l1; int l2; - if (type == Value.BYTES) { + if (type == Value.BYTES || type == Value.JAVA_OBJECT) { l1 = shortVal.getBytes().length; l2 = v2.getBytes().length; } http://git-wip-us.apache.org/repos/asf/ignite/blob/a6e97d14/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java index d18668f..9b5f0d5 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2ValueCacheObject.java @@ -21,6 +21,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.binary.BinaryObjectImpl; import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.CacheObjectValueContext; import org.h2.message.DbException; @@ -49,6 +50,11 @@ public class GridH2ValueCacheObject extends Value { public GridH2ValueCacheObject(CacheObject obj, CacheObjectValueContext valCtx) { assert obj != null; + if (obj instanceof BinaryObjectImpl) { + ((BinaryObjectImpl)obj).detachAllowed(true); + obj = ((BinaryObjectImpl)obj).detach(); + } + this.obj = obj; this.valCtx = valCtx; } http://git-wip-us.apache.org/repos/asf/ignite/blob/a6e97d14/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java new file mode 100644 index 0000000..feb330b --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/BasicIndexTest.java @@ -0,0 +1,820 @@ +/* + * 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.index; + +import java.io.File; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Objects; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.cache.CacheKeyConfiguration; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.QueryIndex; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataRegionConfiguration; +import org.apache.ignite.configuration.DataStorageConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.processors.cache.IgniteInternalCache; +import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager; +import org.apache.ignite.internal.util.typedef.internal.S; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * A set of basic tests for caches with indexes. + */ +public class BasicIndexTest extends GridCommonAbstractTest { + /** */ + private static final TcpDiscoveryIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true); + + /** */ + private Collection<QueryIndex> indexes; + + /** */ + private Integer inlineSize; + + /** */ + private Boolean isPersistenceEnabled; + + /** */ + private String affKeyFieldName; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + assertNotNull(indexes); + + assertNotNull(inlineSize); + + assertNotNull(isPersistenceEnabled); + + for (QueryIndex index : indexes) { + index.setInlineSize(inlineSize); + } + + IgniteConfiguration igniteCfg = super.getConfiguration(igniteInstanceName); + + igniteCfg.setDiscoverySpi( + new TcpDiscoverySpi().setIpFinder(IP_FINDER) + ); + + LinkedHashMap<String, String> fields = new LinkedHashMap<>(); + fields.put("keyStr", String.class.getName()); + fields.put("keyLong", Long.class.getName()); + fields.put("keyPojo", Pojo.class.getName()); + fields.put("valStr", String.class.getName()); + fields.put("valLong", Long.class.getName()); + fields.put("valPojo", Pojo.class.getName()); + + CacheConfiguration<Key, Val> ccfg = new CacheConfiguration<Key, Val>(DEFAULT_CACHE_NAME) + .setQueryEntities(Collections.singleton( + new QueryEntity() + .setKeyType(Key.class.getName()) + .setValueType(Val.class.getName()) + .setFields(fields) + .setIndexes(indexes) + )) + .setSqlIndexMaxInlineSize(inlineSize); + + if (affKeyFieldName != null) { + ccfg.setKeyConfiguration(new CacheKeyConfiguration() + .setTypeName(Key.class.getTypeName()) + .setAffinityKeyFieldName(affKeyFieldName) + ); + } + + igniteCfg.setCacheConfiguration(ccfg); + + if (isPersistenceEnabled) { + igniteCfg.setDataStorageConfiguration(new DataStorageConfiguration() + .setDefaultDataRegionConfiguration( + new DataRegionConfiguration().setPersistenceEnabled(true) + ) + ); + } + + return igniteCfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + stopAllGrids(); + + cleanPersistenceDir(); + } + + /** {@inheritDoc} */ + @Override protected void afterTest() throws Exception { + stopAllGrids(); + + cleanPersistenceDir(); + + indexes = null; + + inlineSize = null; + + isPersistenceEnabled = null; + + affKeyFieldName = null; + + super.afterTest(); + } + + /** */ + public void testNoIndexesNoPersistence() throws Exception { + indexes = Collections.emptyList(); + + isPersistenceEnabled = false; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + populateCache(); + + checkAll(); + + stopGrid(); + } + } + + /** */ + public void testAllIndexesNoPersistence() throws Exception { + indexes = Arrays.asList( + new QueryIndex("keyStr"), + new QueryIndex("keyLong"), + new QueryIndex("keyPojo"), + new QueryIndex("valStr"), + new QueryIndex("valLong"), + new QueryIndex("valPojo") + ); + + isPersistenceEnabled = false; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + populateCache(); + + checkAll(); + + stopGrid(); + } + } + + /** */ + public void testDynamicIndexesNoPersistence() throws Exception { + indexes = Collections.emptyList(); + + isPersistenceEnabled = false; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + populateCache(); + + createDynamicIndexes( + "keyStr", + "keyLong", + "keyPojo", + "valStr", + "valLong", + "valPojo" + ); + + checkAll(); + + stopGrid(); + } + } + + /** */ + public void testNoIndexesWithPersistence() throws Exception { + indexes = Collections.emptyList(); + + isPersistenceEnabled = true; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + grid().cluster().active(true); + + populateCache(); + + checkAll(); + + stopGrid(); + + startGrid(); + + grid().cluster().active(true); + + checkAll(); + + stopGrid(); + + cleanPersistenceDir(); + } + } + + /** */ + public void testAllIndexesWithPersistence() throws Exception { + indexes = Arrays.asList( + new QueryIndex("keyStr"), + new QueryIndex("keyLong"), + new QueryIndex("keyPojo"), + new QueryIndex("valStr"), + new QueryIndex("valLong"), + new QueryIndex("valPojo") + ); + + isPersistenceEnabled = true; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + grid().cluster().active(true); + + populateCache(); + + checkAll(); + + stopGrid(); + + startGrid(); + + grid().cluster().active(true); + + checkAll(); + + stopGrid(); + + cleanPersistenceDir(); + } + } + + /** */ + public void testDynamicIndexesWithPersistence() throws Exception { + indexes = Collections.emptyList(); + + isPersistenceEnabled = true; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + grid().cluster().active(true); + + populateCache(); + + createDynamicIndexes( + "keyStr", + "keyLong", + "keyPojo", + "valStr", + "valLong", + "valPojo" + ); + + checkAll(); + + stopGrid(); + + startGrid(); + + grid().cluster().active(true); + + checkAll(); + + stopGrid(); + + cleanPersistenceDir(); + } + } + + /** */ + public void testNoIndexesWithPersistenceIndexRebuild() throws Exception { + indexes = Collections.emptyList(); + + isPersistenceEnabled = true; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + grid().cluster().active(true); + + populateCache(); + + checkAll(); + + Path idxPath = getIndexBinPath(); + + // Shutdown gracefully to ensure there is a checkpoint with index.bin. + // Otherwise index.bin rebuilding may not work. + grid().cluster().active(false); + + stopGrid(); + + assertTrue(U.delete(idxPath)); + + startGrid(); + + grid().cluster().active(true); + + grid().cache(DEFAULT_CACHE_NAME).indexReadyFuture().get(); + + checkAll(); + + stopGrid(); + + cleanPersistenceDir(); + } + } + + /** */ + public void testAllIndexesWithPersistenceIndexRebuild() throws Exception { + indexes = Arrays.asList( + new QueryIndex("keyStr"), + new QueryIndex("keyLong"), + new QueryIndex("keyPojo"), + new QueryIndex("valStr"), + new QueryIndex("valLong"), + new QueryIndex("valPojo") + ); + + isPersistenceEnabled = true; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + grid().cluster().active(true); + + populateCache(); + + checkAll(); + + Path idxPath = getIndexBinPath(); + + // Shutdown gracefully to ensure there is a checkpoint with index.bin. + // Otherwise index.bin rebuilding may not work. + grid().cluster().active(false); + + stopGrid(); + + assertTrue(U.delete(idxPath)); + + startGrid(); + + grid().cluster().active(true); + + grid().cache(DEFAULT_CACHE_NAME).indexReadyFuture().get(); + + checkAll(); + + stopGrid(); + + cleanPersistenceDir(); + } + } + + /** */ + public void testDynamicIndexesWithPersistenceIndexRebuild() throws Exception { + indexes = Collections.emptyList(); + + isPersistenceEnabled = true; + + int[] inlineSizes = { 0, 10, 20, 50, 100 }; + + for (int i : inlineSizes) { + log().info("Checking inlineSize=" + i); + + inlineSize = i; + + startGrid(); + + grid().cluster().active(true); + + populateCache(); + + createDynamicIndexes( + "keyStr", + "keyLong", + "keyPojo", + "valStr", + "valLong", + "valPojo" + ); + + checkAll(); + + Path idxPath = getIndexBinPath(); + + // Shutdown gracefully to ensure there is a checkpoint with index.bin. + // Otherwise index.bin rebuilding may not work. + grid().cluster().active(false); + + stopGrid(); + + assertTrue(U.delete(idxPath)); + + startGrid(); + + grid().cluster().active(true); + + grid().cache(DEFAULT_CACHE_NAME).indexReadyFuture().get(); + + checkAll(); + + stopGrid(); + + cleanPersistenceDir(); + } + } + + /** */ + private void checkAll() { + IgniteCache<Key, Val> cache = grid().cache(DEFAULT_CACHE_NAME); + + checkRemovePut(cache); + + checkSelectAll(cache); + + checkSelectStringEqual(cache); + + checkSelectLongEqual(cache); + + checkSelectStringRange(cache); + + checkSelectLongRange(cache); + } + + /** */ + private void populateCache() { + IgniteCache<Key, Val> cache = grid().cache(DEFAULT_CACHE_NAME); + + // Be paranoid and populate first even indexes in ascending order, then odd indexes in descending + // to check that inserting in the middle works. + + for (int i = 0; i < 100; i += 2) + cache.put(key(i), val(i)); + + for (int i = 99; i > 0; i -= 2) + cache.put(key(i), val(i)); + + for (int i = 99; i > 0; i -= 2) + assertEquals(val(i), cache.get(key(i))); + } + + /** */ + private void checkRemovePut(IgniteCache<Key, Val> cache) { + final int INT = 24; + + assertEquals(val(INT), cache.get(key(INT))); + + cache.remove(key(INT)); + + assertNull(cache.get(key(INT))); + + cache.put(key(INT), val(INT)); + + assertEquals(val(INT), cache.get(key(INT))); + } + + /** */ + private void checkSelectAll(IgniteCache<Key, Val> cache) { + List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val")).getAll(); + + assertEquals(100, data.size()); + + for (List<?> row : data) { + Key key = (Key)row.get(0); + + Val val = (Val)row.get(1); + + long i = key.keyLong; + + assertEquals(key(i), key); + + assertEquals(val(i), val); + } + } + + /** */ + private void checkSelectStringEqual(IgniteCache<Key, Val> cache) { + final String STR = "foo011"; + + final long LONG = 11; + + List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val where keyStr = ?") + .setArgs(STR)) + .getAll(); + + assertEquals(1, data.size()); + + List<?> row = data.get(0); + + assertEquals(key(LONG), row.get(0)); + + assertEquals(val(LONG), row.get(1)); + } + + /** */ + private void checkSelectLongEqual(IgniteCache<Key, Val> cache) { + final long LONG = 42; + + List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val where valLong = ?") + .setArgs(LONG)) + .getAll(); + + assertEquals(1, data.size()); + + List<?> row = data.get(0); + + assertEquals(key(LONG), row.get(0)); + + assertEquals(val(LONG), row.get(1)); + } + + /** */ + private void checkSelectStringRange(IgniteCache<Key, Val> cache) { + final String PREFIX = "foo06"; + + List<List<?>> data = cache.query(new SqlFieldsQuery("select _key, _val from Val where keyStr like ?") + .setArgs(PREFIX + "%")) + .getAll(); + + assertEquals(10, data.size()); + + for (List<?> row : data) { + Key key = (Key)row.get(0); + + Val val = (Val)row.get(1); + + long i = key.keyLong; + + assertEquals(key(i), key); + + assertEquals(val(i), val); + + assertTrue(key.keyStr.startsWith(PREFIX)); + } + } + + /** */ + private void checkSelectLongRange(IgniteCache<Key, Val> cache) { + final long RANGE_START = 70; + + final long RANGE_END = 80; + + List<List<?>> data = cache.query( + new SqlFieldsQuery("select _key, _val from Val where valLong >= ? and valLong < ?") + .setArgs(RANGE_START, RANGE_END)) + .getAll(); + + assertEquals(10, data.size()); + + for (List<?> row : data) { + Key key = (Key)row.get(0); + + Val val = (Val)row.get(1); + + long i = key.keyLong; + + assertEquals(key(i), key); + + assertEquals(val(i), val); + + assertTrue(i >= RANGE_START && i < RANGE_END); + } + } + + /** Must be called when the grid is up. */ + private Path getIndexBinPath() { + IgniteInternalCache<Object, Object> cachex = grid().cachex(DEFAULT_CACHE_NAME); + + assertNotNull(cachex); + + FilePageStoreManager pageStoreMgr = (FilePageStoreManager)cachex.context().shared().pageStore(); + + assertNotNull(pageStoreMgr); + + File cacheWorkDir = pageStoreMgr.cacheWorkDir(cachex.configuration()); + + return cacheWorkDir.toPath().resolve("index.bin"); + } + + /** */ + private void createDynamicIndexes(String... cols) { + IgniteCache<Key, Val> cache = grid().cache(DEFAULT_CACHE_NAME); + + for (String col : cols) { + cache.query(new SqlFieldsQuery( + "create index on Val(" + col + ") INLINE_SIZE " + inlineSize + )); + } + + cache.indexReadyFuture().get(); + } + + /** */ + private static Key key(long i) { + return new Key(String.format("foo%03d", i), i, new Pojo(i)); + } + + /** */ + private static Val val(long i) { + return new Val(String.format("bar%03d", i), i, new Pojo(i)); + } + + /** */ + private static class Key { + /** */ + private String keyStr; + + /** */ + private long keyLong; + + /** */ + private Pojo keyPojo; + + /** */ + private Key(String str, long aLong, Pojo pojo) { + keyStr = str; + keyLong = aLong; + keyPojo = pojo; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + Key key = (Key)o; + + return keyLong == key.keyLong && + Objects.equals(keyStr, key.keyStr) && + Objects.equals(keyPojo, key.keyPojo); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(keyStr, keyLong, keyPojo); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(Key.class, this); + } + } + + /** */ + private static class Val { + /** */ + private String valStr; + + /** */ + private long valLong; + + /** */ + private Pojo valPojo; + + /** */ + private Val(String str, long aLong, Pojo pojo) { + valStr = str; + valLong = aLong; + valPojo = pojo; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + Val val = (Val)o; + + return valLong == val.valLong && + Objects.equals(valStr, val.valStr) && + Objects.equals(valPojo, val.valPojo); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(valStr, valLong, valPojo); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(Val.class, this); + } + } + + /** */ + private static class Pojo { + /** */ + private long pojoLong; + + /** */ + private Pojo(long pojoLong) { + this.pojoLong = pojoLong; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + Pojo pojo = (Pojo)o; + + return pojoLong == pojo.pojoLong; + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(pojoLong); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return S.toString(Pojo.class, this); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/a6e97d14/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelperTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelperTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelperTest.java index ea23483..4c64264 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelperTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/database/InlineIndexHelperTest.java @@ -1,3 +1,4 @@ + /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -42,6 +43,7 @@ import org.h2.value.ValueDate; import org.h2.value.ValueDouble; import org.h2.value.ValueFloat; 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; @@ -49,6 +51,7 @@ import org.h2.value.ValueString; import org.h2.value.ValueTime; import org.h2.value.ValueTimestamp; import org.h2.value.ValueUuid; +import org.springframework.util.SerializationUtils; /** * Simple tests for {@link InlineIndexHelper}. @@ -169,7 +172,7 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { int i1 = rnd.nextInt(strings.length); int i2 = rnd.nextInt(strings.length); - assertEquals(Integer.compare(i1,i2), putAndCompare(strings[i1], strings[i2], inlineSize)); + assertEquals(Integer.compare(i1, i2), putAndCompare(strings[i1], strings[i2], inlineSize)); } } @@ -185,12 +188,12 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { .setMaxSize(1024 * MB); PageMemory pageMem = new PageMemoryNoStoreImpl(log, - new UnsafeMemoryProvider(log), - null, - PAGE_SIZE, - plcCfg, - new DataRegionMetricsImpl(plcCfg), - false); + new UnsafeMemoryProvider(log), + null, + PAGE_SIZE, + plcCfg, + new DataRegionMetricsImpl(plcCfg), + false); pageMem.start(); @@ -209,7 +212,7 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { ih.put(pageAddr, off, v1 == null ? ValueNull.INSTANCE : ValueString.get(v1), maxSize); - return ih.compare(pageAddr, off, maxSize, v2 == null ? ValueNull.INSTANCE : ValueString.get(v2), ALWAYS_FAILS_COMPARATOR); + return ih.compare(pageAddr, off, maxSize, v2 == null ? ValueNull.INSTANCE : ValueString.get(v2), ALWAYS_FAILS_COMPARATOR); } finally { if (page != 0L) @@ -271,6 +274,30 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { assertTrue(getResBytes(ha, null, null)); } + /** Test on Bytes values compare */ + public void testRelyOnCompareJavaObject() { + InlineIndexHelper ha = new InlineIndexHelper(Value.JAVA_OBJECT, 0, SortOrder.ASCENDING, + CompareMode.getInstance(null, 0)); + + // different types + assertTrue(getResJavaObjects(ha, new String("1234"), new Integer(10))); + + // the same types, but different values + assertTrue(getResJavaObjects(ha, new String("1234"), new String("123"))); + + // the same types and values + assertFalse(getResJavaObjects(ha, new String("1234"), new String("1234"))); + + // the same object + String key = "1"; + assertFalse(getResJavaObjects(ha, key, key)); + + // one is null + assertTrue(getResJavaObjects(ha, key, null)); + assertTrue(getResJavaObjects(ha, null, key)); + assertTrue(getResJavaObjects(ha, null, null)); + } + /** */ public void testStringTruncate() throws Exception { DataRegionConfiguration plcCfg = new DataRegionConfiguration().setInitialSize(1024 * MB) @@ -350,17 +377,86 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { InlineIndexHelper ih = new InlineIndexHelper(Value.BYTES, 1, 0, CompareMode.getInstance(null, 0)); - ih.put(pageAddr, off, ValueBytes.get(new byte[] {1, 2, 3, 4, 5}), 3 + 3); + int maxSize = 3 + 3; + int savedBytesCnt = ih.put(pageAddr, off, ValueBytes.get(new byte[] {1, 2, 3, 4, 5}), maxSize); + + assertTrue(savedBytesCnt > 0); + + assertTrue(savedBytesCnt <= maxSize); + + assertFalse(ih.isValueFull(pageAddr, off)); + + maxSize = 3 + 5; + + assertTrue(Arrays.equals(new byte[] {1, 2, 3}, ih.get(pageAddr, off, maxSize).getBytes())); + + savedBytesCnt = ih.put(pageAddr, off, ValueBytes.get(new byte[] {1, 2, 3, 4, 5}), maxSize); + + assertTrue(savedBytesCnt > 0); + + assertTrue(savedBytesCnt <= maxSize); + + assertTrue(ih.isValueFull(pageAddr, off)); + + assertTrue(Arrays.equals(new byte[] {1, 2, 3, 4, 5}, ih.get(pageAddr, off, maxSize).getBytes())); + } + finally { + if (page != 0L) + pageMem.releasePage(CACHE_ID, pageId, page); + pageMem.stop(); + } + } + + /** */ + public void testJavaObject() throws Exception { + DataRegionConfiguration plcCfg = new DataRegionConfiguration().setInitialSize(1024 * MB) + .setMaxSize(1024 * MB); + + PageMemory pageMem = new PageMemoryNoStoreImpl(log(), + new UnsafeMemoryProvider(log()), + null, + PAGE_SIZE, + plcCfg, + new DataRegionMetricsImpl(plcCfg), + false); + + pageMem.start(); + + long pageId = 0L; + long page = 0L; + + try { + pageId = pageMem.allocatePage(CACHE_ID, 1, PageIdAllocator.FLAG_DATA); + page = pageMem.acquirePage(CACHE_ID, pageId); + long pageAddr = pageMem.readLock(CACHE_ID, pageId, page); + + int off = 0; + + InlineIndexHelper ih = new InlineIndexHelper(Value.JAVA_OBJECT, 1, 0, + CompareMode.getInstance(null, 0)); + + int maxSize = 3 + 3; + int savedBytesCnt = ih.put(pageAddr, off, ValueJavaObject.getNoCopy(null, new byte[] {1, 2, 3, 4, 5}, null), maxSize); + + assertTrue(savedBytesCnt > 0); + + assertTrue(savedBytesCnt <= maxSize); assertFalse(ih.isValueFull(pageAddr, off)); - assertTrue(Arrays.equals(new byte[] {1, 2, 3}, ih.get(pageAddr, off, 3 + 5).getBytes())); + maxSize = 3 + 5; + + assertTrue(Arrays.equals(new byte[] {1, 2, 3}, ih.get(pageAddr, off, maxSize).getBytes())); - ih.put(pageAddr, off, ValueBytes.get(new byte[] {1, 2, 3, 4, 5}), 3 + 5); + savedBytesCnt = ih.put(pageAddr, off, ValueJavaObject.getNoCopy(null, new byte[] {1, 2, 3, 4, 5}, null), maxSize); + + assertTrue(savedBytesCnt > 0); + + assertTrue(savedBytesCnt <= maxSize); assertTrue(ih.isValueFull(pageAddr, off)); - assertTrue(Arrays.equals(new byte[] {1, 2, 3, 4, 5}, ih.get(pageAddr, off, 3 + 5).getBytes())); + assertTrue(Arrays.equals(new byte[] {1, 2, 3, 4, 5}, ih.get(pageAddr, off, maxSize).getBytes())); } finally { if (page != 0L) @@ -510,6 +606,16 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { return ha.canRelyOnCompare(c, v1, v2); } + /** */ + private boolean getResJavaObjects(InlineIndexHelper ha, Object o1, Object o2) { + Value v1 = o1 == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(null, SerializationUtils.serialize(o1), null); + Value v2 = o2 == null ? ValueNull.INSTANCE : ValueJavaObject.getNoCopy(null, SerializationUtils.serialize(o2), null); + + int c = v1.compareTypeSafe(v2, CompareMode.getInstance(null, 0)); + + return ha.canRelyOnCompare(c, v1, v2); + } + /** * @param cnt String length. * @return Random string. @@ -523,31 +629,31 @@ public class InlineIndexHelperTest extends GridCommonAbstractTest { char ch; if (rnd.nextInt(100) > 3) - ch = (char) (rnd.nextInt(95) + 32); // regular symbols + ch = (char)(rnd.nextInt(95) + 32); // regular symbols else - ch = (char) (rnd.nextInt(65407) + 127); // others symbols + ch = (char)(rnd.nextInt(65407) + 127); // others symbols - if(ch >= 56320 && ch <= 57343) { - if(cnt == 0) + if (ch >= 56320 && ch <= 57343) { + if (cnt == 0) cnt++; else { // low surrogate, insert high surrogate after putting it in buffer[cnt] = ch; cnt--; - buffer[cnt] = (char) (55296 + rnd.nextInt(128)); + buffer[cnt] = (char)(55296 + rnd.nextInt(128)); } } - else if(ch >= 55296 && ch <= 56191) { - if(cnt == 0) + else if (ch >= 55296 && ch <= 56191) { + if (cnt == 0) cnt++; else { // high surrogate, insert low surrogate before putting it in - buffer[cnt] = (char) (56320 + rnd.nextInt(128)); + buffer[cnt] = (char)(56320 + rnd.nextInt(128)); cnt--; buffer[cnt] = ch; } } - else if(ch >= 56192 && ch <= 56319) + else if (ch >= 56192 && ch <= 56319) // private high surrogate, no effing clue, so skip it cnt++; else http://git-wip-us.apache.org/repos/asf/ignite/blob/a6e97d14/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java index 7f67b35..1036eed 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java @@ -104,6 +104,7 @@ import org.apache.ignite.internal.processors.cache.distributed.replicated.Ignite import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQueryEvtsDisabledSelfTest; import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQueryP2PDisabledSelfTest; import org.apache.ignite.internal.processors.cache.distributed.replicated.IgniteCacheReplicatedQuerySelfTest; +import org.apache.ignite.internal.processors.cache.index.BasicIndexTest; import org.apache.ignite.internal.processors.cache.index.DuplicateKeyValueClassesSelfTest; import org.apache.ignite.internal.processors.cache.index.DynamicIndexClientBasicSelfTest; import org.apache.ignite.internal.processors.cache.index.DynamicIndexServerBasicSelfTest; @@ -225,6 +226,8 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite { suite.addTestSuite(SqlIllegalSchemaSelfTest.class); suite.addTestSuite(MultipleStatementsSqlQuerySelfTest.class); + suite.addTestSuite(BasicIndexTest.class); + // Misc tests. // TODO: Enable when IGNITE-1094 is fixed. // suite.addTest(new TestSuite(QueryEntityValidationSelfTest.class));