You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by se...@apache.org on 2017/04/10 07:22:13 UTC
[1/2] ignite git commit: IGNITE-4524: Index direct lookup for SQL
MIN() and MAX() #1749 - Fixes #1749.
Repository: ignite
Updated Branches:
refs/heads/ignite-3477-master c2d8bd9a4 -> c449d6e93
IGNITE-4524: Index direct lookup for SQL MIN() and MAX() #1749 - Fixes #1749.
Signed-off-by: Sergi Vladykin <se...@gmail.com>
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/035b0fa8
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/035b0fa8
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/035b0fa8
Branch: refs/heads/ignite-3477-master
Commit: 035b0fa8712489b0ba86bd612611f258c2e85f5d
Parents: c2d8bd9
Author: skalashnikov <sk...@gridgain.com>
Authored: Mon Apr 10 10:19:03 2017 +0300
Committer: Sergi Vladykin <se...@gmail.com>
Committed: Mon Apr 10 10:19:03 2017 +0300
----------------------------------------------------------------------
.../cache/database/tree/BPlusTree.java | 103 ++++-
.../apache/ignite/internal/util/IgniteTree.java | 14 +
.../processors/database/BPlusTreeSelfTest.java | 28 +-
.../query/h2/database/H2TreeIndex.java | 16 +-
.../query/h2/opt/GridH2TreeIndex.java | 29 +-
.../query/h2/IgniteSqlQueryMinMaxTest.java | 376 +++++++++++++++++++
.../query/h2/opt/GridH2TableSelfTest.java | 50 +++
.../IgniteCacheQuerySelfTestSuite.java | 2 +
8 files changed, 603 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
index f3d020b..e5fd0ab 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
@@ -251,7 +251,13 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
g.backId(0L); // Usually we'll go left down and don't need it.
int cnt = io.getCount(pageAddr);
- int idx = findInsertionPoint(io, pageAddr, 0, cnt, g.row, g.shift);
+
+ int idx;
+ if (g.findLast)
+ idx = io.isLeaf()? cnt - 1: -cnt - 1; //(-cnt - 1) mimics not_found result of findInsertionPoint
+ //in case of cnt = 0 we end up in 'not found' branch below with idx being 0 after fix() adjustment
+ else
+ idx = findInsertionPoint(io, pageAddr, 0, cnt, g.row, g.shift);
boolean found = idx >= 0;
@@ -949,6 +955,76 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
}
}
+ /** {@inheritDoc} */
+ @Override public T findFirst() throws IgniteCheckedException {
+ checkDestroyed();
+
+ try {
+ long firstPageId;
+
+ long metaPage = acquirePage(metaPageId);
+ try {
+ firstPageId = getFirstPageId(metaPageId, metaPage, 0);
+ }
+ finally {
+ releasePage(metaPageId, metaPage);
+ }
+
+ long page = acquirePage(firstPageId);
+ try {
+ long pageAddr = readLock(firstPageId, page);
+
+ try {
+ BPlusIO<L> io = io(pageAddr);
+
+ int cnt = io.getCount(pageAddr);
+
+ if (cnt == 0)
+ return null;
+
+ return getRow(io, pageAddr, 0);
+ }
+ finally {
+ readUnlock(firstPageId, page, pageAddr);
+ }
+ }
+ finally {
+ releasePage(firstPageId, page);
+ }
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteCheckedException("Runtime failure on first row lookup", e);
+ }
+ catch (RuntimeException e) {
+ throw new IgniteException("Runtime failure on first row lookup", e);
+ }
+ catch (AssertionError e) {
+ throw new AssertionError("Assertion error on first row lookup", e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override public T findLast() throws IgniteCheckedException {
+ checkDestroyed();
+
+ try {
+ GetOne g = new GetOne(null, null, true);
+ doFind(g);
+
+ return (T)g.row;
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteCheckedException("Runtime failure on last row lookup", e);
+ }
+ catch (RuntimeException e) {
+ throw new IgniteException("Runtime failure on last row lookup", e);
+ }
+ catch (AssertionError e) {
+ throw new AssertionError("Assertion error on last row lookup", e);
+ }
+ }
+
/**
* @param row Lookup row for exact match.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
@@ -960,7 +1036,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
checkDestroyed();
try {
- GetOne g = new GetOne(row, x);
+ GetOne g = new GetOne(row, x, false);
doFind(g);
@@ -2251,13 +2327,18 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/** If this operation is a part of invoke. */
Invoke invoke;
+ /** Ignore row passed, find last row */
+ boolean findLast;
+
/**
* @param row Row.
+ * @param findLast find last row.
*/
- Get(L row) {
- assert row != null;
+ Get(L row, boolean findLast) {
+ assert findLast ^ row != null;
this.row = row;
+ this.findLast = findLast;
}
/**
@@ -2270,6 +2351,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
fwdId = g.fwdId;
backId = g.backId;
shift = g.shift;
+ findLast = g.findLast;
}
/**
@@ -2372,9 +2454,10 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/**
* @param row Row.
* @param x Implementation specific argument.
+ * @param findLast Ignore row passed, find last row
*/
- private GetOne(L row, Object x) {
- super(row);
+ private GetOne(L row, Object x, boolean findLast) {
+ super(row, findLast);
this.x = x;
}
@@ -2405,7 +2488,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param cursor Cursor.
*/
GetCursor(L lower, int shift, ForwardCursor cursor) {
- super(lower);
+ super(lower, false);
assert shift != 0; // Either handle range of equal rows or find a greater row after concurrent merge.
@@ -2468,7 +2551,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param needOld {@code True} If need return old value.
*/
private Put(T row, boolean needOld) {
- super(row);
+ super(row, false);
this.needOld = needOld;
}
@@ -2789,7 +2872,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param clo Closure.
*/
private Invoke(L row, Object x, final InvokeClosure<T> clo) {
- super(row);
+ super(row, false);
assert clo != null;
@@ -3089,7 +3172,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param needOld {@code True} If need return old value.
*/
private Remove(L row, boolean needOld) {
- super(row);
+ super(row, false);
this.needOld = needOld;
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
index 7eae0d5..396b8a4 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java
@@ -64,6 +64,20 @@ public interface IgniteTree<L, T> {
public GridCursor<T> find(L lower, L upper) throws IgniteCheckedException;
/**
+ * Returns a value mapped to the lowest key, or {@code null} if tree is empty
+ * @return Value.
+ * @throws IgniteCheckedException If failed.
+ */
+ public T findFirst() throws IgniteCheckedException;
+
+ /**
+ * Returns a value mapped to the greatest key, or {@code null} if tree is empty
+ * @return Value.
+ * @throws IgniteCheckedException If failed.
+ */
+ public T findLast() throws IgniteCheckedException;
+
+ /**
* Removes the mapping for a key from this tree if it is present.
*
* @param key Key whose mapping is to be removed from the tree.
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
index 9e5ca70..dd89406 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/BPlusTreeSelfTest.java
@@ -121,7 +121,7 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
protected void assertNoLocks() {
assertTrue(TestTree.checkNoLocks());
}
-
+
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
long seed = System.nanoTime();
@@ -1198,6 +1198,32 @@ public class BPlusTreeSelfTest extends GridCommonAbstractTest {
}
/**
+ * @throws IgniteCheckedException If failed.
+ */
+ public void testFindFirstAndLast() throws IgniteCheckedException {
+ MAX_PER_PAGE = 5;
+
+ TestTree tree = createTestTree(true);
+
+ Long first = tree.findFirst();
+ assertNull(first);
+
+ Long last = tree.findLast();
+ assertNull(last);
+
+ for (long idx = 1L; idx <= 10L; ++idx)
+ tree.put(idx);
+
+ first = tree.findFirst();
+ assertEquals((Long)1L, first);
+
+ last = tree.findLast();
+ assertEquals((Long)10L, last);
+
+ assertNoLocks();
+ }
+
+ /**
* @param canGetRow Can get row from inner page.
* @throws Exception If failed.
*/
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
index 51a997b..1ea3204 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/database/H2TreeIndex.java
@@ -39,6 +39,7 @@ import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
+import org.h2.index.SingleRowCursor;
import org.h2.message.DbException;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
@@ -300,12 +301,23 @@ public class H2TreeIndex extends GridH2IndexBase {
/** {@inheritDoc} */
@Override public boolean canGetFirstOrLast() {
- return false;
+ return true;
}
/** {@inheritDoc} */
@Override public Cursor findFirstOrLast(Session session, boolean b) {
- throw new UnsupportedOperationException();
+ try {
+ int seg = threadLocalSegment();
+
+ H2Tree tree = treeForRead(seg);
+
+ GridH2Row row = b ? tree.findFirst(): tree.findLast();
+
+ return new SingleRowCursor(row);
+ }
+ catch (IgniteCheckedException e) {
+ throw DbException.convert(e);
+ }
}
/** {@inheritDoc} */
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
index d28b99e..4a12c78 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TreeIndex.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.query.h2.opt;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.apache.ignite.IgniteCheckedException;
@@ -35,6 +36,7 @@ import org.apache.ignite.spi.indexing.IndexingQueryFilter;
import org.h2.engine.Session;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
+import org.h2.index.SingleRowCursor;
import org.h2.message.DbException;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
@@ -380,12 +382,23 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
/** {@inheritDoc} */
@Override public boolean canGetFirstOrLast() {
- return false;
+ return true;
}
/** {@inheritDoc} */
@Override public Cursor findFirstOrLast(Session ses, boolean first) {
- throw DbException.throwInternalError();
+ try {
+ int seg = threadLocalSegment();
+
+ IgniteTree t = treeForRead(seg);
+
+ GridH2Row row = (GridH2Row)(first ? t.findFirst() : t.findLast());
+
+ return new SingleRowCursor(row);
+ }
+ catch (IgniteCheckedException e) {
+ throw DbException.convert(e);
+ }
}
/** {@inheritDoc} */
@@ -548,6 +561,18 @@ public class GridH2TreeIndex extends GridH2IndexBase implements Comparator<GridS
}
/** {@inheritDoc} */
+ @Override public GridH2Row findFirst() throws IgniteCheckedException {
+ Map.Entry<GridSearchRowPointer, GridH2Row> first = tree.firstEntry();
+ return (first == null) ? null : first.getValue();
+ }
+
+ /** {@inheritDoc} */
+ @Override public GridH2Row findLast() throws IgniteCheckedException {
+ Map.Entry<GridSearchRowPointer, GridH2Row> last = tree.lastEntry();
+ return (last == null) ? null : last.getValue();
+ }
+
+ /** {@inheritDoc} */
@Override public GridH2Row remove(GridSearchRowPointer key) {
return tree.remove(key);
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
new file mode 100644
index 0000000..0f50d7e
--- /dev/null
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/IgniteSqlQueryMinMaxTest.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.processors.query.h2;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+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.spi.discovery.tcp.TcpDiscoverySpi;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+
+import java.util.List;
+
+/** Test for SQL min() and max() optimization */
+public class IgniteSqlQueryMinMaxTest extends GridCommonAbstractTest {
+ /** IP finder. */
+ private static final TcpDiscoveryVmIpFinder IP_FINDER = new TcpDiscoveryVmIpFinder(true);
+
+ /** Name of the cache for test */
+ private static final String CACHE_NAME = "intCache";
+
+ /** Name of the second test cache */
+ private static final String CACHE_NAME_2 = "valCache";
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ startGrids(4);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void afterTest() throws Exception {
+ super.afterTest();
+
+ stopAllGrids();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(gridName);
+
+ TcpDiscoverySpi spi = (TcpDiscoverySpi)cfg.getDiscoverySpi();
+
+ spi.setIpFinder(IP_FINDER);
+
+ CacheConfiguration<?, ?> ccfg = new CacheConfiguration<>();
+ ccfg.setIndexedTypes(Integer.class, Integer.class);
+ ccfg.setName(CACHE_NAME);
+
+ CacheConfiguration<?, ?> ccfg2 = new CacheConfiguration<>();
+ ccfg2.setIndexedTypes(Integer.class, ValueObj.class);
+ ccfg2.setName(CACHE_NAME_2);
+
+ cfg.setCacheConfiguration(ccfg, ccfg2);
+
+ if ("client".equals(gridName))
+ cfg.setClientMode(true);
+
+ return cfg;
+ }
+
+ /** Check min() and max() functions in queries */
+ public void testQueryMinMax() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, ValueObj> cache = client.cache(CACHE_NAME_2);
+
+ int count = 1_000;
+ for (int idx = 0; idx < count; ++idx)
+ cache.put(idx, new ValueObj(count - idx - 1, 0));
+
+ long start = System.currentTimeMillis();
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("select min(_key), max(_key) from ValueObj"));
+ List<List<?>> result = cursor.getAll();
+ assertEquals(1, result.size());
+ assertEquals(0, result.get(0).get(0));
+ assertEquals(count - 1, result.get(0).get(1));
+ if (log.isDebugEnabled())
+ log.debug("Elapsed(1): " + (System.currentTimeMillis() - start));
+
+ start = System.currentTimeMillis();
+ cursor = cache.query(new SqlFieldsQuery("select min(idxVal), max(idxVal) from ValueObj"));
+ result = cursor.getAll();
+ assertEquals(1, result.size());
+ assertEquals(0, result.get(0).get(0));
+ assertEquals(count - 1, result.get(0).get(1));
+ if (log.isDebugEnabled())
+ log.debug("Elapsed(2): " + (System.currentTimeMillis() - start));
+
+ start = System.currentTimeMillis();
+ cursor = cache.query(new SqlFieldsQuery("select min(nonIdxVal), max(nonIdxVal) from ValueObj"));
+ result = cursor.getAll();
+ assertEquals(1, result.size());
+ assertEquals(0, result.get(0).get(0));
+ assertEquals(count - 1, result.get(0).get(1));
+ if (log.isDebugEnabled())
+ log.debug("Elapsed(3): " + (System.currentTimeMillis() - start));
+ }
+ }
+
+ /** Check min() and max() on empty cache */
+ public void testQueryMinMaxEmptyCache() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, ValueObj> cache = client.cache(CACHE_NAME_2);
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("select min(idxVal), max(idxVal) from ValueObj"));
+ List<List<?>> result = cursor.getAll();
+ assertEquals(1, result.size());
+ assertEquals(2, result.get(0).size());
+ assertNull(result.get(0).get(0));
+ assertNull(result.get(0).get(1));
+ }
+ }
+
+ /**
+ * Check min() and max() over _key use correct index
+ * Test uses value object cache
+ */
+ public void testMinMaxQueryPlanOnKey() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, ValueObj> cache = client.cache(CACHE_NAME_2);
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(_key), max(_key) from ValueObj"));
+ List<List<?>> result = cursor.getAll();
+ assertEquals(2, result.size());
+ assertTrue(((String) result.get(0).get(0)).contains("_key_PK"));
+ assertTrue(((String) result.get(0).get(0)).contains("direct lookup"));
+ }
+ }
+
+ /**
+ * Check min() and max() over value fields use correct index.
+ * Test uses value object cache
+ */
+ public void testMinMaxQueryPlanOnFields() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, ValueObj> cache = client.cache(CACHE_NAME_2);
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(idxVal), max(idxVal) from ValueObj"));
+ List<List<?>> result = cursor.getAll();
+ assertEquals(2, result.size());
+ assertTrue(((String)result.get(0).get(0)).contains("idxVal_idx"));
+ assertTrue(((String)result.get(0).get(0)).contains("direct lookup"));
+ }
+ }
+
+ /**
+ * Check min() and max() over _key uses correct index
+ * Test uses primitive cache
+ */
+ public void testSimpleMinMaxQueryPlanOnKey() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, Integer> cache = client.cache(CACHE_NAME);
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(_key), max(_key) from Integer"));
+ List<List<?>> result = cursor.getAll();
+ assertEquals(2, result.size());
+ assertTrue(((String)result.get(0).get(0)).contains("_key_PK"));
+ assertTrue(((String)result.get(0).get(0)).contains("direct lookup"));
+ }
+ }
+
+ /**
+ * Check min() and max() over _val uses correct index.
+ * Test uses primitive cache
+ */
+ public void testSimpleMinMaxQueryPlanOnValue() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, Integer> cache = client.cache(CACHE_NAME);
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery("explain select min(_val), max(_val) from Integer"));
+ List<List<?>> result = cursor.getAll();
+ assertEquals(2, result.size());
+ assertTrue(((String)result.get(0).get(0)).contains("_val_idx"));
+ assertTrue(((String)result.get(0).get(0)).contains("direct lookup"));
+ }
+ }
+
+ /** Check min() and max() over group */
+ public void testGroupMinMax() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, ValueObj> cache = client.cache(CACHE_NAME_2);
+
+ int count = 1_000;
+ int groupSize = 100;
+ for (int idx = 0; idx < count; ++idx)
+ cache.put(idx, new ValueObj(count - idx - 1, groupSize));
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery(
+ "select groupVal, min(idxVal), max(idxVal), min(nonIdxVal), max(nonIdxVal) " +
+ " from ValueObj group by groupVal order by groupVal"));
+ List<List<?>> result = cursor.getAll();
+
+ assertEquals(count / groupSize, result.size());
+
+ for (int idx = 0; idx < result.size(); ++idx) {
+ assertEquals(idx, result.get(idx).get(0));//groupVal
+ int min = idx * groupSize;
+ int max = (idx + 1) * groupSize - 1;
+ assertEquals(min, result.get(idx).get(1));//min(idxVal)
+ assertEquals(max, result.get(idx).get(2));//max(idxVal)
+ assertEquals(min, result.get(idx).get(3));//min(nonIdxVal)
+ assertEquals(max, result.get(idx).get(4));//max(nonIdxVal)
+ }
+ }
+ }
+
+ /** Check min() and max() over group with having clause */
+ public void testGroupHavingMinMax() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, ValueObj> cache = client.cache(CACHE_NAME_2);
+
+ int count = 1_000;
+ int groupSize = 100;
+ for (int idx = 0; idx < count; ++idx)
+ cache.put(idx, new ValueObj(count - idx - 1, groupSize));
+
+ QueryCursor<List<?>> cursor = cache.query(new SqlFieldsQuery(
+ "select groupVal, min(idxVal), max(idxVal), min(nonIdxVal), max(nonIdxVal) " +
+ "from ValueObj group by groupVal having min(idxVal) = ?" ).setArgs(0));
+
+ List<List<?>> result = cursor.getAll();
+ assertEquals(1, result.size());
+ assertEquals(0, result.get(0).get(0));//groupVal
+ assertEquals(0, result.get(0).get(1));//min(idxVal)
+ assertEquals(groupSize - 1, result.get(0).get(2));//max(idxVal)
+ assertEquals(0, result.get(0).get(3));//min(nonIdxVal)
+ assertEquals(groupSize - 1, result.get(0).get(4));//max(nonIdxVal)
+
+ cursor = cache.query(new SqlFieldsQuery(
+ "select groupVal, min(idxVal), max(idxVal), min(nonIdxVal), max(nonIdxVal) " +
+ "from ValueObj group by groupVal having max(idxVal) = ?" ).setArgs(count - 1));
+
+ result = cursor.getAll();
+ assertEquals(1, result.size());
+ assertEquals((count - 1)/groupSize, result.get(0).get(0));//groupVal
+ assertEquals(count - groupSize, result.get(0).get(1));//min(idxVal)
+ assertEquals(count - 1, result.get(0).get(2));//max(idxVal)
+ assertEquals(count - groupSize, result.get(0).get(3));//min(nonIdxVal)
+ assertEquals(count - 1, result.get(0).get(4));//max(nonIdxVal)
+ }
+ }
+
+ /** Check min() and max() over group with joins */
+ public void testJoinGroupMinMax() throws Exception {
+ try (Ignite client = startGrid("client")) {
+ IgniteCache<Integer, Integer> cache = client.cache(CACHE_NAME);
+ IgniteCache<Integer, ValueObj> cache2 = client.cache(CACHE_NAME_2);
+
+ int count = 1_000;
+ int groupSize = 100;
+ for (int idx = 0; idx < count; ++idx) {
+ cache.put(idx, idx);
+ cache2.put(idx, new ValueObj(count - idx - 1, groupSize));
+ }
+
+ //join a.key = b.key, collocated
+ QueryCursor<List<?>> cursor = cache.query(
+ new SqlFieldsQuery("select b.groupVal, min(a._key), max(a._key), min(a._val), max(a._val), " +
+ "min(b._key), max(b._key), min(b.idxVal), max(b.idxVal), min(b.nonIdxVal), max(b.nonIdxVal) " +
+ "from \"intCache\".Integer a, \"valCache\".ValueObj b where a._key = b._key " +
+ "group by b.groupVal order by b.groupVal"));
+
+ List<List<?>> result = cursor.getAll();
+ assertEquals(count / groupSize, result.size());
+ for (int idx = 0; idx < result.size(); ++idx) {
+ assertEquals(idx, result.get(idx).get(0));
+ int min = idx * groupSize;
+ int max = (idx + 1) * groupSize - 1;
+ int revMin = count - max - 1;
+ int revMax = count - min - 1;
+
+ assertEquals(revMin, result.get(idx).get(1));//min(a._key)
+ assertEquals(revMax, result.get(idx).get(2));//max(a._key)
+ assertEquals(revMin, result.get(idx).get(3));//min(a._val)
+ assertEquals(revMax, result.get(idx).get(4));//max(a._val)
+ assertEquals(revMin, result.get(idx).get(5));//min(b._key)
+ assertEquals(revMax, result.get(idx).get(6));//max(b_key)
+ assertEquals(min, result.get(idx).get(7));//min(b.idxVal)
+ assertEquals(max, result.get(idx).get(8));//max(b.idxVal),
+ assertEquals(min, result.get(idx).get(9));//min(b.nonIdxVal)
+ assertEquals(max, result.get(idx).get(10));//max(b.nonIdxVal)
+ }
+
+ //join a.key = b.val, non-collocated
+ cursor = cache.query(
+ new SqlFieldsQuery("select b.groupVal, min(a._key), max(a._key), min(a._val), max(a._val), " +
+ "min(b._key), max(b._key), min(b.idxVal), max(b.idxVal), min(b.nonIdxVal), max(b.nonIdxVal) " +
+ "from \"intCache\".Integer a, \"valCache\".ValueObj b where a._key = b.idxVal " +
+ "group by b.groupVal order by b.groupVal")
+ .setDistributedJoins(true));
+
+ result = cursor.getAll();
+
+ assertEquals(count / groupSize, result.size());
+ for (int idx = 0; idx < result.size(); ++idx) {
+ assertEquals(idx, result.get(idx).get(0));
+ int min = idx * groupSize;
+ int max = (idx + 1) * groupSize - 1;
+ int revMin = count - max - 1;
+ int revMax = count - min - 1;
+
+ assertEquals(min, result.get(idx).get(1));//min(a._key)
+ assertEquals(max, result.get(idx).get(2));//max(a._key)
+ assertEquals(min, result.get(idx).get(3));//min(a._val)
+ assertEquals(max, result.get(idx).get(4));//max(a._val)
+ assertEquals(revMin, result.get(idx).get(5));//min(b._key)
+ assertEquals(revMax, result.get(idx).get(6));//max(b_key)
+ assertEquals(min, result.get(idx).get(7));//min(b.idxVal)
+ assertEquals(max, result.get(idx).get(8));//max(b.idxVal),
+ assertEquals(min, result.get(idx).get(9));//min(b.nonIdxVal)
+ assertEquals(max, result.get(idx).get(10));//max(b.nonIdxVal)
+ }
+ }
+ }
+
+ /** Value object for test cache */
+ public class ValueObj {
+ /** */
+ @QuerySqlField(index = true)
+ private final int idxVal;
+
+ /** */
+ @QuerySqlField
+ private final int nonIdxVal;
+
+ /** used for grouping */
+ @QuerySqlField
+ private final int groupVal;
+
+ /** */
+ public ValueObj(int v, int g) {
+ this.idxVal = v;
+ this.nonIdxVal = v;
+ this.groupVal = (g == 0) ? v : v / g;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ return idxVal;
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof ValueObj))
+ return false;
+
+ ValueObj other = (ValueObj)o;
+ return idxVal == other.idxVal &&
+ nonIdxVal == other.nonIdxVal &&
+ groupVal == other.groupVal;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
index f9388ef..5dd3f65 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2TableSelfTest.java
@@ -39,6 +39,7 @@ import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.h2.Driver;
+import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.result.Row;
import org.h2.result.SearchRow;
@@ -509,6 +510,55 @@ public class GridH2TableSelfTest extends GridCommonAbstractTest {
assertEquals(ids.length - deleted.get(), rs.getInt(1));
}
+
+ /**
+ * @throws Exception If failed.
+ */
+ public void testIndexFindFirstOrLast() throws Exception {
+ Index index = tbl.getIndexes().get(2);
+ assertTrue(index instanceof GridH2TreeIndex);
+ assertTrue(index.canGetFirstOrLast());
+
+ //find first on empty data
+ Cursor cursor = index.findFirstOrLast(null, true);
+ assertFalse(cursor.next());
+ assertNull(cursor.get());
+
+ //find last on empty data
+ cursor = index.findFirstOrLast(null, false);
+ assertFalse(cursor.next());
+ assertNull(cursor.get());
+
+ //fill with data
+ int rows = 100;
+ long t = System.currentTimeMillis();
+ Random rnd = new Random();
+ UUID min = null;
+ UUID max = null;
+
+ for (int i = 0 ; i < rows; i++) {
+ UUID id = UUID.randomUUID();
+ if (min == null || id.compareTo(min) < 0)
+ min = id;
+ if (max == null || id.compareTo(max) > 0)
+ max = id;
+ GridH2Row row = row(id, t++, id.toString(), rnd.nextInt(100));
+ ((GridH2TreeIndex)index).put(row);
+ }
+
+ //find first
+ cursor = index.findFirstOrLast(null, true);
+ assertTrue(cursor.next());
+ assertEquals(min, cursor.get().getValue(0).getObject());
+ assertFalse(cursor.next());
+
+ //find last
+ cursor = index.findFirstOrLast(null, false);
+ assertTrue(cursor.next());
+ assertEquals(max, cursor.get().getValue(0).getObject());
+ assertFalse(cursor.next());
+ }
+
/**
* Check query plan to correctly select index.
*
http://git-wip-us.apache.org/repos/asf/ignite/blob/035b0fa8/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 adfc50e..2ce4072 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
@@ -98,6 +98,7 @@ import org.apache.ignite.internal.processors.query.IgniteSqlSegmentedIndexSelfTe
import org.apache.ignite.internal.processors.query.IgniteSqlSplitterSelfTest;
import org.apache.ignite.internal.processors.query.h2.GridH2IndexingInMemSelfTest;
import org.apache.ignite.internal.processors.query.h2.GridH2IndexingOffheapSelfTest;
+import org.apache.ignite.internal.processors.query.h2.IgniteSqlQueryMinMaxTest;
import org.apache.ignite.internal.processors.query.h2.opt.GridH2TableSelfTest;
import org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest;
import org.apache.ignite.internal.processors.query.h2.sql.GridQueryParsingTest;
@@ -184,6 +185,7 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite {
suite.addTestSuite(IndexingSpiQueryTxSelfTest.class);
suite.addTestSuite(IgniteCacheMultipleIndexedTypesTest.class);
+ suite.addTestSuite(IgniteSqlQueryMinMaxTest.class);
// Fields queries.
suite.addTestSuite(SqlFieldsQuerySelfTest.class);
[2/2] ignite git commit: ignite-3477-master - minor
Posted by se...@apache.org.
ignite-3477-master - minor
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/c449d6e9
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/c449d6e9
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/c449d6e9
Branch: refs/heads/ignite-3477-master
Commit: c449d6e93ffda3e895615d5958a1b00e476e3ad6
Parents: 035b0fa
Author: Sergi Vladykin <se...@gmail.com>
Authored: Mon Apr 10 10:22:03 2017 +0300
Committer: Sergi Vladykin <se...@gmail.com>
Committed: Mon Apr 10 10:22:03 2017 +0300
----------------------------------------------------------------------
.../internal/processors/cache/database/tree/BPlusTree.java | 7 +++++++
1 file changed, 7 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/c449d6e9/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
index e5fd0ab..648f35a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/database/tree/BPlusTree.java
@@ -971,6 +971,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
}
long page = acquirePage(firstPageId);
+
try {
long pageAddr = readLock(firstPageId, page);
@@ -1001,6 +1002,9 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
catch (AssertionError e) {
throw new AssertionError("Assertion error on first row lookup", e);
}
+ finally {
+ checkDestroyed();
+ }
}
/** {@inheritDoc} */
@@ -1023,6 +1027,9 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
catch (AssertionError e) {
throw new AssertionError("Assertion error on last row lookup", e);
}
+ finally {
+ checkDestroyed();
+ }
}
/**