You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2022/12/09 06:35:16 UTC
[ignite] branch master updated: IGNITE-13030 SQL Calcite: Inline index scans - Fixes #10397.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 029754992c5 IGNITE-13030 SQL Calcite: Inline index scans - Fixes #10397.
029754992c5 is described below
commit 029754992c55cd1f25436f21ecf870e18004349a
Author: Aleksey Plekhanov <pl...@gmail.com>
AuthorDate: Fri Dec 9 09:27:19 2022 +0300
IGNITE-13030 SQL Calcite: Inline index scans - Fixes #10397.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../benchmarks/jmh/sql/JmhSqlBenchmark.java | 43 ++++--
.../benchmarks/jmh/tree/IndexFindBenchmark.java | 4 +-
.../processors/query/calcite/exec/IndexScan.java | 143 ++++++++++++++++++-
.../query/calcite/rel/AbstractIndexScan.java | 27 +++-
.../query/calcite/schema/CacheIndexImpl.java | 33 +++++
.../query/calcite/schema/IgniteIndex.java | 5 +
.../query/calcite/schema/SystemViewIndexImpl.java | 5 +
.../calcite/exec/LogicalRelImplementorTest.java | 2 +-
.../integration/AbstractBasicIntegrationTest.java | 5 +
.../integration/IndexScanlIntegrationTest.java | 152 +++++++++++++++++++--
.../query/calcite/planner/AbstractPlannerTest.java | 3 +-
.../planner/InlineIndexScanPlannerTest.java | 76 +++++++++++
.../calcite/planner/MergeJoinPlannerTest.java | 3 +-
.../query/calcite/planner/TestTable.java | 44 +++++-
.../apache/ignite/testsuites/PlannerTestSuite.java | 2 +
.../query/index/IndexQueryCriteriaClosure.java | 3 +-
.../cache/query/index/IndexSingleRangeQuery.java | 14 +-
...exSearchRowImpl.java => IndexPlainRowImpl.java} | 18 +--
.../cache/query/index/sorted/IndexRow.java | 6 +-
.../cache/query/index/sorted/IndexRowImpl.java | 13 +-
.../index/sorted/client/ClientIndexFactory.java | 2 +-
.../index/sorted/client/ClientInlineIndex.java | 18 ++-
.../index/sorted/inline/IndexQueryContext.java | 21 +++
.../query/index/sorted/inline/InlineIndex.java | 6 +
.../query/index/sorted/inline/InlineIndexImpl.java | 12 +-
.../index/sorted/inline/InlineIndexKeyType.java | 7 +-
.../query/index/sorted/inline/InlineIndexTree.java | 4 +-
.../index/sorted/inline/InlineRecommender.java | 2 +-
.../inline/types/BytesInlineIndexKeyType.java | 7 +-
.../inline/types/NullableInlineIndexKeyType.java | 19 +++
.../types/ObjectByteArrayInlineIndexKeyType.java | 4 +-
.../inline/types/StringInlineIndexKeyType.java | 7 +-
.../types/StringNoCompareInlineIndexKeyType.java | 4 +-
.../cache/persistence/tree/BPlusTree.java | 63 +++++++--
.../processors/query/h2/database/H2TreeIndex.java | 4 +-
35 files changed, 667 insertions(+), 114 deletions(-)
diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/sql/JmhSqlBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/sql/JmhSqlBenchmark.java
index a4d231e97de..78c0589f1d2 100644
--- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/sql/JmhSqlBenchmark.java
+++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/sql/JmhSqlBenchmark.java
@@ -149,7 +149,8 @@ public class JmhSqlBenchmark {
List<?> res = executeSql("SELECT name FROM Item WHERE fld=?", key);
- assert res.size() == 1;
+ if (res.size() != 1)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -161,7 +162,8 @@ public class JmhSqlBenchmark {
List<?> res = executeSql("SELECT name FROM Item WHERE fldIdx=?", key);
- assert res.size() == 1;
+ if (res.size() != 1)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -173,7 +175,8 @@ public class JmhSqlBenchmark {
List<?> res = executeSql("SELECT name FROM Item WHERE fldBatch=?", key / BATCH_SIZE);
- assert res.size() == BATCH_SIZE;
+ if (res.size() != BATCH_SIZE)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -185,7 +188,8 @@ public class JmhSqlBenchmark {
List<?> res = executeSql("SELECT name FROM Item WHERE fldIdxBatch=?", key / BATCH_SIZE);
- assert res.size() == BATCH_SIZE;
+ if (res.size() != BATCH_SIZE)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -195,7 +199,8 @@ public class JmhSqlBenchmark {
public void queryGroupBy() {
List<?> res = executeSql("SELECT fldBatch, AVG(fld) FROM Item GROUP BY fldBatch");
- assert res.size() == KEYS_CNT / BATCH_SIZE;
+ if (res.size() != KEYS_CNT / BATCH_SIZE)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -205,7 +210,8 @@ public class JmhSqlBenchmark {
public void queryGroupByIndexed() {
List<?> res = executeSql("SELECT fldIdxBatch, AVG(fld) FROM Item GROUP BY fldIdxBatch");
- assert res.size() == KEYS_CNT / BATCH_SIZE;
+ if (res.size() != KEYS_CNT / BATCH_SIZE)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -215,7 +221,8 @@ public class JmhSqlBenchmark {
public void queryOrderByFull() {
List<?> res = executeSql("SELECT name, fld FROM Item ORDER BY fld DESC");
- assert res.size() == KEYS_CNT;
+ if (res.size() != KEYS_CNT)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
/**
@@ -227,14 +234,26 @@ public class JmhSqlBenchmark {
List<?> res = executeSql("SELECT name, fld FROM Item WHERE fldIdxBatch=? ORDER BY fld DESC", key / BATCH_SIZE);
- assert res.size() == BATCH_SIZE;
+ if (res.size() != BATCH_SIZE)
+ throw new AssertionError("Unexpected result size: " + res.size());
}
- /** */
- private List<?> executeSql(String sql, Object... args) {
- List<List<?>> res = cache.query(new SqlFieldsQuery(sql).setArgs(args)).getAll();
+ /**
+ * Query sum of indexed field.
+ */
+ @Benchmark
+ public void querySumIndexed() {
+ List<List<?>> res = executeSql("SELECT sum(fldIdx) FROM Item");
- return res.get(0);
+ Long expRes = ((long)KEYS_CNT) * (KEYS_CNT - 1) / 2;
+
+ if (!expRes.equals(res.get(0).get(0)))
+ throw new AssertionError("Unexpected result: " + res.get(0));
+ }
+
+ /** */
+ private List<List<?>> executeSql(String sql, Object... args) {
+ return cache.query(new SqlFieldsQuery(sql).setArgs(args)).getAll();
}
/**
diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/IndexFindBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/IndexFindBenchmark.java
index 0b215f945a0..b63525fbe4c 100644
--- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/IndexFindBenchmark.java
+++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/tree/IndexFindBenchmark.java
@@ -27,8 +27,8 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexPlainRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
-import org.apache.ignite.internal.cache.query.index.sorted.IndexSearchRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKeyFactory;
@@ -118,7 +118,7 @@ public class IndexFindBenchmark {
/** */
private static IndexRow searchRow(Object key, IndexKeyType type) {
IndexKey[] keys = new IndexKey[] { IndexKeyFactory.wrap(key, type, null, null), null };
- return new IndexSearchRowImpl(keys, null);
+ return new IndexPlainRowImpl(keys, null);
}
/** */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
index 7a6ca9ea761..49a54d10199 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/IndexScan.java
@@ -30,11 +30,14 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.cluster.ClusterTopologyException;
import org.apache.ignite.internal.GridKernalContext;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexPlainRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
-import org.apache.ignite.internal.cache.query.index.sorted.IndexSearchRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.InlineIndexRowHandler;
import org.apache.ignite.internal.cache.query.index.sorted.inline.IndexQueryContext;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
+import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.inline.io.InlineIO;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKeyFactory;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
@@ -44,6 +47,8 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.topology.Grid
import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState;
import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionTopology;
import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
+import org.apache.ignite.internal.processors.cache.persistence.tree.io.BPlusIO;
import org.apache.ignite.internal.processors.query.calcite.exec.RowHandler.RowFactory;
import org.apache.ignite.internal.processors.query.calcite.exec.exp.RangeIterable;
import org.apache.ignite.internal.processors.query.calcite.schema.CacheTableDescriptor;
@@ -91,6 +96,9 @@ public class IndexScan<Row> extends AbstractIndexScan<Row, IndexRow> {
/** Mapping from index keys to row fields. */
private final ImmutableIntList idxFieldMapping;
+ /** Mapping from row fields to index keys. */
+ private final int[] fieldIdxMapping;
+
/** Types of key fields stored in index. */
private final Type[] fieldsStoreTypes;
@@ -163,6 +171,50 @@ public class IndexScan<Row> extends AbstractIndexScan<Row, IndexRow> {
for (int i = 0; i < srcRowType.getFieldCount(); i++)
fieldsStoreTypes[i] = typeFactory.getResultClass(srcRowType.getFieldList().get(i).getType());
+
+ fieldIdxMapping = fieldToInlinedKeysMapping(srcRowType.getFieldCount());
+ }
+
+ /**
+ * Checks if we can use inlined index keys instead of cache row iteration and returns fields to keys mapping.
+ *
+ * @return Mapping from target row fields to inlined index keys, or {@code null} if inlined index keys
+ * should not be used.
+ */
+ private int[] fieldToInlinedKeysMapping(int srcFieldsCnt) {
+ List<InlineIndexKeyType> inlinedKeys = idx.segment(0).rowHandler().inlineIndexKeyTypes();
+
+ // Even if we need some subset of inlined keys we are required to the read full inlined row, since this row
+ // is also participated in comparison with other rows when cursor processing the next index page.
+ if (inlinedKeys.size() < idx.segment(0).rowHandler().indexKeyDefinitions().size() ||
+ inlinedKeys.size() < (requiredColumns == null ? srcFieldsCnt : requiredColumns.cardinality()))
+ return null;
+
+ for (InlineIndexKeyType keyType : inlinedKeys) {
+ // Variable length types can be not fully inlined, so it's probably better to directly read full cache row
+ // instead of trying to read inlined value and than falllback to cache row reading.
+ // Inlined JAVA_OBJECT can't be compared with fill cache row in case of hash collision, this can lead to
+ // issues when processing the next index page in cursor if current page was concurrently splitted.
+ if (keyType.keySize() < 0 || keyType.type() == IndexKeyType.JAVA_OBJECT)
+ return null;
+ }
+
+ ImmutableBitSet reqCols = requiredColumns == null ? ImmutableBitSet.range(0, srcFieldsCnt) :
+ requiredColumns;
+
+ int[] fieldIdxMapping = new int[rowType.getFieldCount()];
+
+ for (int i = 0, j = reqCols.nextSetBit(0); j != -1; j = reqCols.nextSetBit(j + 1), i++) {
+ // j = source field index, i = target field index.
+ int keyIdx = idxFieldMapping.indexOf(j);
+
+ if (keyIdx >= 0 && keyIdx < inlinedKeys.size())
+ fieldIdxMapping[i] = keyIdx;
+ else
+ return null;
+ }
+
+ return fieldIdxMapping;
}
/** {@inheritDoc} */
@@ -208,12 +260,27 @@ public class IndexScan<Row> extends AbstractIndexScan<Row, IndexRow> {
}
}
- return nullSearchRow ? null : new IndexSearchRowImpl(keys, idxRowHnd);
+ return nullSearchRow ? null : new IndexPlainRowImpl(keys, idxRowHnd);
}
/** {@inheritDoc} */
@Override protected Row indexRow2Row(IndexRow row) throws IgniteCheckedException {
- return desc.toRow(ectx, row.cacheDataRow(), factory, requiredColumns);
+ if (row.indexPlainRow())
+ return inlineIndexRow2Row(row);
+ else
+ return desc.toRow(ectx, row.cacheDataRow(), factory, requiredColumns);
+ }
+
+ /** */
+ private Row inlineIndexRow2Row(IndexRow row) {
+ RowHandler<Row> hnd = ectx.rowHandler();
+
+ Row res = factory.create();
+
+ for (int i = 0; i < fieldIdxMapping.length; i++)
+ hnd.set(i, res, TypeUtils.toInternal(ectx, row.key(fieldIdxMapping[i]).key()));
+
+ return res;
}
/** */
@@ -302,7 +369,75 @@ public class IndexScan<Row> extends AbstractIndexScan<Row, IndexRow> {
/** {@inheritDoc} */
@Override protected IndexQueryContext indexQueryContext() {
IndexingQueryFilter filter = new IndexingQueryFilterImpl(kctx, topVer, parts);
- return new IndexQueryContext(filter, null, mvccSnapshot);
+
+ InlineIndexRowHandler rowHnd = idx.segment(0).rowHandler();
+
+ InlineIndexRowFactory rowFactory = isInlineScan() ?
+ new InlineIndexRowFactory(rowHnd.inlineIndexKeyTypes().toArray(new InlineIndexKeyType[0]), rowHnd) : null;
+
+ return new IndexQueryContext(filter, null, rowFactory, mvccSnapshot);
+ }
+
+ /** */
+ public boolean isInlineScan() {
+ return fieldIdxMapping != null;
+ }
+
+ /** */
+ private static class InlineIndexRowFactory implements BPlusTree.TreeRowFactory<IndexRow, IndexRow> {
+ /** Inline key types. */
+ private final InlineIndexKeyType[] keyTypes;
+
+ /** */
+ private final InlineIndexRowHandler idxRowHnd;
+
+ /** Read full cache index row instead of inlined values. */
+ private boolean useCacheRow;
+
+ /** */
+ private InlineIndexRowFactory(
+ InlineIndexKeyType[] keyTypes,
+ InlineIndexRowHandler idxRowHnd
+ ) {
+ this.keyTypes = keyTypes;
+ this.idxRowHnd = idxRowHnd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public IndexRow create(
+ BPlusTree<IndexRow, IndexRow> tree,
+ BPlusIO<IndexRow> io,
+ long pageAddr,
+ int idx
+ ) throws IgniteCheckedException {
+ if (useCacheRow)
+ return io.getLookupRow(tree, pageAddr, idx);
+
+ int inlineSize = ((InlineIO)io).inlineSize();
+ int rowOffset = io.offset(idx);
+ int keyOffset = 0;
+
+ IndexKey[] keys = new IndexKey[keyTypes.length];
+
+ // Check if all required keys is inlined before creating index row.
+ for (int keyIdx = 0; keyIdx < keyTypes.length; keyIdx++) {
+ InlineIndexKeyType keyType = keyTypes[keyIdx];
+
+ if (!keyType.inlinedFullValue(pageAddr, rowOffset + keyOffset, inlineSize - keyOffset)) {
+ // Since we are checking only fixed-length keys, this condition means that for all rows current
+ // key type is not fully inlined, so fallback to cache index row.
+ useCacheRow = true;
+
+ return io.getLookupRow(tree, pageAddr, idx);
+ }
+
+ keys[keyIdx] = keyType.get(pageAddr, rowOffset + keyOffset, inlineSize - keyOffset);
+
+ keyOffset += keyType.inlineSize(pageAddr, rowOffset + keyOffset);
+ }
+
+ return new IndexPlainRowImpl(keys, idxRowHnd);
+ }
}
/** */
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIndexScan.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIndexScan.java
index c63a59c28f7..b5a5a7e695d 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIndexScan.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/rel/AbstractIndexScan.java
@@ -30,10 +30,13 @@ import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.sql.SqlExplainLevel;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.ignite.internal.processors.query.calcite.externalize.RelInputEx;
import org.apache.ignite.internal.processors.query.calcite.metadata.cost.IgniteCost;
import org.apache.ignite.internal.processors.query.calcite.prepare.bounds.SearchBounds;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteIndex;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.jetbrains.annotations.Nullable;
@@ -82,6 +85,9 @@ public abstract class AbstractIndexScan extends ProjectableFilterableTableScan {
pw = super.explainTerms0(pw);
pw = pw.itemIf("searchBounds", searchBounds, searchBounds != null);
+ if (pw.getDetailLevel() == SqlExplainLevel.ALL_ATTRIBUTES)
+ pw = pw.item("inlineScan", isInlineScan());
+
return pw;
}
@@ -92,14 +98,31 @@ public abstract class AbstractIndexScan extends ProjectableFilterableTableScan {
return idxName;
}
+ /** */
+ public boolean isInlineScan() {
+ IgniteTable tbl = table.unwrap(IgniteTable.class);
+ IgniteIndex idx = tbl.getIndex(idxName);
+
+ if (idx != null)
+ return idx.isInlineScanPossible(requiredColumns);
+
+ return false;
+ }
+
/** {@inheritDoc} */
@Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
double rows = table.getRowCount();
double cost;
+ IgniteTable tbl = table.unwrap(IgniteTable.class);
+ IgniteIndex idx = tbl.getIndex(idxName);
+
+ double inlineReward = (idx != null && isInlineScan()) ?
+ (0.5d + 0.5d * idx.collation().getFieldCollations().size() / table.getRowType().getFieldCount()) : 1d;
+
if (condition == null)
- cost = rows * IgniteCost.ROW_PASS_THROUGH_COST;
+ cost = rows * IgniteCost.ROW_PASS_THROUGH_COST * inlineReward;
else {
RexBuilder builder = getCluster().getRexBuilder();
@@ -119,7 +142,7 @@ public abstract class AbstractIndexScan extends ProjectableFilterableTableScan {
if (rows <= 0)
rows = 1;
- cost += rows * (IgniteCost.ROW_COMPARISON_COST + IgniteCost.ROW_PASS_THROUGH_COST);
+ cost += rows * (IgniteCost.ROW_COMPARISON_COST + IgniteCost.ROW_PASS_THROUGH_COST * inlineReward);
}
// additional tiny cost for preventing equality with table scan.
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java
index 368e4cdfd6b..d096f2ae89b 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/CacheIndexImpl.java
@@ -16,6 +16,7 @@
*/
package org.apache.ignite.internal.processors.query.calcite.schema;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -27,12 +28,18 @@ import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.ImmutableIntList;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.internal.cache.query.index.Index;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyDefinition;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyTypeSettings;
import org.apache.ignite.internal.cache.query.index.sorted.inline.IndexQueryContext;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexImpl;
+import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyTypeRegistry;
import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.exec.IndexFirstLastScan;
import org.apache.ignite.internal.processors.query.calcite.exec.IndexScan;
@@ -190,4 +197,30 @@ public class CacheIndexImpl implements IgniteIndex {
// Empty index find predicate.
return null;
}
+
+ /** {@inheritDoc} */
+ @Override public boolean isInlineScanPossible(@Nullable ImmutableBitSet requiredColumns) {
+ if (idx == null)
+ return false;
+
+ if (requiredColumns == null)
+ requiredColumns = ImmutableBitSet.range(tbl.descriptor().columnDescriptors().size());
+
+ ImmutableIntList idxKeys = collation.getKeys();
+
+ // All indexed keys should be inlined, all required colummns should be inlined.
+ if (idxKeys.size() < requiredColumns.cardinality() || !ImmutableBitSet.of(idxKeys).contains(requiredColumns))
+ return false;
+
+ List<IndexKeyDefinition> keyDefs = new ArrayList<>(idx.unwrap(InlineIndex.class).indexDefinition()
+ .indexKeyDefinitions().values());
+
+ for (InlineIndexKeyType keyType : InlineIndexKeyTypeRegistry.types(keyDefs, new IndexKeyTypeSettings())) {
+ // Skip variable length keys and java objects (see comments about these limitations in IndexScan class).
+ if (keyType.keySize() < 0 || keyType.type() == IndexKeyType.JAVA_OBJECT)
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
index 83e76f2d346..3f691945936 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/IgniteIndex.java
@@ -110,4 +110,9 @@ public interface IgniteIndex {
ColocationGroup grp,
@Nullable ImmutableBitSet requiredColumns
);
+
+ /**
+ * If its possible to scan requred columns using inlined index keys.
+ */
+ public boolean isInlineScanPossible(@Nullable ImmutableBitSet requiredColumns);
}
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java
index d511aa47e16..ccf6674516d 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/schema/SystemViewIndexImpl.java
@@ -125,4 +125,9 @@ public class SystemViewIndexImpl implements IgniteIndex {
return RexUtils.buildHashSearchBounds(cluster, cond, rowType, requiredColumns, true);
}
+
+ /** {@inheritDoc} */
+ @Override public boolean isInlineScanPossible(@Nullable ImmutableBitSet requiredColumns) {
+ return false;
+ }
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java
index 59b16875e4c..78baf7f068d 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/exec/LogicalRelImplementorTest.java
@@ -133,7 +133,7 @@ public class LogicalRelImplementorTest extends GridCommonAbstractTest {
null
) {
@Override public ColocationGroup group(long srcId) {
- return ColocationGroup.forNodes(Collections.singletonList(nodeId));
+ return ColocationGroup.forNodes(Collections.emptyList());
}
};
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java
index 18d390108eb..3cdc981943e 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java
@@ -267,6 +267,11 @@ public class AbstractBasicIntegrationTest extends GridCommonAbstractTest {
) {
return delegate.firstOrLast(first, ectx, grp, requiredColumns);
}
+
+ /** {@inheritDoc} */
+ @Override public boolean isInlineScanPossible(@Nullable ImmutableBitSet requiredColumns) {
+ return delegate.isInlineScanPossible(requiredColumns);
+ }
}
/** */
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java
index e4c03e9f422..ddd2e320b47 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IndexScanlIntegrationTest.java
@@ -17,20 +17,33 @@
package org.apache.ignite.internal.processors.query.calcite.integration;
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
+import java.util.function.IntFunction;
import java.util.function.Predicate;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.QueryEntity;
import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.processors.query.calcite.QueryChecker;
import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
+import org.apache.ignite.internal.processors.query.calcite.exec.IndexScan;
import org.apache.ignite.internal.processors.query.calcite.exec.exp.RangeIterable;
import org.apache.ignite.internal.processors.query.calcite.metadata.ColocationGroup;
import org.apache.ignite.internal.processors.query.calcite.schema.IgniteIndex;
import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
+import org.apache.ignite.internal.processors.query.schema.management.SchemaManager;
import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.G;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
@@ -38,6 +51,9 @@ import org.junit.Test;
* Index scan test.
*/
public class IndexScanlIntegrationTest extends AbstractBasicIntegrationTest {
+ /** */
+ private static final int ROWS_CNT = 100;
+
/** {@inheritDoc} */
@Override protected int nodeCount() {
return 1;
@@ -50,11 +66,7 @@ public class IndexScanlIntegrationTest extends AbstractBasicIntegrationTest {
executeSql("INSERT INTO t VALUES (0, null), (1, null), (2, 2), (3, null), (4, null), (null, 5)");
executeSql("CREATE INDEX t_idx ON t(i1)");
- IgniteTable tbl = (IgniteTable)queryProcessor(grid(0)).schemaHolder().schema("PUBLIC").getTable("T");
-
- RowCountingIndex idx = new RowCountingIndex(tbl.getIndex("T_IDX"));
-
- tbl.addIndex(idx);
+ RowCountingIndex idx = injectRowCountingIndex(grid(0), "T", "T_IDX");
String sql = "SELECT /*+ DISABLE_RULE('NestedLoopJoinConverter', 'MergeJoinConverter') */ t1.i1, t2.i1 " +
"FROM t t1 " +
@@ -83,11 +95,7 @@ public class IndexScanlIntegrationTest extends AbstractBasicIntegrationTest {
executeSql("INSERT INTO t VALUES (null, 0), (1, null), (2, 2), (3, null)");
executeSql("CREATE INDEX t_idx ON t(i1, i2)");
- IgniteTable tbl = (IgniteTable)queryProcessor(grid(0)).schemaHolder().schema("PUBLIC").getTable("T");
-
- RowCountingIndex idx = new RowCountingIndex(tbl.getIndex("T_IDX"));
-
- tbl.addIndex(idx);
+ RowCountingIndex idx = injectRowCountingIndex(grid(0), "T", "T_IDX");
assertQuery("SELECT * FROM t WHERE i1 = ?")
.withParams(new Object[] { null })
@@ -240,11 +248,121 @@ public class IndexScanlIntegrationTest extends AbstractBasicIntegrationTest {
.check();
}
+ /** */
+ @Test
+ public void testInlineScan() {
+ // Single column scans.
+ checkSingleColumnInlineScan(true, "INTEGER", i -> i);
+ checkSingleColumnInlineScan(true, "DOUBLE", i -> (double)i);
+ checkSingleColumnInlineScan(true, "UUID", i -> new UUID(0, i));
+ checkSingleColumnInlineScan(true, "TIMESTAMP",
+ i -> new Timestamp(Timestamp.valueOf("2022-01-01 00:00:00").getTime() + TimeUnit.SECONDS.toMillis(i)));
+ checkSingleColumnInlineScan(true, "DATE",
+ i -> new Date(Date.valueOf("2022-01-01").getTime() + TimeUnit.DAYS.toMillis(i)));
+ checkSingleColumnInlineScan(true, "TIME",
+ i -> new Time(Time.valueOf("00:00:00").getTime() + TimeUnit.SECONDS.toMillis(i)));
+ checkSingleColumnInlineScan(false, "VARCHAR", i -> "str" + i);
+ checkSingleColumnInlineScan(false, "DECIMAL", BigDecimal::valueOf);
+
+ // Multi columns scans.
+ executeSql("CREATE TABLE t(id INTEGER PRIMARY KEY, i1 INTEGER, i2 INTEGER, i3 INTEGER)");
+ executeSql("CREATE INDEX t_idx ON t(i1, i3)");
+ RowCountingIndex idx = injectRowCountingIndex(grid(0), "T", "T_IDX");
+
+ for (int i = 0; i < ROWS_CNT; i++)
+ executeSql("INSERT INTO t VALUES (?, ?, ?, ?)", i, i * 2, i * 3, i * 4);
+
+ checkMultiColumnsInlineScan(true, "SELECT i1, i3 FROM t", idx, i -> new Object[] {i * 2, i * 4});
+ checkMultiColumnsInlineScan(false, "SELECT i1, i2 FROM t", idx, i -> new Object[] {i * 2, i * 3});
+ checkMultiColumnsInlineScan(true, "SELECT sum(i1), i3 FROM t GROUP BY i3", idx,
+ i -> new Object[] {(long)i * 2, i * 4});
+ }
+
+ /** */
+ public void checkSingleColumnInlineScan(boolean expInline, String dataType, IntFunction<Object> valFactory) {
+ executeSql("CREATE TABLE t(id INTEGER PRIMARY KEY, val " + dataType + ')');
+
+ try {
+ executeSql("CREATE INDEX t_idx ON t(val)");
+ RowCountingIndex idx = injectRowCountingIndex(grid(0), "T", "T_IDX");
+
+ for (int i = 0; i < ROWS_CNT; i++)
+ executeSql("INSERT INTO t VALUES (?, ?)", i, valFactory.apply(i));
+
+ QueryChecker checker = assertQuery("SELECT val FROM t");
+
+ for (int i = 0; i < ROWS_CNT; i++)
+ checker.returns(valFactory.apply(i));
+
+ if (expInline) {
+ checker.matches(QueryChecker.containsIndexScan("PUBLIC", "T", "T_IDX")).check();
+
+ assertEquals(ROWS_CNT, idx.rowsProcessed());
+ assertTrue(idx.isInlineScan());
+ }
+ else {
+ checker.check();
+
+ assertFalse(idx.isInlineScan());
+ }
+ }
+ finally {
+ executeSql("DROP TABLE t");
+ }
+ }
+
+ /** */
+ public void checkMultiColumnsInlineScan(
+ boolean expInline,
+ String sql,
+ RowCountingIndex idx,
+ IntFunction<Object[]> rowFactory
+ ) {
+ QueryChecker checker = assertQuery(sql);
+
+ for (int i = 0; i < ROWS_CNT; i++)
+ checker.returns(rowFactory.apply(i));
+
+ if (expInline) {
+ checker.matches(QueryChecker.containsIndexScan("PUBLIC", "T", "T_IDX")).check();
+
+ assertEquals(ROWS_CNT, idx.rowsProcessed());
+ assertTrue(idx.isInlineScan());
+ }
+ else {
+ checker.check();
+
+ assertFalse(idx.isInlineScan());
+ }
+ }
+
+ /** */
+ private RowCountingIndex injectRowCountingIndex(IgniteEx node, String tableName, String idxName) {
+ RowCountingIndex idx = null;
+
+ for (Ignite ignite : G.allGrids()) {
+ IgniteTable tbl = (IgniteTable)queryProcessor((IgniteEx)ignite).schemaHolder().schema("PUBLIC").getTable(tableName);
+
+ if (ignite == node) {
+ idx = new RowCountingIndex(tbl.getIndex(idxName));
+
+ tbl.addIndex(idx);
+ }
+
+ tbl.removeIndex(SchemaManager.generateProxyIdxName(idxName));
+ }
+
+ return idx;
+ }
+
/** */
private static class RowCountingIndex extends DelegatingIgniteIndex {
/** */
private final AtomicInteger filteredRows = new AtomicInteger();
+ /** */
+ private final AtomicBoolean isInlineScan = new AtomicBoolean();
+
/** */
public RowCountingIndex(IgniteIndex delegate) {
super(delegate);
@@ -265,14 +383,24 @@ public class IndexScanlIntegrationTest extends AbstractBasicIntegrationTest {
return true;
};
- filters = filter.and(filters);
+ filters = filters == null ? filter : filter.and(filters);
- return delegate.scan(execCtx, grp, filters, ranges, rowTransformer, requiredColumns);
+ IndexScan<Row> scan = (IndexScan<Row>)delegate.scan(execCtx, grp, filters, ranges, rowTransformer,
+ requiredColumns);
+
+ isInlineScan.set(scan.isInlineScan());
+
+ return scan;
}
/** */
public int rowsProcessed() {
return filteredRows.getAndSet(0);
}
+
+ /** */
+ public boolean isInlineScan() {
+ return isInlineScan.getAndSet(false);
+ }
}
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
index a1ecdecb5c5..64c5858e435 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/AbstractPlannerTest.java
@@ -85,6 +85,7 @@ import org.apache.ignite.internal.processors.query.calcite.schema.ModifyTuple;
import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeSystem;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.plugin.extensions.communication.Message;
import org.apache.ignite.testframework.GridTestUtils;
@@ -753,7 +754,7 @@ public abstract class AbstractPlannerTest extends GridCommonAbstractTest {
/** {@inheritDoc} */
@Override public Collection<ColumnDescriptor> columnDescriptors() {
- throw new AssertionError();
+ return Commons.transform(rowType.getFieldList(), f -> new TestColumnDescriptor(f.getIndex(), f.getName()));
}
/** {@inheritDoc} */
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/InlineIndexScanPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/InlineIndexScanPlannerTest.java
new file mode 100644
index 00000000000..a434da809e4
--- /dev/null
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/InlineIndexScanPlannerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.calcite.planner;
+
+import java.sql.Timestamp;
+import java.util.UUID;
+import org.apache.ignite.internal.processors.query.calcite.rel.AbstractIndexScan;
+import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema;
+import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
+import org.junit.Test;
+
+/**
+ * Planner test for index inline scan.
+ */
+public class InlineIndexScanPlannerTest extends AbstractPlannerTest {
+ /** */
+ @Test
+ public void testInlinScan() throws Exception {
+ TestTable tbl = createTable("TBL", 100, IgniteDistributions.single(),
+ "I0", UUID.class,
+ "I1", Integer.class,
+ "I2", Long.class,
+ "I3", String.class,
+ "I4", Timestamp.class,
+ "I5", Byte.class,
+ "I6", Object.class
+ );
+
+ tbl.addIndex("IDX1", 0, 2, 4);
+ tbl.addIndex("IDX2", 2, 0);
+ tbl.addIndex("IDX3", 5, 3);
+ tbl.addIndex("IDX4", 5, 6);
+
+ IgniteSchema publicSchema = createSchema(tbl);
+
+ // Index IDX1 the only possible index with inlined I4 key.
+ assertPlan("SELECT I4 FROM TBL", publicSchema, isIndexScan("TBL", "IDX1")
+ .and(AbstractIndexScan::isInlineScan));
+
+ // Index IDX2 has less keys count than IDX1.
+ assertPlan("SELECT I2 FROM TBL", publicSchema, isIndexScan("TBL", "IDX2")
+ .and(AbstractIndexScan::isInlineScan));
+
+ // But if we have filter on the first key than IDX1 is prefered.
+ assertPlan("SELECT I2 FROM TBL WHERE I0 = ?", publicSchema, isIndexScan("TBL", "IDX1")
+ .and(AbstractIndexScan::isInlineScan));
+
+ // Index IDX1 is prefered, but inline scan can't be used, since I1 is not inlined.
+ assertPlan("SELECT I2 FROM TBL WHERE I0 = ? AND I1 = ?", publicSchema, isIndexScan("TBL", "IDX1")
+ .and(i -> !i.isInlineScan()));
+
+ // Don't use variable length types for inline scans.
+ assertPlan("SELECT I3 FROM TBL", publicSchema, isTableScan("TBL"));
+
+ // Don't use objects for inline scans.
+ assertPlan("SELECT I6 FROM TBL", publicSchema, isTableScan("TBL"));
+
+ // Don't use any indexes that contain variable length types or objects for inline scans.
+ assertPlan("SELECT I5 FROM TBL", publicSchema, isTableScan("TBL"));
+ }
+}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/MergeJoinPlannerTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/MergeJoinPlannerTest.java
index 1bc501afcc7..5cc79b4ef05 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/MergeJoinPlannerTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/MergeJoinPlannerTest.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.core.Join;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteRel;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteSort;
import org.apache.ignite.internal.processors.query.calcite.rel.IgniteTableScan;
+import org.apache.ignite.internal.processors.query.calcite.rel.ProjectableFilterableTableScan;
import org.apache.ignite.internal.processors.query.calcite.schema.IgniteSchema;
import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistributions;
import org.junit.Test;
@@ -2772,7 +2773,7 @@ public class MergeJoinPlannerTest extends AbstractPlannerTest {
*/
private IgniteSort sortOnTopOfScan(IgniteRel root, String tableName) {
List<IgniteSort> sortNodes = findNodes(root, byClass(IgniteSort.class)
- .and(node -> node.getInputs().size() == 1 && node.getInput(0) instanceof IgniteTableScan
+ .and(node -> node.getInputs().size() == 1 && node.getInput(0) instanceof ProjectableFilterableTableScan
&& node.getInput(0).getTable().unwrap(TestTable.class).name().equals(tableName)));
if (sortNodes.size() > 1)
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/TestTable.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/TestTable.java
index 26fd26d26de..b07711417bf 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/TestTable.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/planner/TestTable.java
@@ -17,9 +17,11 @@
package org.apache.ignite.internal.processors.query.calcite.planner;
+import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -30,8 +32,10 @@ import org.apache.calcite.config.CalciteConnectionConfig;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeImpl;
import org.apache.calcite.rel.type.RelProtoDataType;
import org.apache.calcite.rex.RexNode;
@@ -40,6 +44,15 @@ import org.apache.calcite.schema.Statistic;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.ignite.internal.cache.query.index.IndexDefinition;
+import org.apache.ignite.internal.cache.query.index.IndexName;
+import org.apache.ignite.internal.cache.query.index.Order;
+import org.apache.ignite.internal.cache.query.index.SortOrder;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyDefinition;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.client.ClientIndexDefinition;
+import org.apache.ignite.internal.cache.query.index.sorted.client.ClientInlineIndex;
+import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.processors.query.calcite.exec.ExecutionContext;
import org.apache.ignite.internal.processors.query.calcite.metadata.ColocationGroup;
import org.apache.ignite.internal.processors.query.calcite.prepare.MappingQueryContext;
@@ -51,9 +64,12 @@ import org.apache.ignite.internal.processors.query.calcite.schema.IgniteIndex;
import org.apache.ignite.internal.processors.query.calcite.schema.IgniteStatisticsImpl;
import org.apache.ignite.internal.processors.query.calcite.trait.IgniteDistribution;
import org.apache.ignite.internal.processors.query.calcite.trait.TraitUtils;
+import org.apache.ignite.internal.processors.query.calcite.util.Commons;
import org.apache.ignite.internal.processors.query.stat.ObjectStatisticsImpl;
import org.jetbrains.annotations.Nullable;
+import static org.apache.ignite.internal.processors.query.calcite.planner.AbstractPlannerTest.DEFAULT_SCHEMA;
+
/** */
public class TestTable implements IgniteCacheTable {
/** */
@@ -210,7 +226,33 @@ public class TestTable implements IgniteCacheTable {
/** */
public TestTable addIndex(RelCollation collation, String name) {
- indexes.put(name, new CacheIndexImpl(collation, name, null, this));
+ LinkedHashMap<String, IndexKeyDefinition> keyDefs = new LinkedHashMap<>();
+
+ RelDataType rowType = protoType.apply(Commons.typeFactory());
+
+ for (RelFieldCollation fc : collation.getFieldCollations()) {
+ RelDataTypeField field = rowType.getFieldList().get(fc.getFieldIndex());
+
+ Type fieldType = Commons.typeFactory().getResultClass(field.getType());
+
+ // For some reason IndexKeyType.forClass throw an exception for char classes, but we have such
+ // classes in tests.
+ IndexKeyType keyType = (fieldType == Character.class || fieldType == char.class) ? IndexKeyType.STRING_FIXED :
+ fieldType instanceof Class ? IndexKeyType.forClass((Class<?>)fieldType) : IndexKeyType.UNKNOWN;
+
+ Order order = new Order(fc.direction.isDescending() ? SortOrder.DESC : SortOrder.ASC, null);
+
+ keyDefs.put(field.getName(), new IndexKeyDefinition(keyType.code(), order, -1));
+ }
+
+ IndexDefinition idxDef = new ClientIndexDefinition(
+ new IndexName(QueryUtils.createTableCacheName(DEFAULT_SCHEMA, this.name), DEFAULT_SCHEMA, this.name, name),
+ keyDefs,
+ -1,
+ -1
+ );
+
+ indexes.put(name, new CacheIndexImpl(collation, name, new ClientInlineIndex(idxDef, -1), this));
return this;
}
diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java
index bb92571bd83..fb42d1457eb 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/PlannerTestSuite.java
@@ -25,6 +25,7 @@ import org.apache.ignite.internal.processors.query.calcite.planner.HashAggregate
import org.apache.ignite.internal.processors.query.calcite.planner.HashIndexSpoolPlannerTest;
import org.apache.ignite.internal.processors.query.calcite.planner.IndexRebuildPlannerTest;
import org.apache.ignite.internal.processors.query.calcite.planner.IndexSearchBoundsPlannerTest;
+import org.apache.ignite.internal.processors.query.calcite.planner.InlineIndexScanPlannerTest;
import org.apache.ignite.internal.processors.query.calcite.planner.JoinColocationPlannerTest;
import org.apache.ignite.internal.processors.query.calcite.planner.JoinCommutePlannerTest;
import org.apache.ignite.internal.processors.query.calcite.planner.JoinWithUsingPlannerTest;
@@ -73,6 +74,7 @@ import org.junit.runners.Suite;
IndexRebuildPlannerTest.class,
PlannerTimeoutTest.class,
IndexSearchBoundsPlannerTest.class,
+ InlineIndexScanPlannerTest.class,
})
public class PlannerTestSuite {
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexQueryCriteriaClosure.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexQueryCriteriaClosure.java
index 3bd5a7ac0e4..690b0884a09 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexQueryCriteriaClosure.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexQueryCriteriaClosure.java
@@ -99,7 +99,8 @@ class IndexQueryCriteriaClosure implements BPlusTree.TreeRowClosure<IndexRow, In
if (inVals != null) {
IndexKey key = null;
- if (keyType != null && keyType.type() != JAVA_OBJECT && keyType.inlinedFullValue(pageAddr, off + fieldOff))
+ if (keyType != null && keyType.type() != JAVA_OBJECT
+ && keyType.inlinedFullValue(pageAddr, off + fieldOff, maxSize))
key = keyType.get(pageAddr, off + fieldOff, maxSize);
if (key == null) {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexSingleRangeQuery.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexSingleRangeQuery.java
index 448c778f399..a95470ad19b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexSingleRangeQuery.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/IndexSingleRangeQuery.java
@@ -18,9 +18,9 @@
package org.apache.ignite.internal.cache.query.index;
import org.apache.ignite.internal.cache.query.RangeIndexQueryCriterion;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexPlainRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRowComparator;
-import org.apache.ignite.internal.cache.query.index.sorted.IndexSearchRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.jetbrains.annotations.Nullable;
@@ -43,10 +43,10 @@ class IndexSingleRangeQuery {
private boolean upperAllNulls = true;
/** Lower bound to query underlying index. */
- private @Nullable IndexSearchRowImpl lower;
+ private @Nullable IndexPlainRowImpl lower;
/** Upper bound to query underlying index. */
- private @Nullable IndexSearchRowImpl upper;
+ private @Nullable IndexPlainRowImpl upper;
/** */
IndexSingleRangeQuery(int idxRowSize, int critSize) {
@@ -101,17 +101,17 @@ class IndexSingleRangeQuery {
}
/** */
- @Nullable IndexSearchRowImpl lower() {
+ @Nullable IndexPlainRowImpl lower() {
if (lower == null && !lowerAllNulls)
- lower = new IndexSearchRowImpl(lowerBounds, null);
+ lower = new IndexPlainRowImpl(lowerBounds, null);
return lower;
}
/** */
- @Nullable IndexSearchRowImpl upper() {
+ @Nullable IndexPlainRowImpl upper() {
if (upper == null && !upperAllNulls)
- upper = new IndexSearchRowImpl(upperBounds, null);
+ upper = new IndexPlainRowImpl(upperBounds, null);
return upper;
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexSearchRowImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexPlainRowImpl.java
similarity index 77%
rename from modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexSearchRowImpl.java
rename to modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexPlainRowImpl.java
index a800da03652..e647773a708 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexSearchRowImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexPlainRowImpl.java
@@ -22,9 +22,9 @@ import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.util.typedef.internal.S;
/**
- * Represents a search row that used to find a place in a tree.
+ * Represents a plain row (not bounded to cache data row) that used to find a place in a tree.
*/
-public class IndexSearchRowImpl implements IndexRow {
+public class IndexPlainRowImpl implements IndexRow {
/** */
private final IndexKey[] keys;
@@ -32,7 +32,7 @@ public class IndexSearchRowImpl implements IndexRow {
private final InlineIndexRowHandler rowHnd;
/** Constructor. */
- public IndexSearchRowImpl(IndexKey[] idxKeys, InlineIndexRowHandler rowHnd) {
+ public IndexPlainRowImpl(IndexKey[] idxKeys, InlineIndexRowHandler rowHnd) {
keys = idxKeys;
this.rowHnd = rowHnd;
}
@@ -43,18 +43,18 @@ public class IndexSearchRowImpl implements IndexRow {
}
/** {@inheritDoc} */
- @Override public IndexKey[] keys() {
- return keys;
+ @Override public int keysCount() {
+ return keys.length;
}
/** {@inheritDoc} */
@Override public String toString() {
- return S.toString(IndexSearchRowImpl.class, this);
+ return S.toString(IndexPlainRowImpl.class, this);
}
/** {@inheritDoc} */
@Override public long link() {
- assert false : "Should not get link by IndexSearchRowImpl";
+ assert false : "Should not get link by IndexPlainRowImpl";
return 0;
}
@@ -66,13 +66,13 @@ public class IndexSearchRowImpl implements IndexRow {
/** {@inheritDoc} */
@Override public CacheDataRow cacheDataRow() {
- assert false : "Should not cache data row by IndexSearchRowImpl";
+ assert false : "Should not cache data row by IndexPlainRowImpl";
return null;
}
/** {@inheritDoc} */
- @Override public boolean indexSearchRow() {
+ @Override public boolean indexPlainRow() {
return true;
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRow.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRow.java
index ac2e8f99699..d6c2e51f252 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRow.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRow.java
@@ -38,9 +38,9 @@ public interface IndexRow extends MvccVersionAware {
public IndexKey key(int idx);
/**
- * @return Underlying keys.
+ * @return Keys count.
*/
- public IndexKey[] keys();
+ public int keysCount();
/**
* @return Link to a cache row.
@@ -82,5 +82,5 @@ public interface IndexRow extends MvccVersionAware {
/**
* @return {@code True} for rows used for index search (as opposed to rows stored in {@link InlineIndexTree}.
*/
- public boolean indexSearchRow();
+ public boolean indexPlainRow();
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRowImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRowImpl.java
index e8e22688eba..2b7e8f5fec1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRowImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/IndexRowImpl.java
@@ -73,15 +73,8 @@ public class IndexRowImpl implements IndexRow {
}
/** {@inheritDoc} */
- @Override public IndexKey[] keys() {
- int keysCnt = rowHnd.indexKeyDefinitions().size();
-
- IndexKey[] keys = new IndexKey[keysCnt];
-
- for (int i = 0; i < keysCnt; ++i)
- keys[i] = key(i);
-
- return keys;
+ @Override public int keysCount() {
+ return rowHnd.indexKeyDefinitions().size();
}
/** {@inheritDoc} */
@@ -169,7 +162,7 @@ public class IndexRowImpl implements IndexRow {
}
/** {@inheritDoc} */
- @Override public boolean indexSearchRow() {
+ @Override public boolean indexPlainRow() {
return false;
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientIndexFactory.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientIndexFactory.java
index 76b40332d63..6f63445550b 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientIndexFactory.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientIndexFactory.java
@@ -63,6 +63,6 @@ public class ClientIndexFactory implements IndexFactory {
log
);
- return new ClientInlineIndex(def.idxName().idxName(), inlineSize);
+ return new ClientInlineIndex(def, inlineSize);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientInlineIndex.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientInlineIndex.java
index 9ae649ec00c..1f04dcab397 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientInlineIndex.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/client/ClientInlineIndex.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.cache.query.index.sorted.client;
import java.util.UUID;
+import org.apache.ignite.internal.cache.query.index.IndexDefinition;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
import org.apache.ignite.internal.cache.query.index.sorted.inline.IndexQueryContext;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndex;
@@ -31,15 +32,15 @@ public class ClientInlineIndex extends AbstractClientIndex implements InlineInde
/** */
private final int inlineSize;
- /** Index name. */
- private final String name;
-
/** Index id. */
private final UUID id = UUID.randomUUID();
+ /** Index definition. */
+ private final IndexDefinition def;
+
/** */
- public ClientInlineIndex(String idxName, int inlineSize) {
- name = idxName;
+ public ClientInlineIndex(IndexDefinition def, int inlineSize) {
+ this.def = def;
this.inlineSize = inlineSize;
}
@@ -123,6 +124,11 @@ public class ClientInlineIndex extends AbstractClientIndex implements InlineInde
/** {@inheritDoc} */
@Override public String name() {
- return name;
+ return def.idxName().idxName();
+ }
+
+ /** {@inheritDoc} */
+ @Override public IndexDefinition indexDefinition() {
+ return def;
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/IndexQueryContext.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/IndexQueryContext.java
index 2e27c708c4d..234fbc2f9d7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/IndexQueryContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/IndexQueryContext.java
@@ -30,6 +30,9 @@ public class IndexQueryContext {
/** Index rows filter. */
private final BPlusTree.TreeRowClosure<IndexRow, IndexRow> rowFilter;
+ /** Index row factory. */
+ private final BPlusTree.TreeRowFactory<IndexRow, IndexRow> rowFactory;
+
/** */
private final MvccSnapshot mvccSnapshot;
@@ -38,9 +41,20 @@ public class IndexQueryContext {
IndexingQueryFilter cacheFilter,
BPlusTree.TreeRowClosure<IndexRow, IndexRow> rowFilter,
MvccSnapshot mvccSnapshot
+ ) {
+ this(cacheFilter, rowFilter, null, mvccSnapshot);
+ }
+
+ /** */
+ public IndexQueryContext(
+ IndexingQueryFilter cacheFilter,
+ BPlusTree.TreeRowClosure<IndexRow, IndexRow> rowFilter,
+ BPlusTree.TreeRowFactory<IndexRow, IndexRow> rowFactory,
+ MvccSnapshot mvccSnapshot
) {
this.cacheFilter = cacheFilter;
this.rowFilter = rowFilter;
+ this.rowFactory = rowFactory;
this.mvccSnapshot = mvccSnapshot;
}
@@ -64,4 +78,11 @@ public class IndexQueryContext {
public BPlusTree.TreeRowClosure<IndexRow, IndexRow> rowFilter() {
return rowFilter;
}
+
+ /**
+ * @return Index row factory.
+ */
+ public BPlusTree.TreeRowFactory<IndexRow, IndexRow> rowFactory() {
+ return rowFactory;
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndex.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndex.java
index 35a198e1de5..ffff30f57a8 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndex.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndex.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.cache.query.index.sorted.inline;
+import org.apache.ignite.internal.cache.query.index.IndexDefinition;
import org.apache.ignite.internal.cache.query.index.sorted.SortedSegmentedIndex;
/**
@@ -38,4 +39,9 @@ public interface InlineIndex extends SortedSegmentedIndex {
* @return Tree segment for specified number.
*/
public InlineIndexTree segment(int segment);
+
+ /**
+ * @return Index definition.
+ */
+ public IndexDefinition indexDefinition();
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexImpl.java
index 2d65ac03bca..fd374b150d2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexImpl.java
@@ -46,6 +46,7 @@ import org.apache.ignite.internal.metric.IoStatisticsHolderIndex;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.mvcc.MvccSnapshot;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
+import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.util.lang.GridCursor;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.indexing.IndexingQueryCacheFilter;
@@ -103,6 +104,7 @@ public class InlineIndexImpl extends AbstractIndex implements InlineIndex {
IndexQueryContext qryCtx
) throws IgniteCheckedException {
InlineTreeFilterClosure closure = filterClosure(qryCtx);
+ BPlusTree.TreeRowFactory<IndexRow, IndexRow> rowFactory = qryCtx == null ? null : qryCtx.rowFactory();
lock.readLock().lock();
@@ -117,7 +119,7 @@ public class InlineIndexImpl extends AbstractIndex implements InlineIndex {
return new SingleCursor<>(row);
}
- return segments[segment].find(lower, upper, lowIncl, upIncl, closure, null);
+ return segments[segment].find(lower, upper, lowIncl, upIncl, closure, rowFactory, null);
}
finally {
lock.readLock().unlock();
@@ -587,10 +589,8 @@ public class InlineIndexImpl extends AbstractIndex implements InlineIndex {
cctx, def.idxName().cacheName(), def.idxName().tableName(), row.key(), row.value());
}
- /**
- * @return Index definition.
- */
- public SortedIndexDefinition indexDefinition() {
+ /** {@inheritDoc} */
+ @Override public SortedIndexDefinition indexDefinition() {
return def;
}
@@ -615,7 +615,7 @@ public class InlineIndexImpl extends AbstractIndex implements InlineIndex {
@Override public int compare(GridCursor<IndexRow> o1, GridCursor<IndexRow> o2) {
try {
- int keysLen = o1.get().keys().length;
+ int keysLen = o1.get().keysCount();
for (int i = 0; i < keysLen; i++) {
int cmp = rowComparator.compareRow(o1.get(), o2.get(), i);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java
index 47b6ea2707a..18f174138ef 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexKeyType.java
@@ -108,9 +108,8 @@ public interface InlineIndexKeyType {
*
* @param pageAddr Page address.
* @param off Offset.
- * @return {@code true} if inline contains full index key. Can be {@code false} for truncated variable lenght types.
+ * @param maxSize Max size.
+ * @return {@code true} if inline contains full index key. Can be {@code false} for truncated variable length types.
*/
- public default boolean inlinedFullValue(long pageAddr, int off) {
- return true;
- }
+ public boolean inlinedFullValue(long pageAddr, int off, int maxSize);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexTree.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexTree.java
index b2641fa93ed..6117592324a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineIndexTree.java
@@ -625,7 +625,7 @@ public class InlineIndexTree extends BPlusTree<IndexRow, IndexRow> {
* @return Comparison result.
*/
private int mvccCompare(MvccIO io, long pageAddr, int idx, IndexRow row) {
- if (!mvccEnabled || row.indexSearchRow())
+ if (!mvccEnabled || row.indexPlainRow())
return 0;
long crd = io.mvccCoordinatorVersion(pageAddr, idx);
@@ -643,7 +643,7 @@ public class InlineIndexTree extends BPlusTree<IndexRow, IndexRow> {
* @return Comparison result.
*/
private int mvccCompare(IndexRow r1, IndexRow r2) {
- if (!mvccEnabled || r2.indexSearchRow() || r1 == r2)
+ if (!mvccEnabled || r2.indexPlainRow() || r1 == r2)
return 0;
long crdVer1 = r1.mvccCoordinatorVersion();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineRecommender.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineRecommender.java
index 04e2fed1bdb..253a07cc9cf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineRecommender.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/InlineRecommender.java
@@ -66,7 +66,7 @@ public class InlineRecommender {
@SuppressWarnings({"ConditionalBreakInInfiniteLoop", "IfMayBeConditional"})
public void recommend(IndexRow row, int currInlineSize) {
// Do the check only for put operations.
- if (row.indexSearchRow())
+ if (row.indexPlainRow())
return;
long invokeCnt = inlineSizeCalculationCntr.get();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/BytesInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/BytesInlineIndexKeyType.java
index 5ed154b558d..a813afa87e2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/BytesInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/BytesInlineIndexKeyType.java
@@ -89,7 +89,7 @@ public class BytesInlineIndexKeyType extends NullableInlineIndexKeyType<BytesInd
int res = Integer.signum(len1 - len2);
- if (inlinedFullValue(pageAddr, off))
+ if (inlinedFullValue(pageAddr, off, VARTYPE_HEADER_SIZE + 1))
return res;
if (res >= 0)
@@ -143,9 +143,4 @@ public class BytesInlineIndexKeyType extends NullableInlineIndexKeyType<BytesInd
public boolean compareBinaryUnsigned() {
return compareBinaryUnsigned;
}
-
- /** {@inheritDoc} */
- @Override public boolean inlinedFullValue(long pageAddr, int off) {
- return (PageUtils.getShort(pageAddr, off + 1) & 0x8000) == 0;
- }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java
index 030a7b4a6db..f3a86e0f604 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/NullableInlineIndexKeyType.java
@@ -34,6 +34,9 @@ public abstract class NullableInlineIndexKeyType<T extends IndexKey> implements
/** Value for comparison meaning 'Compare not supported for given value'. */
public static final int COMPARE_UNSUPPORTED = Integer.MIN_VALUE;
+ /** Size of header for vartypes inlined values. */
+ public static final int VARTYPE_HEADER_SIZE = 3;
+
/** Type of this key. */
private final IndexKeyType type;
@@ -234,4 +237,20 @@ public abstract class NullableInlineIndexKeyType<T extends IndexKey> implements
/** Return inlined size for specified key. */
protected abstract int inlineSize0(T key);
+
+ /** {@inheritDoc} */
+ @Override public boolean inlinedFullValue(long pageAddr, int off, int maxSize) {
+ if (maxSize < 1)
+ return false;
+
+ int type = PageUtils.getByte(pageAddr, off);
+
+ if (type == IndexKeyType.NULL.code())
+ return true;
+
+ if (keySize > 0) // For fixed length types.
+ return maxSize >= keySize + 1;
+ else // For variable length types.
+ return maxSize > VARTYPE_HEADER_SIZE && (PageUtils.getShort(pageAddr, off + 1) & 0x8000) == 0;
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/ObjectByteArrayInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/ObjectByteArrayInlineIndexKeyType.java
index 81ada03b74c..02f171049e9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/ObjectByteArrayInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/ObjectByteArrayInlineIndexKeyType.java
@@ -69,7 +69,7 @@ public class ObjectByteArrayInlineIndexKeyType extends NullableInlineIndexKeyTyp
}
/** {@inheritDoc} */
- @Override public boolean inlinedFullValue(long pageAddr, int offset) {
- return delegate.inlinedFullValue(pageAddr, offset);
+ @Override public boolean inlinedFullValue(long pageAddr, int offset, int maxSize) {
+ return delegate.inlinedFullValue(pageAddr, offset, maxSize);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java
index b7ffa1df680..593e743d428 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringInlineIndexKeyType.java
@@ -230,7 +230,7 @@ public class StringInlineIndexKeyType extends NullableInlineIndexKeyType<StringI
int res = cntr1 == len1 && cntr2 == len2 ? 0 : cntr1 == len1 ? -1 : 1;
- if (inlinedFullValue(pageAddr, off))
+ if (inlinedFullValue(pageAddr, off, VARTYPE_HEADER_SIZE + 1))
return res;
if (res >= 0)
@@ -264,11 +264,6 @@ public class StringInlineIndexKeyType extends NullableInlineIndexKeyType<StringI
return null;
}
- /** {@inheritDoc} */
- @Override public boolean inlinedFullValue(long pageAddr, int off) {
- return (PageUtils.getShort(pageAddr, off + 1) & 0x8000) == 0;
- }
-
/** {@inheritDoc} */
@Override protected int inlineSize0(StringIndexKey key) {
return ((String)key.key()).getBytes(CHARSET).length + 3;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java
index bcc2ea85f7c..37584b2ea34 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/cache/query/index/sorted/inline/types/StringNoCompareInlineIndexKeyType.java
@@ -55,7 +55,7 @@ public class StringNoCompareInlineIndexKeyType extends NullableInlineIndexKeyTyp
}
/** {@inheritDoc} */
- @Override public boolean inlinedFullValue(long pageAddr, int off) {
- return delegate.inlinedFullValue(pageAddr, off);
+ @Override public boolean inlinedFullValue(long pageAddr, int off, int maxSize) {
+ return delegate.inlinedFullValue(pageAddr, off, maxSize);
}
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
index f65a4bf2769..e4b1bfdb2c2 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java
@@ -1231,12 +1231,19 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param upper Upper bound.
* @param upIncl {@code true} if upper bound is inclusive.
* @param c Filter closure.
+ * @param rowFactory Row factory or (@code null} for default factory.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
* @return Cursor.
* @throws IgniteCheckedException If failed.
*/
- private GridCursor<T> findLowerUnbounded(L upper, boolean upIncl, TreeRowClosure<L, T> c, Object x) throws IgniteCheckedException {
- ForwardCursor cursor = new ForwardCursor(upper, upIncl, c, x);
+ private GridCursor<T> findLowerUnbounded(
+ L upper,
+ boolean upIncl,
+ TreeRowClosure<L, T> c,
+ TreeRowFactory<L, T> rowFactory,
+ Object x
+ ) throws IgniteCheckedException {
+ ForwardCursor cursor = new ForwardCursor(upper, upIncl, c, rowFactory, x);
long firstPageId;
@@ -1299,7 +1306,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @throws IgniteCheckedException If failed.
*/
public GridCursor<T> find(L lower, L upper, TreeRowClosure<L, T> c, Object x) throws IgniteCheckedException {
- return find(lower, upper, true, true, c, x);
+ return find(lower, upper, true, true, c, null, x);
}
/**
@@ -1308,6 +1315,7 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param lowIncl {@code true} if lower bound is inclusive.
* @param upIncl {@code true} if upper bound is inclusive.
* @param c Filter closure.
+ * @param rowFactory Row factory or (@code null} for default factory.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
* @return Cursor.
* @throws IgniteCheckedException If failed.
@@ -1318,15 +1326,16 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
boolean lowIncl,
boolean upIncl,
TreeRowClosure<L, T> c,
+ TreeRowFactory<L, T> rowFactory,
Object x
) throws IgniteCheckedException {
checkDestroyed();
- ForwardCursor cursor = new ForwardCursor(lower, upper, lowIncl, upIncl, c, x);
+ ForwardCursor cursor = new ForwardCursor(lower, upper, lowIncl, upIncl, c, rowFactory, x);
try {
if (lower == null)
- return findLowerUnbounded(upper, upIncl, c, x);
+ return findLowerUnbounded(upper, upIncl, c, rowFactory, x);
cursor.find();
@@ -6114,16 +6123,20 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
/** */
private final TreeRowClosure<L, T> c;
+ /** */
+ private final TreeRowFactory<L, T> rowFactory;
+
/**
* Lower unbound cursor.
*
* @param upperBound Upper bound.
* @param upIncl {@code true} if upper bound is inclusive.
* @param c Filter closure.
+ * @param rowFactory Row factory or (@code null} for default factory.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
*/
- ForwardCursor(L upperBound, boolean upIncl, TreeRowClosure<L, T> c, Object x) {
- this(null, upperBound, true, upIncl, c, x);
+ ForwardCursor(L upperBound, boolean upIncl, TreeRowClosure<L, T> c, TreeRowFactory<L, T> rowFactory, Object x) {
+ this(null, upperBound, true, upIncl, c, rowFactory, x);
}
/**
@@ -6132,12 +6145,22 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
* @param lowIncl {@code true} if lower bound is inclusive.
* @param upIncl {@code true} if upper bound is inclusive.
* @param c Filter closure.
+ * @param rowFactory Row factory or (@code null} for default factory.
* @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
*/
- ForwardCursor(L lowerBound, L upperBound, boolean lowIncl, boolean upIncl, TreeRowClosure<L, T> c, Object x) {
+ ForwardCursor(
+ L lowerBound,
+ L upperBound,
+ boolean lowIncl,
+ boolean upIncl,
+ TreeRowClosure<L, T> c,
+ TreeRowFactory<L, T> rowFactory,
+ Object x
+ ) {
super(lowerBound, upperBound, lowIncl, upIncl);
this.c = c;
+ this.rowFactory = rowFactory;
this.x = x;
}
@@ -6164,8 +6187,10 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
int resCnt = 0;
for (int idx = startIdx; idx < cnt; idx++) {
- if (c == null || c.apply(BPlusTree.this, io, pageAddr, idx))
- rows = GridArrays.set(rows, resCnt++, getRow(io, pageAddr, idx, x));
+ if (c == null || c.apply(BPlusTree.this, io, pageAddr, idx)) {
+ rows = GridArrays.set(rows, resCnt++, rowFactory == null ? getRow(io, pageAddr, idx, x) :
+ rowFactory.create(BPlusTree.this, io, pageAddr, idx));
+ }
}
if (resCnt == 0) {
@@ -6372,6 +6397,24 @@ public abstract class BPlusTree<L, T extends L> extends DataStructure implements
throws IgniteCheckedException;
}
+ /**
+ * Row factory from page memory.
+ */
+ public interface TreeRowFactory<L, T extends L> {
+ /**
+ * Creates row.
+ *
+ * @param tree The tree.
+ * @param io The tree IO object.
+ * @param pageAddr The page address.
+ * @param idx The item index.
+ * @return Created index row.
+ * @throws IgniteCheckedException If failed.
+ */
+ public T create(BPlusTree<L, T> tree, BPlusIO<L> io, long pageAddr, int idx)
+ throws IgniteCheckedException;
+ }
+
/**
* A generic visitor-style interface for performing inspection/modification operations on the tree.
*/
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 6e2c4945b18..f49acd2c96c 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
@@ -31,9 +31,9 @@ import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.GridTopic;
import org.apache.ignite.internal.cache.query.index.Index;
import org.apache.ignite.internal.cache.query.index.sorted.IndexKeyType;
+import org.apache.ignite.internal.cache.query.index.sorted.IndexPlainRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRow;
import org.apache.ignite.internal.cache.query.index.sorted.IndexRowImpl;
-import org.apache.ignite.internal.cache.query.index.sorted.IndexSearchRowImpl;
import org.apache.ignite.internal.cache.query.index.sorted.IndexValueCursor;
import org.apache.ignite.internal.cache.query.index.sorted.InlineIndexRowHandler;
import org.apache.ignite.internal.cache.query.index.sorted.SortedIndexDefinition;
@@ -315,7 +315,7 @@ public class H2TreeIndex extends H2TreeIndexBase {
v.getObject(), v.getType(), cctx.cacheObjectContext(), queryIndex.keyTypeSettings());
}
- return new IndexSearchRowImpl(keys, rowHnd);
+ return new IndexPlainRowImpl(keys, rowHnd);
}
/** */