From c0395a55bbff94d29150cde8bb952678c0441c60 Mon Sep 17 00:00:00 2001 From: Terry Moschou <5567550+tmoschou@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:41:45 +1030 Subject: [PATCH 1/2] CASSANDRA-20012 Add blob type support to SAI Add support for the blob CQL type in Storage Attached Index (SAI) as an equality-only (EQ) indexed literal type. --- conf/cassandra.yaml | 4 + conf/cassandra_latest.yaml | 16 +++ .../cql/indexing/sai/sai-concepts.adoc | 6 +- .../developing/cql/indexing/sai/sai-faq.adoc | 13 +- .../org/apache/cassandra/config/Config.java | 2 + .../cassandra/config/GuardrailsOptions.java | 28 +++++ .../cassandra/db/guardrails/Guardrails.java | 32 +++++ .../db/guardrails/GuardrailsConfig.java | 18 +++ .../db/guardrails/GuardrailsMBean.java | 24 ++++ .../index/sai/StorageAttachedIndex.java | 15 ++- .../index/sai/utils/IndexTermType.java | 24 ++-- .../GuardrailSaiBlobTermSizeTest.java | 119 ++++++++++++++++++ .../index/sai/cql/types/BlobTest.java | 31 +++++ .../index/sai/cql/types/DataSet.java | 33 +++++ .../collections/lists/FrozenListBlobTest.java | 35 ++++++ .../types/collections/lists/ListBlobTest.java | 35 ++++++ .../collections/maps/FrozenMapBlobTest.java | 35 ++++++ .../types/collections/maps/MapBlobTest.java | 35 ++++++ .../collections/maps/MapEntriesBlobTest.java | 35 ++++++ .../collections/maps/MapKeysBlobTest.java | 35 ++++++ .../collections/maps/MapValuesBlobTest.java | 35 ++++++ .../collections/maps/MultiMapBlobTest.java | 35 ++++++ .../collections/sets/FrozenSetBlobTest.java | 35 ++++++ .../types/collections/sets/SetBlobTest.java | 35 ++++++ .../index/sai/utils/IndexTermTypeTest.java | 3 +- .../GuardrailsConfigCommandsTest.java | 3 + 26 files changed, 700 insertions(+), 21 deletions(-) create mode 100644 test/unit/org/apache/cassandra/db/guardrails/GuardrailSaiBlobTermSizeTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/BlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/FrozenListBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/ListBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/FrozenMapBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapEntriesBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapKeysBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapValuesBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MultiMapBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/FrozenSetBlobTest.java create mode 100644 test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/SetBlobTest.java diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index e09518188a60..37bbd5beedd1 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -2210,6 +2210,10 @@ drop_compact_storage_enabled: false # sai_string_term_size_warn_threshold: 1KiB # sai_string_term_size_fail_threshold: 8KiB +# Guardrail specifying warn/fail thresholds for the size of blob terms written to an SAI index +# sai_blob_term_size_warn_threshold: 1KiB +# sai_blob_term_size_fail_threshold: 8KiB + # Guardrail specifying warn/fail thresholds for the size of frozen terms written to an SAI index # sai_frozen_term_size_warn_threshold: 1KiB # sai_frozen_term_size_fail_threshold: 8KiB diff --git a/conf/cassandra_latest.yaml b/conf/cassandra_latest.yaml index 0c7a792c8f40..72ab77b07b59 100644 --- a/conf/cassandra_latest.yaml +++ b/conf/cassandra_latest.yaml @@ -2185,6 +2185,22 @@ drop_compact_storage_enabled: false # before emitting a failure (defaults to -1 to disable) #sai_sstable_indexes_per_query_fail_threshold: -1 +# Guardrail specifying warn/fail thresholds for the size of string terms written to an SAI index +# sai_string_term_size_warn_threshold: 1KiB +# sai_string_term_size_fail_threshold: 8KiB + +# Guardrail specifying warn/fail thresholds for the size of blob terms written to an SAI index +# sai_blob_term_size_warn_threshold: 1KiB +# sai_blob_term_size_fail_threshold: 8KiB + +# Guardrail specifying warn/fail thresholds for the size of frozen terms written to an SAI index +# sai_frozen_term_size_warn_threshold: 1KiB +# sai_frozen_term_size_fail_threshold: 8KiB + +# Guardrail specifying warn/fail thresholds for the size of vector terms written to an SAI index +# sai_vector_term_size_warn_threshold: 16KiB +# sai_vector_term_size_fail_threshold: 32KiB + # The default secondary index implementation when CREATE INDEX does not specify one via USING. # ex. "legacy_local_table" - (default) legacy secondary index, implemented as a hidden table # ex. "sai" - "storage-attched" index, implemented via optimized SSTable/Memtable-attached indexes diff --git a/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-concepts.adoc b/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-concepts.adoc index 6c8f8a345306..ca436fc9556c 100644 --- a/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-concepts.adoc +++ b/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-concepts.adoc @@ -20,11 +20,11 @@ SAI adds column-level indexes to any CQL table column of almost any CQL data typ SAI enables queries that filter based on: * vector embeddings -* AND/OR logic for numeric and text types -* IN logic (use an array of values) for numeric and text types +* AND/OR logic for numeric, text, and blob types +* IN logic (use an array of values) for numeric, text, and blob types * numeric range * non-variable length numeric types -* text type equality +* text and blob type equality * CONTAINs logic (for collections) * tokenized data * row-aware query path diff --git a/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc b/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc index 95f6783a25f6..0efda32082e4 100644 --- a/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc +++ b/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc @@ -140,10 +140,11 @@ See xref:developing/cql/indexing/sai/sai-read-write-paths.adoc[SAI write path an SAI supports two on-disk index formats, optimized for: -* Equality and non-exact matching on *strings*. -** Strings are indexed on-disk using the https://en.wikipedia.org/wiki/Trie[trie] data structure, in conjunction with postings (term/row pairs) lists. -The trie is heap friendly, providing string prefix compression for terms, and can match any query that can be expressed as a deterministic finite automaton. +* Equality and non-exact matching on *strings, blobs, and booleans*. +** These literal types are indexed on-disk using the https://en.wikipedia.org/wiki/Trie[trie] data structure, in conjunction with postings (term/row pairs) lists. +The trie is heap friendly, providing prefix compression for terms, and can match any query that can be expressed as a deterministic finite automaton. The feature minimizes on-disk footprint and supports simple token skipping. +Note that `BLOB` and `BOOLEAN` types support equality queries only. * Equality and range queries on *numeric and non-literal types*. ** Numeric values and the other non-literal CQL types (`timestamp`, `date`, `UUID`) are indexed on-disk using https://en.wikipedia.org/wiki/K-d_tree[k-dimensional tree], a balanced structure that provides fast lookups across one or more dimensions, and compression for both values and postings. @@ -156,12 +157,14 @@ The SAI disk usage is largely dependent on the underlying data model and the num == What are the supported column data types for SAI indexing? -The supported types are: `ASCII, BIGINT, DATE, DECIMAL, DOUBLE, FLOAT, INET, INT, SMALLINT, TEXT, TIME, TIMESTAMP, TIMEUUID, TINYINT, UUID, VARCHAR, VARINT`. +The supported types are: `ASCII, BIGINT, BLOB, BOOLEAN, DATE, DECIMAL, DOUBLE, FLOAT, INET, INT, SMALLINT, TEXT, TIME, TIMESTAMP, TIMEUUID, TINYINT, UUID, VARCHAR, VARINT`. [NOTE] ==== * `INET` support for IPv4 and IPv6 was new starting with Cassandra ???. * The `DECIMAL` and `VARINT` support was new starting with Cassandra ???. +* The `BOOLEAN` support was new starting with Cassandra ???. +* The `BLOB` support was new starting with Cassandra ???. * SAI also supports collections -- see the xref:#saiCollectionColumnFaq[next FAQ]. ==== @@ -348,4 +351,4 @@ SAI returns `InvalidRequestException` if you try to define an index on a column == What partitioner does SAI support? -SAI supports only the `Murmur3Partitioner`. \ No newline at end of file +SAI supports only the `Murmur3Partitioner`. diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index 9b16609154a6..014efa458af4 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -933,6 +933,8 @@ public static void setClientMode(boolean clientMode) public volatile int sai_sstable_indexes_per_query_fail_threshold = -1; public volatile DataStorageSpec.LongBytesBound sai_string_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("1KiB"); public volatile DataStorageSpec.LongBytesBound sai_string_term_size_fail_threshold = new DataStorageSpec.LongBytesBound("8KiB"); + public volatile DataStorageSpec.LongBytesBound sai_blob_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("1KiB"); + public volatile DataStorageSpec.LongBytesBound sai_blob_term_size_fail_threshold = new DataStorageSpec.LongBytesBound("8KiB"); public volatile DataStorageSpec.LongBytesBound sai_frozen_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("1KiB"); public volatile DataStorageSpec.LongBytesBound sai_frozen_term_size_fail_threshold = new DataStorageSpec.LongBytesBound("8KiB"); public volatile DataStorageSpec.LongBytesBound sai_vector_term_size_warn_threshold = new DataStorageSpec.LongBytesBound("16KiB"); diff --git a/src/java/org/apache/cassandra/config/GuardrailsOptions.java b/src/java/org/apache/cassandra/config/GuardrailsOptions.java index 6e82e7f90882..226a351d5663 100644 --- a/src/java/org/apache/cassandra/config/GuardrailsOptions.java +++ b/src/java/org/apache/cassandra/config/GuardrailsOptions.java @@ -988,6 +988,34 @@ public void setSaiStringTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBou x -> config.sai_string_term_size_fail_threshold = x); } + @Override + @Nullable + public DataStorageSpec.LongBytesBound getSaiBlobTermSizeWarnThreshold() + { + return config.sai_blob_term_size_warn_threshold; + } + + @Override + @Nullable + public DataStorageSpec.LongBytesBound getSaiBlobTermSizeFailThreshold() + { + return config.sai_blob_term_size_fail_threshold; + } + + @Override + public void setSaiBlobTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBound warn, @Nullable DataStorageSpec.LongBytesBound fail) + { + validateSizeThreshold(warn, fail, false, "sai_blob_term_size"); + updatePropertyWithLogging("sai_blob_term_size_warn_threshold", + warn, + () -> config.sai_blob_term_size_warn_threshold, + x -> config.sai_blob_term_size_warn_threshold = x); + updatePropertyWithLogging("sai_blob_term_size_fail_threshold", + fail, + () -> config.sai_blob_term_size_fail_threshold, + x -> config.sai_blob_term_size_fail_threshold = x); + } + @Override @Nullable public DataStorageSpec.LongBytesBound getSaiFrozenTermSizeWarnThreshold() diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java index 829dab056237..5672e2a7b4e0 100644 --- a/src/java/org/apache/cassandra/db/guardrails/Guardrails.java +++ b/src/java/org/apache/cassandra/db/guardrails/Guardrails.java @@ -526,6 +526,18 @@ public final class Guardrails implements GuardrailsMBean format("Value of column '%s' has size %s, this exceeds the %s threshold of %s.", what, value, isWarning ? "warning" : "failure", threshold)); + /** + * Guardrail on the size of a blob term written to SAI index. + */ + public static final MaxThreshold saiBlobTermSize = + new MaxThreshold("sai_blob_term_size", + null, + state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getSaiBlobTermSizeWarnThreshold()), + state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getSaiBlobTermSizeFailThreshold()), + (isWarning, what, value, threshold) -> + format("Value of column '%s' has size %s, this exceeds the %s threshold of %s.", + what, value, isWarning ? "warning" : "failure", threshold)); + /** * Guardrail on the size of a frozen term written to SAI index. */ @@ -1329,6 +1341,26 @@ public void setSaiStringTermSizeThreshold(@Nullable String warnSize, @Nullable S DEFAULT_CONFIG.setSaiStringTermSizeThreshold(sizeFromString(warnSize), sizeFromString(failSize)); } + @Override + @Nullable + public String getSaiBlobTermSizeWarnThreshold() + { + return sizeToString(DEFAULT_CONFIG.getSaiBlobTermSizeWarnThreshold()); + } + + @Override + @Nullable + public String getSaiBlobTermSizeFailThreshold() + { + return sizeToString(DEFAULT_CONFIG.getSaiBlobTermSizeFailThreshold()); + } + + @Override + public void setSaiBlobTermSizeThreshold(@Nullable String warnSize, @Nullable String failSize) + { + DEFAULT_CONFIG.setSaiBlobTermSizeThreshold(sizeFromString(warnSize), sizeFromString(failSize)); + } + @Override @Nullable public String getSaiFrozenTermSizeWarnThreshold() diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java b/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java index ece387461d78..9663abe666d0 100644 --- a/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java +++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsConfig.java @@ -494,6 +494,24 @@ void setMinimumTimestampThreshold(@Nullable DurationSpec.LongMicrosecondsBound w */ void setSaiStringTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBound warn, @Nullable DataStorageSpec.LongBytesBound fail); + /** + * @return the warning threshold for the size of blob terms written to an SAI index + */ + DataStorageSpec.LongBytesBound getSaiBlobTermSizeWarnThreshold(); + + /** + * @return the failure threshold for the size of blob terms written to an SAI index + */ + DataStorageSpec.LongBytesBound getSaiBlobTermSizeFailThreshold(); + + /** + * Sets warning and failure thresholds for the size of blob terms written to an SAI index + * + * @param warn value to set for warn threshold + * @param fail value to set for fail threshold + */ + void setSaiBlobTermSizeThreshold(@Nullable DataStorageSpec.LongBytesBound warn, @Nullable DataStorageSpec.LongBytesBound fail); + /** * @return the warning threshold for the size of frozen terms written to an SAI index */ diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java b/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java index d40092314a3e..fa347f6d9c59 100644 --- a/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java +++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsMBean.java @@ -823,6 +823,30 @@ public interface GuardrailsMBean */ void setSaiStringTermSizeThreshold(@Nullable String warnSize, @Nullable String failSize); + /** + * @return The warning threshold for blob terms written to an SAI index, as a human-readable string. + * (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) A {@code null} value means disabled. + */ + @Nullable + String getSaiBlobTermSizeWarnThreshold(); + + /** + * @return The failure threshold for blob terms written to an SAI index, as a human-readable string. + * (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) A {@code null} value means disabled. + */ + @Nullable + String getSaiBlobTermSizeFailThreshold(); + + /** + * @param warnSize The warning threshold for blob terms written to an SAI index, as a human-readable string. + * (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) + * A {@code null} value means disabled. + * @param failSize The failure threshold for blob terms written to an SAI index, as a human-readable string. + * (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) + * A {@code null} value means disabled. + */ + void setSaiBlobTermSizeThreshold(@Nullable String warnSize, @Nullable String failSize); + /** * @return The warning threshold for frozen terms written to an SAI index, as a human-readable string. * (ex. {@code 10GiB}, {@code 20MiB}, {@code 30KiB} or {@code 40B}) A {@code null} value means disabled. diff --git a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java index 10c2a93cc910..3d3667cb49c5 100644 --- a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java +++ b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java @@ -164,7 +164,8 @@ public class StorageAttachedIndex implements Index CQL3Type.Native.SMALLINT, CQL3Type.Native.TEXT, CQL3Type.Native.TIME, CQL3Type.Native.TIMESTAMP, CQL3Type.Native.TIMEUUID, CQL3Type.Native.TINYINT, CQL3Type.Native.UUID, CQL3Type.Native.VARCHAR, CQL3Type.Native.INET, - CQL3Type.Native.VARINT, CQL3Type.Native.DECIMAL, CQL3Type.Native.BOOLEAN); + CQL3Type.Native.VARINT, CQL3Type.Native.DECIMAL, CQL3Type.Native.BOOLEAN, + CQL3Type.Native.BLOB); private static final Set> ILLEGAL_PARTITIONERS = ImmutableSet.of(OrderPreservingPartitioner.class, LocalPartitioner.class, ByteOrderedPartitioner.class, RandomPartitioner.class); @@ -204,10 +205,14 @@ public StorageAttachedIndex(ColumnFamilyStore baseCfs, IndexMetadata indexMetada analyzerFactory = AbstractAnalyzer.fromOptions(indexTermType, indexMetadata.options); memtableIndexManager = new MemtableIndexManager(this); indexMetrics = new IndexMetrics(this, memtableIndexManager); - maxTermSizeGuardrail = indexTermType.isVector() - ? Guardrails.saiVectorTermSize - : (indexTermType.isFrozen() ? Guardrails.saiFrozenTermSize - : Guardrails.saiStringTermSize); + if (indexTermType.isVector()) + maxTermSizeGuardrail = Guardrails.saiVectorTermSize; + else if (indexTermType.isFrozen()) + maxTermSizeGuardrail = Guardrails.saiFrozenTermSize; + else if (indexTermType.isBytes()) + maxTermSizeGuardrail = Guardrails.saiBlobTermSize; + else + maxTermSizeGuardrail = Guardrails.saiStringTermSize; } /** diff --git a/src/java/org/apache/cassandra/index/sai/utils/IndexTermType.java b/src/java/org/apache/cassandra/index/sai/utils/IndexTermType.java index f3c7e2c05f96..be732723b6cd 100644 --- a/src/java/org/apache/cassandra/index/sai/utils/IndexTermType.java +++ b/src/java/org/apache/cassandra/index/sai/utils/IndexTermType.java @@ -47,6 +47,7 @@ import org.apache.cassandra.db.marshal.AsciiType; import org.apache.cassandra.db.marshal.BooleanType; import org.apache.cassandra.db.marshal.ByteBufferAccessor; +import org.apache.cassandra.db.marshal.BytesType; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.CompositeType; import org.apache.cassandra.db.marshal.DecimalType; @@ -78,7 +79,8 @@ public class IndexTermType private static final Set> EQ_ONLY_TYPES = ImmutableSet.of(UTF8Type.instance, AsciiType.instance, BooleanType.instance, - UUIDType.instance); + UUIDType.instance, + BytesType.instance); private static final byte[] IPV4_PREFIX = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 }; @@ -102,6 +104,7 @@ private enum Capability BIG_DECIMAL, LONG, BOOLEAN, + BYTES, LITERAL, REVERSED, FROZEN, @@ -172,7 +175,7 @@ private IndexTermType(ColumnMetadata columnMetadata, List partit /** * Returns {@code true} if the index type is a literal type and will use a literal index. This applies to - * string types, frozen types, composite types and boolean type. + * string types, frozen types, composite types, bytes type and boolean type. */ public boolean isLiteral() { @@ -206,6 +209,14 @@ public boolean isReversed() return capabilities.contains(Capability.REVERSED); } + /** + * Returns {@code true} if the index type is a bytes/blob type. + */ + public boolean isBytes() + { + return capabilities.contains(Capability.BYTES); + } + /** * Returns {@code true} if the index type is frozen, e.g. the type is wrapped with {@code frozen}. */ @@ -328,11 +339,6 @@ public boolean dependsOn(ColumnMetadata columnMetadata) return this.columnMetadata.compareTo(columnMetadata) == 0; } - public static boolean isEqOnlyType(AbstractType type) - { - return EQ_ONLY_TYPES.contains(type); - } - /** * Indicates if the type encoding supports rounding of the raw value. *

@@ -683,8 +689,12 @@ else if (!indexType.subTypes().isEmpty() && !indexType.isMultiCell()) if (indexType instanceof BooleanType) capabilities.add(Capability.BOOLEAN); + if (indexType instanceof BytesType) + capabilities.add(Capability.BYTES); + if (capabilities.contains(Capability.STRING) || capabilities.contains(Capability.BOOLEAN) || + capabilities.contains(Capability.BYTES) || capabilities.contains(Capability.FROZEN) || capabilities.contains(Capability.COMPOSITE)) capabilities.add(Capability.LITERAL); diff --git a/test/unit/org/apache/cassandra/db/guardrails/GuardrailSaiBlobTermSizeTest.java b/test/unit/org/apache/cassandra/db/guardrails/GuardrailSaiBlobTermSizeTest.java new file mode 100644 index 000000000000..43f4074e40f8 --- /dev/null +++ b/test/unit/org/apache/cassandra/db/guardrails/GuardrailSaiBlobTermSizeTest.java @@ -0,0 +1,119 @@ +/* + * 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.cassandra.db.guardrails; + +import java.nio.ByteBuffer; + +import org.junit.Test; + +import org.apache.cassandra.config.DataStorageSpec; +import org.apache.cassandra.transport.messages.ResultMessage; + +import static java.nio.ByteBuffer.allocate; +import static org.apache.cassandra.config.DataStorageSpec.DataStorageUnit.BYTES; +import static org.junit.Assert.assertEquals; + +/** + * Tests the guardrails around the size of SAI blob terms + * + * @see Guardrails#saiBlobTermSize + */ +public class GuardrailSaiBlobTermSizeTest extends ValueThresholdTester +{ + private static final int WARN_THRESHOLD = 1024; // bytes + private static final int FAIL_THRESHOLD = WARN_THRESHOLD * 4; // bytes + + public GuardrailSaiBlobTermSizeTest() + { + super(WARN_THRESHOLD + "B", + FAIL_THRESHOLD + "B", + Guardrails.saiBlobTermSize, + Guardrails::setSaiBlobTermSizeThreshold, + Guardrails::getSaiBlobTermSizeWarnThreshold, + Guardrails::getSaiBlobTermSizeFailThreshold, + bytes -> new DataStorageSpec.LongBytesBound(bytes, BYTES).toString(), + size -> new DataStorageSpec.LongBytesBound(size).toBytes()); + } + + @Override + protected int warnThreshold() + { + return WARN_THRESHOLD; + } + + @Override + protected int failThreshold() + { + return FAIL_THRESHOLD; + } + + @Test + public void testRegularColumn() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v blob)"); + createIndex("CREATE INDEX ON %s (v) USING 'sai'"); + + testThreshold("v", "INSERT INTO %s (k, v) VALUES (0, ?)"); + testThreshold("v", "UPDATE %s SET v = ? WHERE k = 0"); + } + + @Test + public void testStaticColumn() throws Throwable + { + createTable("CREATE TABLE %s (k int, c int, s blob STATIC, r int, PRIMARY KEY(k, c))"); + createIndex("CREATE INDEX ON %s (s) USING 'sai'"); + + testThreshold("s", "INSERT INTO %s (k, s) VALUES (0, ?)"); + testThreshold("s", "INSERT INTO %s (k, c, s, r) VALUES (0, 0, ?, 0)"); + testThreshold("s", "UPDATE %s SET s = ? WHERE k = 0"); + testThreshold("s", "UPDATE %s SET s = ?, r = 0 WHERE k = 0 AND c = 0"); + } + + @Test + public void testWarningTermOnBuild() + { + ByteBuffer largeTerm = allocate(warnThreshold() + 1); + ByteBuffer smallTerm = allocate(1); + + createTable(KEYSPACE, "CREATE TABLE %s (k int PRIMARY KEY, v blob)"); + execute("INSERT INTO %s (k, v) VALUES (0, ?)", largeTerm); + execute("INSERT INTO %s (k, v) VALUES (1, ?)", smallTerm); + createIndex("CREATE INDEX ON %s(v) USING 'sai'"); + + // verify that the large term is written on initial index build + assertEquals(((ResultMessage.Rows) execute("SELECT * FROM %s WHERE v = ?", largeTerm)).result.size(), 1); + assertEquals(((ResultMessage.Rows) execute("SELECT * FROM %s WHERE v = ?", smallTerm)).result.size(), 1); + } + + @Test + public void testFailingTermOnBuild() + { + ByteBuffer oversizedTerm = allocate(failThreshold() + 1); + ByteBuffer smallTerm = allocate(1); + + createTable(KEYSPACE, "CREATE TABLE %s (k int PRIMARY KEY, v blob)"); + execute("INSERT INTO %s (k, v) VALUES (0, ?)", oversizedTerm); + execute("INSERT INTO %s (k, v) VALUES (1, ?)", smallTerm); + createIndex("CREATE INDEX ON %s(v) USING 'sai'"); + + // verify that the oversized term isn't written on initial index build + assertEquals(((ResultMessage.Rows) execute("SELECT * FROM %s WHERE v = ?", oversizedTerm)).result.size(), 0); + assertEquals(((ResultMessage.Rows) execute("SELECT * FROM %s WHERE v = ?", smallTerm)).result.size(), 1); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/BlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/BlobTest.java new file mode 100644 index 000000000000..869c07a073ad --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/BlobTest.java @@ -0,0 +1,31 @@ +/* + * 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.cassandra.index.sai.cql.types; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +public class BlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new DataSet.BlobDataSet()); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/DataSet.java b/test/unit/org/apache/cassandra/index/sai/cql/types/DataSet.java index 6eeefb62e667..da8792b2df6c 100644 --- a/test/unit/org/apache/cassandra/index/sai/cql/types/DataSet.java +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/DataSet.java @@ -694,4 +694,37 @@ public String toString() return "inet"; } } + + public static class BlobDataSet extends DataSet + { + public BlobDataSet() + { + values = new ByteBuffer[NUMBER_OF_VALUES]; + List list = Arrays.asList(values); + + for (int index = 0; index < values.length; index++) + { + ByteBuffer value; + do + { + byte[] bytes = new byte[getRandom().nextIntBetween(1, 256)]; + getRandom().nextBytes(bytes); + value = ByteBuffer.wrap(bytes); + } + while (list.contains(value)); + values[index] = value; + } + } + + @Override + public QuerySet querySet() + { + return new QuerySet.LiteralQuerySet(); + } + + public String toString() + { + return "blob"; + } + } } diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/FrozenListBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/FrozenListBlobTest.java new file mode 100644 index 000000000000..59e74ae0902d --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/FrozenListBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.lists; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class FrozenListBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.FrozenListDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/ListBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/ListBlobTest.java new file mode 100644 index 000000000000..d3d33b56bc73 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/lists/ListBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.lists; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class ListBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.ListDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/FrozenMapBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/FrozenMapBlobTest.java new file mode 100644 index 000000000000..f9defd1414f7 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/FrozenMapBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.maps; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class FrozenMapBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.FrozenMapValuesDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapBlobTest.java new file mode 100644 index 000000000000..fc1c16398c24 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.maps; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class MapBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.MapDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapEntriesBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapEntriesBlobTest.java new file mode 100644 index 000000000000..988249c26268 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapEntriesBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.maps; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class MapEntriesBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.MapEntriesDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapKeysBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapKeysBlobTest.java new file mode 100644 index 000000000000..102293d73a77 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapKeysBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.maps; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class MapKeysBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.MapKeysDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapValuesBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapValuesBlobTest.java new file mode 100644 index 000000000000..0f05757b9f3a --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MapValuesBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.maps; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class MapValuesBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.MapValuesDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MultiMapBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MultiMapBlobTest.java new file mode 100644 index 000000000000..7fbc597d8c24 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/maps/MultiMapBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.maps; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class MultiMapBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.MultiMapDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/FrozenSetBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/FrozenSetBlobTest.java new file mode 100644 index 000000000000..3042eecbbec2 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/FrozenSetBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.sets; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class FrozenSetBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.FrozenSetDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/SetBlobTest.java b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/SetBlobTest.java new file mode 100644 index 000000000000..6c48cfa89f40 --- /dev/null +++ b/test/unit/org/apache/cassandra/index/sai/cql/types/collections/sets/SetBlobTest.java @@ -0,0 +1,35 @@ +/* + * 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.cassandra.index.sai.cql.types.collections.sets; + +import java.util.Collection; + +import org.junit.runners.Parameterized; + +import org.apache.cassandra.index.sai.cql.types.DataSet; +import org.apache.cassandra.index.sai.cql.types.IndexingTypeSupport; +import org.apache.cassandra.index.sai.cql.types.collections.CollectionDataSet; + +public class SetBlobTest extends IndexingTypeSupport +{ + @Parameterized.Parameters(name = "dataset={0},wide={1},scenario={2}") + public static Collection generateParameters() + { + return generateParameters(new CollectionDataSet.SetDataSet<>(new DataSet.BlobDataSet())); + } +} diff --git a/test/unit/org/apache/cassandra/index/sai/utils/IndexTermTypeTest.java b/test/unit/org/apache/cassandra/index/sai/utils/IndexTermTypeTest.java index 315adfdac5e8..64ae4b17f5c7 100644 --- a/test/unit/org/apache/cassandra/index/sai/utils/IndexTermTypeTest.java +++ b/test/unit/org/apache/cassandra/index/sai/utils/IndexTermTypeTest.java @@ -67,7 +67,8 @@ public void testSimpleType() boolean isUTF8OrAscii = cql3Type == CQL3Type.Native.ASCII || cql3Type == CQL3Type.Native.TEXT || cql3Type == CQL3Type.Native.VARCHAR; boolean isLiteral = cql3Type == CQL3Type.Native.ASCII || cql3Type == CQL3Type.Native.TEXT || - cql3Type == CQL3Type.Native.VARCHAR || cql3Type == CQL3Type.Native.BOOLEAN; + cql3Type == CQL3Type.Native.VARCHAR || cql3Type == CQL3Type.Native.BOOLEAN || + cql3Type == CQL3Type.Native.BLOB; assertEquals(isLiteral, indexTermType.isLiteral()); assertEquals(indexTermType.isLiteral(), reversedIndexTermType.isLiteral()); assertEquals(isUTF8OrAscii, indexTermType.isString()); diff --git a/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java b/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java index d674c68c36de..1909c6cb5b1d 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/GuardrailsConfigCommandsTest.java @@ -233,6 +233,7 @@ private Set getConfigFieldNames() "partition_keys_in_select_threshold [-1, -1] \n" + "partition_size_threshold [null, null] \n" + "partition_tombstones_threshold [-1, -1] \n" + + "sai_blob_term_size_threshold [8KiB, 1KiB] \n" + "sai_frozen_term_size_threshold [8KiB, 1KiB] \n" + "sai_sstable_indexes_per_query_threshold [-1, 32] \n" + "sai_string_term_size_threshold [8KiB, 1KiB] \n" + @@ -276,6 +277,8 @@ private Set getConfigFieldNames() "partition_size_warn_threshold null \n" + "partition_tombstones_fail_threshold -1 \n" + "partition_tombstones_warn_threshold -1 \n" + + "sai_blob_term_size_fail_threshold 8KiB \n" + + "sai_blob_term_size_warn_threshold 1KiB \n" + "sai_frozen_term_size_fail_threshold 8KiB \n" + "sai_frozen_term_size_warn_threshold 1KiB \n" + "sai_sstable_indexes_per_query_fail_threshold -1 \n" + From 324c8aacb905a079cdcd7b87146581c4e1482dbf Mon Sep 17 00:00:00 2001 From: Terry Moschou <5567550+tmoschou@users.noreply.github.com> Date: Fri, 20 Mar 2026 10:52:43 +1030 Subject: [PATCH 2/2] CASSANDRA-20012: Update CHANGES.txt and SAI FAQ targeting 5.0.7 --- CHANGES.txt | 3 +-- .../cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 111f698949a9..51f04ccc05f7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0.7 + * Add blob type support to SAI (CASSANDRA-20012) * Dynamically skip sharding L0 when SAI Vector index present (CASSANDRA-19661) * Optionally force IndexStatusManager to use the optimized index status format (CASSANDRA-21132) * No need to evict already prepared statements, as it creates a race condition between multiple threads (CASSANDRA-17401) @@ -8266,5 +8267,3 @@ Full list of issues resolved in 0.4 is at https://issues.apache.org/jira/secure/ * Combined blocking and non-blocking versions of insert APIs * Added FlushPeriodInMinutes configuration parameter to force flushing of infrequently-updated ColumnFamilies - - diff --git a/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc b/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc index 0efda32082e4..725d9c8b6cdf 100644 --- a/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc +++ b/doc/modules/cassandra/pages/developing/cql/indexing/sai/sai-faq.adoc @@ -161,10 +161,7 @@ The supported types are: `ASCII, BIGINT, BLOB, BOOLEAN, DATE, DECIMAL, DOUBLE, F [NOTE] ==== -* `INET` support for IPv4 and IPv6 was new starting with Cassandra ???. -* The `DECIMAL` and `VARINT` support was new starting with Cassandra ???. -* The `BOOLEAN` support was new starting with Cassandra ???. -* The `BLOB` support was new starting with Cassandra ???. +* The `BLOB` support was new starting with Cassandra 5.0.7. * SAI also supports collections -- see the xref:#saiCollectionColumnFaq[next FAQ]. ====