diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java
index afaf712a2d59..0c1b91dd3f0a 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java
@@ -47,8 +47,8 @@ public class BinaryTupleCommon {
public static final int PREFIX_FLAG = 1 << 3;
/**
- * Flag, which indicates how to interpret situations when Binary Tuple Prefix columns are equal to
- * first N columns of a Binary Tuple (where N is the length of the prefix).
+ * Flag, which indicates how to interpret situations when Binary Tuple Prefix columns are equal to first N columns of a Binary Tuple
+ * (where N is the length of the prefix).
*
*
This flag is used by some index implementations for internal optimizations.
*/
@@ -133,4 +133,26 @@ public static int nullOffset(int index) {
public static byte nullMask(int index) {
return (byte) (1 << (index % 8));
}
+
+ /**
+ * Calculates the size of entry in variable-length offset table.
+ *
+ * @param size Variable-length area size.
+ * @return Size in bytes.
+ */
+ public static int valueSizeToEntrySize(long size) {
+ if (size <= 0xff) {
+ return 1;
+ }
+
+ if (size <= 0xffff) {
+ return 2;
+ }
+
+ if (size <= Integer.MAX_VALUE) {
+ return 4;
+ }
+
+ throw new IgniteInternalException("Too big binary tuple size");
+ }
}
diff --git a/modules/binary-tuple/src/test/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommonTest.java b/modules/binary-tuple/src/test/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommonTest.java
new file mode 100644
index 000000000000..d618e44995d8
--- /dev/null
+++ b/modules/binary-tuple/src/test/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommonTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.binarytuple;
+
+import static org.apache.ignite.internal.binarytuple.BinaryTupleCommon.valueSizeToEntrySize;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.ignite.lang.IgniteInternalException;
+import org.junit.jupiter.api.Test;
+
+/**
+ * For {@link BinaryTupleCommon} testing.
+ */
+public class BinaryTupleCommonTest {
+ @Test
+ void testTableOffsetEntrySize() {
+ assertEquals(1, valueSizeToEntrySize(10));
+ assertEquals(1, valueSizeToEntrySize(Byte.MAX_VALUE));
+ assertEquals(1, valueSizeToEntrySize(0xff));
+
+ assertEquals(2, valueSizeToEntrySize(0x1ff));
+ assertEquals(2, valueSizeToEntrySize(Short.MAX_VALUE));
+ assertEquals(2, valueSizeToEntrySize(0xffff));
+
+ assertEquals(4, valueSizeToEntrySize(0x1ffff));
+ assertEquals(4, valueSizeToEntrySize(Integer.MAX_VALUE));
+
+ assertThrows(IgniteInternalException.class, () -> valueSizeToEntrySize(0xffffffffL));
+ }
+}
diff --git a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusInnerIo.java b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusInnerIo.java
index 330e36a3d404..0a1cbe5519b2 100644
--- a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusInnerIo.java
+++ b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusInnerIo.java
@@ -41,6 +41,9 @@ public abstract class BplusInnerIo extends BplusIo {
/** Offset of the link. */
private static final int SHIFT_LINK = SHIFT_LEFT + PARTITIONLESS_LINK_SIZE_BYTES;
+ /** Number of bytes a child link takes in storage. */
+ public static final int CHILD_LINK_SIZE = PARTITIONLESS_LINK_SIZE_BYTES;
+
/** Offset of the right page ID of the item. */
private final int shiftRight = SHIFT_LINK + itemSize;
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypes.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypes.java
index 6308687c1089..fff7ad5e5b4b 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypes.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/NativeTypes.java
@@ -101,7 +101,7 @@ public static NativeType numberOf(int precision) {
/**
* Creates a STRING type with maximal length is len
.
*
- * @param len Maximum length of the string.
+ * @param len Maximum length of the string, {@link Integer#MAX_VALUE} if not defined.
* @return Native type.
*/
public static NativeType stringOf(int len) {
@@ -111,7 +111,7 @@ public static NativeType stringOf(int len) {
/**
* Creates a BYTES type with maximal length is len
.
*
- * @param len Maximum length of the byte array.
+ * @param len Maximum length of the byte array, {@link Integer#MAX_VALUE} if not defined.
* @return Native type.
*/
public static NativeType blobOf(int len) {
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/VarlenNativeType.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/VarlenNativeType.java
index 89613edb850e..692083528052 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/VarlenNativeType.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/VarlenNativeType.java
@@ -23,14 +23,14 @@
* Variable-length native type.
*/
public class VarlenNativeType extends NativeType {
- /** Length of the type. */
+ /** Length of the type, {@link Integer#MAX_VALUE} if not defined. */
private final int len;
/**
* Constructor.
*
* @param typeSpec Type spec.
- * @param len Type length.
+ * @param len Type length.
*/
protected VarlenNativeType(NativeTypeSpec typeSpec, int len) {
super(typeSpec);
@@ -38,20 +38,18 @@ protected VarlenNativeType(NativeTypeSpec typeSpec, int len) {
this.len = len;
}
- /** {@inheritDoc} */
@Override
public boolean mismatch(NativeType type) {
return super.mismatch(type) || len < ((VarlenNativeType) type).len;
}
/**
- * Get length of the type.
+ * Get length of the type, {@link Integer#MAX_VALUE} if not defined.
*/
public int length() {
return len;
}
- /** {@inheritDoc} */
@Override
public String toString() {
return S.toString(VarlenNativeType.class.getSimpleName(), "name", spec(), "len", len);
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/BinaryTupleComparator.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/BinaryTupleComparator.java
index f86b3d678e9d..95e2a419e725 100644
--- a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/BinaryTupleComparator.java
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/BinaryTupleComparator.java
@@ -29,7 +29,7 @@
import org.apache.ignite.internal.schema.BinaryTupleSchema;
import org.apache.ignite.internal.schema.NativeTypeSpec;
import org.apache.ignite.internal.schema.row.InternalTuple;
-import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.ColumnDescriptor;
+import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.SortedIndexColumnDescriptor;
/**
* Comparator implementation for comparing {@link BinaryTuple}s on a per-column basis.
@@ -63,10 +63,10 @@ public int compare(ByteBuffer buffer1, ByteBuffer buffer2) {
int columnsToCompare = Math.min(tuple1.count(), tuple2.count());
- assert columnsToCompare <= descriptor.indexColumns().size();
+ assert columnsToCompare <= descriptor.columns().size();
for (int i = 0; i < columnsToCompare; i++) {
- ColumnDescriptor columnDescriptor = descriptor.indexColumns().get(i);
+ SortedIndexColumnDescriptor columnDescriptor = descriptor.columns().get(i);
int compare = compareField(tuple1, tuple2, i);
@@ -102,7 +102,7 @@ private int compareField(InternalTuple tuple1, InternalTuple tuple2, int index)
return 1;
}
- ColumnDescriptor columnDescriptor = descriptor.indexColumns().get(index);
+ SortedIndexColumnDescriptor columnDescriptor = descriptor.columns().get(index);
NativeTypeSpec typeSpec = columnDescriptor.type().spec();
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/HashIndexDescriptor.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/HashIndexDescriptor.java
index e3740ccdfd3d..b546f4f51357 100644
--- a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/HashIndexDescriptor.java
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/HashIndexDescriptor.java
@@ -38,40 +38,34 @@
*
* @see HashIndexStorage
*/
-public class HashIndexDescriptor {
+public class HashIndexDescriptor implements IndexDescriptor {
/**
* Descriptor of a Hash Index column.
*/
- public static class ColumnDescriptor {
+ public static class HashIndexColumnDescriptor implements ColumnDescriptor {
private final String name;
private final NativeType type;
private final boolean nullable;
- ColumnDescriptor(ColumnView tableColumnView) {
+ HashIndexColumnDescriptor(ColumnView tableColumnView) {
this.name = tableColumnView.name();
this.type = ConfigurationToSchemaDescriptorConverter.convert(tableColumnView.type());
this.nullable = tableColumnView.nullable();
}
- /**
- * Returns the name of an index column.
- */
+ @Override
public String name() {
return name;
}
- /**
- * Returns a column type.
- */
+ @Override
public NativeType type() {
return type;
}
- /**
- * Returns {@code true} if this column can contain null values or {@code false} otherwise.
- */
+ @Override
public boolean nullable() {
return nullable;
}
@@ -84,7 +78,7 @@ public String toString() {
private final UUID id;
- private final List columns;
+ private final List columns;
/**
* Creates an Index Descriptor from a given Table Configuration.
@@ -122,22 +116,18 @@ public HashIndexDescriptor(UUID indexId, TablesView tablesConfig) {
assert columnView != null : "Incorrect index column configuration. " + columnName + " column does not exist";
- return new ColumnDescriptor(columnView);
+ return new HashIndexColumnDescriptor(columnView);
})
.collect(toUnmodifiableList());
}
- /**
- * Returns the ID of this Index.
- */
+ @Override
public UUID id() {
return id;
}
- /**
- * Returns the Column Descriptors that comprise a row of this index.
- */
- public List indexColumns() {
+ @Override
+ public List columns() {
return columns;
}
}
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/IndexDescriptor.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/IndexDescriptor.java
new file mode 100644
index 000000000000..5dd2fb94657f
--- /dev/null
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/IndexDescriptor.java
@@ -0,0 +1,57 @@
+/*
+ * 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.storage.index;
+
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.schema.NativeType;
+
+/**
+ * Index descriptor.
+ */
+public interface IndexDescriptor {
+ /**
+ * Index column descriptor.
+ */
+ interface ColumnDescriptor {
+ /**
+ * Returns the name of an index column.
+ */
+ String name();
+
+ /**
+ * Returns a column type.
+ */
+ NativeType type();
+
+ /**
+ * Returns {@code true} if this column can contain null values or {@code false} otherwise.
+ */
+ boolean nullable();
+ }
+
+ /**
+ * Returns the index ID.
+ */
+ UUID id();
+
+ /**
+ * Returns index column descriptions.
+ */
+ List extends ColumnDescriptor> columns();
+}
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/SortedIndexDescriptor.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/SortedIndexDescriptor.java
index a278f114de22..da5faa8fadaf 100644
--- a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/SortedIndexDescriptor.java
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/index/SortedIndexDescriptor.java
@@ -41,11 +41,11 @@
*
* @see SortedIndexStorage
*/
-public class SortedIndexDescriptor {
+public class SortedIndexDescriptor implements IndexDescriptor {
/**
* Descriptor of a Sorted Index column (column name and column sort order).
*/
- public static class ColumnDescriptor {
+ public static class SortedIndexColumnDescriptor implements ColumnDescriptor {
private final String name;
private final NativeType type;
@@ -62,7 +62,7 @@ public static class ColumnDescriptor {
* @param nullable Flag indicating that the column may contain {@code null}s.
* @param asc Sort order of the column.
*/
- public ColumnDescriptor(String name, NativeType type, boolean nullable, boolean asc) {
+ public SortedIndexColumnDescriptor(String name, NativeType type, boolean nullable, boolean asc) {
this.name = name;
this.type = type;
this.nullable = nullable;
@@ -75,30 +75,24 @@ public ColumnDescriptor(String name, NativeType type, boolean nullable, boolean
* @param tableColumnView Table column configuration.
* @param indexColumnView Index column configuration.
*/
- public ColumnDescriptor(ColumnView tableColumnView, IndexColumnView indexColumnView) {
+ public SortedIndexColumnDescriptor(ColumnView tableColumnView, IndexColumnView indexColumnView) {
this.name = tableColumnView.name();
this.type = ConfigurationToSchemaDescriptorConverter.convert(tableColumnView.type());
this.nullable = tableColumnView.nullable();
this.asc = indexColumnView.asc();
}
- /**
- * Returns the name of an index column.
- */
+ @Override
public String name() {
return name;
}
- /**
- * Returns a column descriptor.
- */
+ @Override
public NativeType type() {
return type;
}
- /**
- * Returns {@code true} if this column can contain null values or {@code false} otherwise.
- */
+ @Override
public boolean nullable() {
return nullable;
}
@@ -118,7 +112,7 @@ public String toString() {
private final UUID id;
- private final List columns;
+ private final List columns;
private final BinaryTupleSchema binaryTupleSchema;
@@ -138,13 +132,13 @@ public SortedIndexDescriptor(UUID indexId, TablesView tablesConfig) {
* @param indexId Index ID.
* @param columnDescriptors Column descriptors.
*/
- public SortedIndexDescriptor(UUID indexId, List columnDescriptors) {
+ public SortedIndexDescriptor(UUID indexId, List columnDescriptors) {
this.id = indexId;
this.columns = List.copyOf(columnDescriptors);
this.binaryTupleSchema = createSchema(columns);
}
- private static List extractIndexColumnsConfiguration(UUID indexId, TablesView tablesConfig) {
+ private static List extractIndexColumnsConfiguration(UUID indexId, TablesView tablesConfig) {
TableIndexView indexConfig = ConfigurationUtil.getByInternalId(tablesConfig.indexes(), indexId);
if (indexConfig == null) {
@@ -174,12 +168,12 @@ private static List extractIndexColumnsConfiguration(UUID inde
IndexColumnView indexColumnView = indexColumns.get(columnName);
- return new ColumnDescriptor(columnView, indexColumnView);
+ return new SortedIndexColumnDescriptor(columnView, indexColumnView);
})
.collect(toUnmodifiableList());
}
- private static BinaryTupleSchema createSchema(List columns) {
+ private static BinaryTupleSchema createSchema(List columns) {
Element[] elements = columns.stream()
.map(columnDescriptor -> new Element(columnDescriptor.type(), columnDescriptor.nullable()))
.toArray(Element[]::new);
@@ -187,17 +181,13 @@ private static BinaryTupleSchema createSchema(List columns) {
return BinaryTupleSchema.create(elements);
}
- /**
- * Returns this index' ID.
- */
+ @Override
public UUID id() {
return id;
}
- /**
- * Returns the Column Descriptors that comprise a row of this index.
- */
- public List indexColumns() {
+ @Override
+ public List columns() {
return columns;
}
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/index/BinaryTupleComparatorTest.java b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/index/BinaryTupleComparatorTest.java
index 3c929ba1d882..8e614273faf4 100644
--- a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/index/BinaryTupleComparatorTest.java
+++ b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/index/BinaryTupleComparatorTest.java
@@ -37,7 +37,7 @@
import org.apache.ignite.internal.binarytuple.BinaryTuplePrefixBuilder;
import org.apache.ignite.internal.schema.NativeType;
import org.apache.ignite.internal.schema.NativeTypes;
-import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.ColumnDescriptor;
+import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.SortedIndexColumnDescriptor;
import org.apache.ignite.lang.IgniteBiTuple;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -51,7 +51,7 @@ public class BinaryTupleComparatorTest {
@ParameterizedTest
@MethodSource("allTypes")
public void testCompareSingleColumnTuples(NativeType type) {
- var columnDescriptor = new ColumnDescriptor("column", type, false, true);
+ var columnDescriptor = new SortedIndexColumnDescriptor("column", type, false, true);
var descriptor = new SortedIndexDescriptor(UUID.randomUUID(), List.of(columnDescriptor));
@@ -300,9 +300,9 @@ private static IgniteBiTuple createTestValues(NativeType
@Test
public void testCompareMultipleColumnTuples() {
- List columnDescriptors = List.of(
- new ColumnDescriptor("column", NativeTypes.INT32, false, true),
- new ColumnDescriptor("column", NativeTypes.STRING, false, false)
+ List columnDescriptors = List.of(
+ new SortedIndexColumnDescriptor("column", NativeTypes.INT32, false, true),
+ new SortedIndexColumnDescriptor("column", NativeTypes.STRING, false, false)
);
var descriptor = new SortedIndexDescriptor(UUID.randomUUID(), columnDescriptors);
@@ -334,9 +334,9 @@ public void testCompareMultipleColumnTuples() {
@Test
public void testCompareMultipleColumnTuplesWithNulls() {
- List columnDescriptors = List.of(
- new ColumnDescriptor("column", NativeTypes.INT32, true, true),
- new ColumnDescriptor("column", NativeTypes.STRING, true, false)
+ List columnDescriptors = List.of(
+ new SortedIndexColumnDescriptor("column", NativeTypes.INT32, true, true),
+ new SortedIndexColumnDescriptor("column", NativeTypes.STRING, true, false)
);
var descriptor = new SortedIndexDescriptor(UUID.randomUUID(), columnDescriptors);
@@ -377,9 +377,9 @@ public void testCompareMultipleColumnTuplesWithNulls() {
@Test
public void testCompareWithPrefix() {
- List columnDescriptors = List.of(
- new ColumnDescriptor("column", NativeTypes.INT32, false, true),
- new ColumnDescriptor("column", NativeTypes.STRING, false, false)
+ List columnDescriptors = List.of(
+ new SortedIndexColumnDescriptor("column", NativeTypes.INT32, false, true),
+ new SortedIndexColumnDescriptor("column", NativeTypes.STRING, false, false)
);
var descriptor = new SortedIndexDescriptor(UUID.randomUUID(), columnDescriptors);
@@ -420,9 +420,9 @@ public void testCompareWithPrefix() {
@Test
public void testCompareWithPrefixWithNulls() {
- List columnDescriptors = List.of(
- new ColumnDescriptor("column", NativeTypes.INT32, true, true),
- new ColumnDescriptor("column", NativeTypes.STRING, false, false)
+ List columnDescriptors = List.of(
+ new SortedIndexColumnDescriptor("column", NativeTypes.INT32, true, true),
+ new SortedIndexColumnDescriptor("column", NativeTypes.STRING, false, false)
);
var descriptor = new SortedIndexDescriptor(UUID.randomUUID(), columnDescriptors);
diff --git a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractSortedIndexStorageTest.java b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractSortedIndexStorageTest.java
index 7ba2a0231b9e..6f241dd086d1 100644
--- a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractSortedIndexStorageTest.java
+++ b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/AbstractSortedIndexStorageTest.java
@@ -69,7 +69,7 @@
import org.apache.ignite.internal.storage.MvPartitionStorage;
import org.apache.ignite.internal.storage.RowId;
import org.apache.ignite.internal.storage.engine.MvTableStorage;
-import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.ColumnDescriptor;
+import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.SortedIndexColumnDescriptor;
import org.apache.ignite.internal.storage.index.impl.BinaryTupleRowSerializer;
import org.apache.ignite.internal.storage.index.impl.TestIndexRow;
import org.apache.ignite.internal.testframework.VariableSource;
@@ -212,8 +212,8 @@ private SortedIndexStorage createIndexStorage(ColumnarIndexDefinition indexDefin
void testRowSerialization() {
SortedIndexStorage indexStorage = createIndexStorage(ALL_TYPES_COLUMN_DEFINITIONS);
- Object[] columns = indexStorage.indexDescriptor().indexColumns().stream()
- .map(ColumnDescriptor::type)
+ Object[] columns = indexStorage.indexDescriptor().columns().stream()
+ .map(SortedIndexColumnDescriptor::type)
.map(type -> SchemaTestUtils.generateRandomValue(random, type))
.toArray();
diff --git a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/BinaryTupleRowSerializer.java b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/BinaryTupleRowSerializer.java
index 347bd8cc4e6e..9b029d00180c 100644
--- a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/BinaryTupleRowSerializer.java
+++ b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/BinaryTupleRowSerializer.java
@@ -58,7 +58,7 @@ private static class ColumnDescriptor {
* Creates a new instance for a Sorted Index.
*/
public BinaryTupleRowSerializer(SortedIndexDescriptor descriptor) {
- this(descriptor.indexColumns().stream()
+ this(descriptor.columns().stream()
.map(colDesc -> new ColumnDescriptor(colDesc.type(), colDesc.nullable()))
.collect(toUnmodifiableList()));
}
@@ -67,7 +67,7 @@ public BinaryTupleRowSerializer(SortedIndexDescriptor descriptor) {
* Creates a new instance for a Hash Index.
*/
public BinaryTupleRowSerializer(HashIndexDescriptor descriptor) {
- this(descriptor.indexColumns().stream()
+ this(descriptor.columns().stream()
.map(colDesc -> new ColumnDescriptor(colDesc.type(), colDesc.nullable()))
.collect(toUnmodifiableList()));
}
diff --git a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/TestIndexRow.java b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/TestIndexRow.java
index b2f072d00918..5ddd6f03e952 100644
--- a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/TestIndexRow.java
+++ b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/index/impl/TestIndexRow.java
@@ -32,7 +32,7 @@
import org.apache.ignite.internal.schema.SchemaTestUtils;
import org.apache.ignite.internal.storage.RowId;
import org.apache.ignite.internal.storage.index.IndexRow;
-import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.ColumnDescriptor;
+import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.SortedIndexColumnDescriptor;
import org.apache.ignite.internal.storage.index.SortedIndexStorage;
/**
@@ -64,8 +64,8 @@ public TestIndexRow(SortedIndexStorage storage, BinaryTupleRowSerializer seriali
public static TestIndexRow randomRow(SortedIndexStorage indexStorage) {
var random = new Random();
- Object[] columns = indexStorage.indexDescriptor().indexColumns().stream()
- .map(ColumnDescriptor::type)
+ Object[] columns = indexStorage.indexDescriptor().columns().stream()
+ .map(SortedIndexColumnDescriptor::type)
.map(type -> generateRandomValue(random, type))
.toArray();
@@ -109,7 +109,7 @@ public int compareTo(TestIndexRow o) {
int compare = comparator.compare(columns[i], o.columns[i]);
if (compare != 0) {
- boolean asc = indexStorage.indexDescriptor().indexColumns().get(i).asc();
+ boolean asc = indexStorage.indexDescriptor().columns().get(i).asc();
return asc ? compare : -compare;
}
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/InlineUtils.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/InlineUtils.java
new file mode 100644
index 000000000000..4f69d569f7b0
--- /dev/null
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/InlineUtils.java
@@ -0,0 +1,197 @@
+/*
+ * 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.storage.pagememory.index;
+
+import static org.apache.ignite.internal.binarytuple.BinaryTupleCommon.valueSizeToEntrySize;
+import static org.apache.ignite.internal.pagememory.tree.io.BplusInnerIo.CHILD_LINK_SIZE;
+import static org.apache.ignite.internal.util.Constants.KiB;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+import org.apache.ignite.internal.binarytuple.BinaryTupleCommon;
+import org.apache.ignite.internal.pagememory.freelist.FreeList;
+import org.apache.ignite.internal.pagememory.tree.BplusTree;
+import org.apache.ignite.internal.pagememory.tree.io.BplusInnerIo;
+import org.apache.ignite.internal.pagememory.tree.io.BplusLeafIo;
+import org.apache.ignite.internal.schema.BinaryTuple;
+import org.apache.ignite.internal.schema.NativeType;
+import org.apache.ignite.internal.schema.NativeTypeSpec;
+import org.apache.ignite.internal.schema.VarlenNativeType;
+import org.apache.ignite.internal.storage.index.IndexDescriptor;
+import org.apache.ignite.internal.storage.index.IndexDescriptor.ColumnDescriptor;
+
+/**
+ * Helper class for index inlining.
+ *
+ * Index inlining is an optimization that allows index rows (or prefix) to be compared in a {@link BplusTree} without loading them from a
+ * {@link FreeList}.
+ */
+public class InlineUtils {
+ /** Maximum inline size for a {@link BinaryTuple}, in bytes. */
+ public static final int MAX_BINARY_TUPLE_INLINE_SIZE = 2 * KiB;
+
+ /** Heuristic maximum inline size of a variable length column in bytes. */
+ static final int MAX_VARLEN_INLINE_SIZE = 64;
+
+ /** Heuristic maximum size of an entry in the {@link BinaryTuple} offset table, in bytes. */
+ static final int MAX_BINARY_TUPLE_OFFSET_TABLE_ENTRY_SIZE = 2;
+
+ /**
+ * Minimum number of items in a {@link BplusInnerIo} so that the search in the B+tree does not lose much in performance due to its rapid
+ * growth.
+ */
+ static final int MIN_INNER_PAGE_ITEM_COUNT = 2;
+
+ /** Heuristic maximum inline size for {@link BigDecimal} and {@link BigInteger} column in bytes. */
+ static final int BIG_NUMBER_INLINE_SIZE = 4;
+
+ /**
+ * Calculates inline size for column.
+ *
+ * @param nativeType Column type.
+ * @return Inline size in bytes.
+ */
+ static int inlineSize(NativeType nativeType) {
+ NativeTypeSpec spec = nativeType.spec();
+
+ if (spec.fixedLength()) {
+ return nativeType.sizeInBytes();
+ }
+
+ // Variable length columns.
+
+ switch (spec) {
+ case STRING:
+ case BYTES:
+ return Math.min(MAX_VARLEN_INLINE_SIZE, ((VarlenNativeType) nativeType).length());
+
+ case DECIMAL:
+ case NUMBER:
+ return BIG_NUMBER_INLINE_SIZE;
+
+ default:
+ throw new IllegalArgumentException("Unknown type " + spec);
+ }
+ }
+
+ /**
+ * Calculates inline size for {@link BinaryTuple}, given its format.
+ *
+ * @param indexDescriptor Index descriptor.
+ * @return Inline size in bytes, no more than {@link #MAX_BINARY_TUPLE_INLINE_SIZE}.
+ */
+ static int binaryTupleInlineSize(IndexDescriptor indexDescriptor) {
+ List extends ColumnDescriptor> columns = indexDescriptor.columns();
+
+ assert !columns.isEmpty();
+
+ boolean hasNullColumns = columns.stream().anyMatch(ColumnDescriptor::nullable);
+
+ // Let's calculate the inline size for all columns.
+ int columnsInlineSize = columns.stream().map(ColumnDescriptor::type).mapToInt(InlineUtils::inlineSize).sum();
+
+ int inlineSize = BinaryTupleCommon.HEADER_SIZE
+ + (hasNullColumns ? BinaryTupleCommon.nullMapSize(columns.size()) : 0)
+ + columns.size() * Math.min(MAX_BINARY_TUPLE_OFFSET_TABLE_ENTRY_SIZE, valueSizeToEntrySize(columnsInlineSize))
+ + columnsInlineSize;
+
+ return Math.min(inlineSize, MAX_BINARY_TUPLE_INLINE_SIZE);
+ }
+
+ /**
+ * Calculates the inline size of {@link BinaryTuple} that will be stored in the {@link BplusInnerIo} and {@link BplusLeafIo} item.
+ *
+ * @param pageSize Page size in bytes.
+ * @param itemHeaderSize Size of the item header that is stored in the {@link BplusInnerIo} and {@link BplusLeafIo}, in bytes.
+ * @param indexDescriptor Index descriptor.
+ * @return Inline size in bytes, no more than {@link #MAX_BINARY_TUPLE_INLINE_SIZE}.
+ */
+ public static int binaryTupleInlineSize(int pageSize, int itemHeaderSize, IndexDescriptor indexDescriptor) {
+ int maxInnerNodeItemSize = ((innerNodePayloadSize(pageSize) - CHILD_LINK_SIZE) / MIN_INNER_PAGE_ITEM_COUNT) - CHILD_LINK_SIZE;
+
+ int binaryTupleInlineSize = Math.min(maxInnerNodeItemSize - itemHeaderSize, binaryTupleInlineSize(indexDescriptor));
+
+ if (binaryTupleInlineSize >= MAX_BINARY_TUPLE_INLINE_SIZE) {
+ return MAX_BINARY_TUPLE_INLINE_SIZE;
+ }
+
+ // If we have variable length columns and there is enough unused space in the innerNodes and leafNodes, then we can try increasing
+ // the inline size for the BinaryTuple. Example: pageSize = 1024, binaryTupleInlineSize = 124, BplusLeafIo.HEADER_SIZE = 56.
+ // In this case, the innerNode will fit 7 (with child links) items and 7 items in the leafNode, while 52 bytes will remain free for
+ // the innerNode items, and 100 bytes for the leafNode items, which we could use. For a innerNode, we can add (52 / 7) = 7 bytes
+ // for each item (with link), and for a leafNode, (100 / 7) = 14 bytes for each item, so we can safely use the 7 extra bytes for the
+ // innerNode and leafNode per item.
+
+ if (indexDescriptor.columns().stream().anyMatch(c -> !c.type().spec().fixedLength())) {
+ int itemSize = binaryTupleInlineSize + itemHeaderSize;
+
+ int innerNodeItemSize =
+ optimizeItemSize(innerNodePayloadSize(pageSize) - CHILD_LINK_SIZE, itemSize + CHILD_LINK_SIZE) - CHILD_LINK_SIZE;
+
+ int leafNodeItemSize = optimizeItemSize(leafNodePayloadSize(pageSize), itemSize);
+
+ int optimizedItemSize = Math.min(innerNodeItemSize, leafNodeItemSize);
+
+ assert leafNodePayloadSize(pageSize) / itemSize == leafNodePayloadSize(pageSize) / optimizedItemSize;
+
+ binaryTupleInlineSize = optimizedItemSize - itemHeaderSize;
+ }
+
+ return Math.min(binaryTupleInlineSize, MAX_BINARY_TUPLE_INLINE_SIZE);
+ }
+
+ /**
+ * Returns number of bytes that can be used to store items and links to child nodes in an inner node.
+ *
+ * @param pageSize Page size in bytes.
+ */
+ static int innerNodePayloadSize(int pageSize) {
+ return pageSize - BplusInnerIo.HEADER_SIZE;
+ }
+
+ /**
+ * Returns number of bytes that can be used to store items in a leaf node.
+ *
+ * @param pageSize Page size in bytes.
+ */
+ static int leafNodePayloadSize(int pageSize) {
+ return pageSize - BplusLeafIo.HEADER_SIZE;
+ }
+
+ /**
+ * Optimizes the item size for a {@link BplusInnerIo} or {@link BplusLeafIo} if there is free space for each item.
+ *
+ *
We try to use the available memory on the page as much as possible.
+ *
+ * @param nodePayloadSize Payload size of a {@link BplusInnerIo} or {@link BplusLeafIo}, in bytes.
+ * @param itemSize Size of the item in {@link BplusInnerIo} or {@link BplusLeafIo}, in bytes.
+ * @return Size in bytes.
+ */
+ static int optimizeItemSize(int nodePayloadSize, int itemSize) {
+ // Let's calculate how much space remains in the BplusInnerIo or the BplusLeafIo.
+ int remainingNodePayloadSize = nodePayloadSize % itemSize;
+
+ int nodeItemCount = nodePayloadSize / itemSize;
+
+ // Let's calculate how many additional bytes can be added for each item of the BplusInnerIo and the BplusLeafIo.
+ int additionalNodeItemBytes = remainingNodePayloadSize / nodeItemCount;
+
+ return itemSize + additionalNodeItemBytes;
+ }
+}
diff --git a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/index/InlineUtilsTest.java b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/index/InlineUtilsTest.java
new file mode 100644
index 000000000000..2b7cb6af1754
--- /dev/null
+++ b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/index/InlineUtilsTest.java
@@ -0,0 +1,297 @@
+/*
+ * 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.storage.pagememory.index;
+
+import static org.apache.ignite.internal.pagememory.tree.io.BplusInnerIo.CHILD_LINK_SIZE;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.BIG_NUMBER_INLINE_SIZE;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.MAX_BINARY_TUPLE_INLINE_SIZE;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.MAX_VARLEN_INLINE_SIZE;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.binaryTupleInlineSize;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.inlineSize;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.innerNodePayloadSize;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.leafNodePayloadSize;
+import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.optimizeItemSize;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.stream.IntStream;
+import org.apache.ignite.internal.binarytuple.BinaryTupleCommon;
+import org.apache.ignite.internal.pagememory.tree.io.BplusInnerIo;
+import org.apache.ignite.internal.pagememory.tree.io.BplusLeafIo;
+import org.apache.ignite.internal.schema.NativeType;
+import org.apache.ignite.internal.schema.NativeTypeSpec;
+import org.apache.ignite.internal.schema.NativeTypes;
+import org.apache.ignite.internal.storage.index.IndexDescriptor;
+import org.apache.ignite.internal.storage.index.IndexDescriptor.ColumnDescriptor;
+import org.junit.jupiter.api.Test;
+
+/**
+ * For {@link InlineUtils} testing.
+ */
+public class InlineUtilsTest {
+ @Test
+ void testInlineSizeForNativeType() {
+ EnumSet nativeTypeSpecs = EnumSet.allOf(NativeTypeSpec.class);
+
+ // Fixed length type checking.
+
+ NativeType nativeType = NativeTypes.INT8;
+
+ assertEquals(1, inlineSize(nativeType));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(2, inlineSize(nativeType = NativeTypes.INT16));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(4, inlineSize(nativeType = NativeTypes.INT32));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(8, inlineSize(nativeType = NativeTypes.INT64));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(4, inlineSize(nativeType = NativeTypes.FLOAT));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(8, inlineSize(nativeType = NativeTypes.DOUBLE));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(16, inlineSize(nativeType = NativeTypes.UUID));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(1, inlineSize(nativeType = NativeTypes.bitmaskOf(8)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(3, inlineSize(nativeType = NativeTypes.DATE));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(4, inlineSize(nativeType = NativeTypes.time()));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(9, inlineSize(nativeType = NativeTypes.datetime()));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(12, inlineSize(nativeType = NativeTypes.timestamp()));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ // Variable length type checking.
+
+ assertEquals(BIG_NUMBER_INLINE_SIZE, inlineSize(nativeType = NativeTypes.decimalOf(1, 1)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(BIG_NUMBER_INLINE_SIZE, inlineSize(nativeType = NativeTypes.decimalOf(100, 1)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(7, inlineSize(nativeType = NativeTypes.stringOf(7)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(MAX_VARLEN_INLINE_SIZE, inlineSize(nativeType = NativeTypes.stringOf(256)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(MAX_VARLEN_INLINE_SIZE, inlineSize(nativeType = NativeTypes.stringOf(Integer.MAX_VALUE)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(9, inlineSize(nativeType = NativeTypes.blobOf(9)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(MAX_VARLEN_INLINE_SIZE, inlineSize(nativeType = NativeTypes.blobOf(256)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(MAX_VARLEN_INLINE_SIZE, inlineSize(nativeType = NativeTypes.blobOf(Integer.MAX_VALUE)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(BIG_NUMBER_INLINE_SIZE, inlineSize(nativeType = NativeTypes.numberOf(1)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ assertEquals(BIG_NUMBER_INLINE_SIZE, inlineSize(nativeType = NativeTypes.numberOf(100)));
+ nativeTypeSpecs.remove(nativeType.spec());
+
+ // Let's check that all types have been checked.
+ assertThat(nativeTypeSpecs, empty());
+ }
+
+ @Test
+ void testBinaryTupleInlineSize() {
+ IndexDescriptor indexDescriptor = testIndexDescriptor(testColumnDescriptor(NativeTypes.INT8, false));
+
+ assertEquals(
+ BinaryTupleCommon.HEADER_SIZE + 1 + NativeTypes.INT8.sizeInBytes(), // Without a nullMap card.
+ binaryTupleInlineSize(indexDescriptor)
+ );
+
+ indexDescriptor = testIndexDescriptor(testColumnDescriptor(NativeTypes.INT32, true));
+
+ assertEquals(
+ BinaryTupleCommon.HEADER_SIZE + 1 + 1 + NativeTypes.INT32.sizeInBytes(), // With a nullMap card.
+ binaryTupleInlineSize(indexDescriptor)
+ );
+
+ // Let's check the 2-byte entry size for the BinaryTuple offset table.
+
+ indexDescriptor = testIndexDescriptor(
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false),
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false),
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false),
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false)
+ );
+
+ assertEquals(
+ BinaryTupleCommon.HEADER_SIZE + 4 * 2 + 4 * MAX_VARLEN_INLINE_SIZE, // Without a nullMap card.
+ binaryTupleInlineSize(indexDescriptor)
+ );
+
+ indexDescriptor = testIndexDescriptor(
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false),
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), true),
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false),
+ testColumnDescriptor(NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE), false)
+ );
+
+ assertEquals(
+ BinaryTupleCommon.HEADER_SIZE + 4 * 2 + 1 + 4 * MAX_VARLEN_INLINE_SIZE, // With a nullMap card.
+ binaryTupleInlineSize(indexDescriptor)
+ );
+
+ // Let's check that it does not exceed the MAX_BINARY_TUPLE_INLINE_SIZE.
+
+ ColumnDescriptor[] columnDescriptors = IntStream.range(0, MAX_BINARY_TUPLE_INLINE_SIZE / MAX_VARLEN_INLINE_SIZE)
+ .mapToObj(i -> NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE))
+ .map(nativeType -> testColumnDescriptor(nativeType, false))
+ .toArray(ColumnDescriptor[]::new);
+
+ assertEquals(
+ MAX_BINARY_TUPLE_INLINE_SIZE, // Without a nullMap card.
+ binaryTupleInlineSize(testIndexDescriptor(columnDescriptors))
+ );
+
+ columnDescriptors = IntStream.range(0, MAX_BINARY_TUPLE_INLINE_SIZE / MAX_VARLEN_INLINE_SIZE)
+ .mapToObj(i -> NativeTypes.stringOf(MAX_VARLEN_INLINE_SIZE))
+ .map(nativeType -> testColumnDescriptor(nativeType, true))
+ .toArray(ColumnDescriptor[]::new);
+
+ assertEquals(
+ MAX_BINARY_TUPLE_INLINE_SIZE, // With a nullMap card.
+ binaryTupleInlineSize(testIndexDescriptor(columnDescriptors))
+ );
+ }
+
+ @Test
+ void testInnerNodePayloadSize() {
+ int pageSize = 1024;
+
+ assertEquals(pageSize - BplusInnerIo.HEADER_SIZE, innerNodePayloadSize(pageSize));
+
+ pageSize = 128;
+
+ assertEquals(pageSize - BplusInnerIo.HEADER_SIZE, innerNodePayloadSize(pageSize));
+ }
+
+ @Test
+ void testLeafNodePayloadSize() {
+ int pageSize = 1024;
+
+ assertEquals(pageSize - BplusLeafIo.HEADER_SIZE, leafNodePayloadSize(pageSize));
+
+ pageSize = 128;
+
+ assertEquals(pageSize - BplusLeafIo.HEADER_SIZE, leafNodePayloadSize(pageSize));
+ }
+
+ @Test
+ void testBinaryTupleInlineSizeForBplusTree() {
+ int pageSize = 1024;
+ int itemHeaderSize = 6;
+
+ // Let's check without variable length columns.
+
+ IndexDescriptor indexDescriptor = testIndexDescriptor(
+ testColumnDescriptor(NativeTypes.INT64, false),
+ testColumnDescriptor(NativeTypes.UUID, false)
+ );
+
+ assertEquals(
+ BinaryTupleCommon.HEADER_SIZE + 2 + NativeTypes.INT64.sizeInBytes() + NativeTypes.UUID.sizeInBytes(),
+ binaryTupleInlineSize(pageSize, itemHeaderSize, indexDescriptor)
+ );
+
+ indexDescriptor = testIndexDescriptor(
+ testColumnDescriptor(NativeTypes.INT64, false),
+ testColumnDescriptor(NativeTypes.UUID, false),
+ testColumnDescriptor(NativeTypes.UUID, false),
+ testColumnDescriptor(NativeTypes.UUID, false),
+ testColumnDescriptor(NativeTypes.UUID, true)
+ );
+
+ assertEquals(
+ (((innerNodePayloadSize(128) - CHILD_LINK_SIZE) / 2) - CHILD_LINK_SIZE) - itemHeaderSize,
+ binaryTupleInlineSize(128, itemHeaderSize, indexDescriptor)
+ );
+
+ // Let's check without variable length columns.
+
+ indexDescriptor = testIndexDescriptor(
+ testColumnDescriptor(NativeTypes.stringOf(32), true)
+ );
+
+ assertEquals(
+ (((innerNodePayloadSize(128) - CHILD_LINK_SIZE) / 2) - CHILD_LINK_SIZE) - itemHeaderSize,
+ binaryTupleInlineSize(128, itemHeaderSize, indexDescriptor)
+ );
+
+ indexDescriptor = testIndexDescriptor(
+ testColumnDescriptor(NativeTypes.INT64, false),
+ testColumnDescriptor(NativeTypes.UUID, false),
+ testColumnDescriptor(NativeTypes.UUID, false),
+ testColumnDescriptor(NativeTypes.UUID, false),
+ testColumnDescriptor(NativeTypes.stringOf(32), true)
+ );
+
+ assertEquals(
+ BinaryTupleCommon.HEADER_SIZE + 1 + 5 + NativeTypes.INT64.sizeInBytes() + 3 * NativeTypes.UUID.sizeInBytes() + 32 + 6,
+ binaryTupleInlineSize(pageSize, itemHeaderSize, indexDescriptor)
+ );
+ }
+
+ @Test
+ void testOptimizeItemSize() {
+ assertEquals(100, optimizeItemSize(1000, 100));
+
+ assertEquals(333, optimizeItemSize(1000, 330));
+ }
+
+ private static IndexDescriptor testIndexDescriptor(ColumnDescriptor... columnDescriptors) {
+ IndexDescriptor indexDescriptor = mock(IndexDescriptor.class);
+
+ when(indexDescriptor.columns()).then(answer -> List.of(columnDescriptors));
+
+ return indexDescriptor;
+ }
+
+ private static ColumnDescriptor testColumnDescriptor(NativeType nativeType, boolean nullable) {
+ ColumnDescriptor columnDescriptor = mock(ColumnDescriptor.class);
+
+ when(columnDescriptor.type()).thenReturn(nativeType);
+ when(columnDescriptor.nullable()).thenReturn(nullable);
+
+ return columnDescriptor;
+ }
+}