mirror of https://github.com/apache/cassandra
Add support for index implementation selection via USING for CREATE INDEX
patch by Caleb Rackliffe; reviewed by Maxwell Guo and Andres de la Peña for CASSANDRA-18615
This commit is contained in:
parent
b57181032d
commit
394d4b1168
|
|
@ -1975,6 +1975,15 @@ drop_compact_storage_enabled: false
|
|||
# if zero_ttl_on_twcs_enabled is set to false, this property is irrelevant as such statements will fail.
|
||||
#zero_ttl_on_twcs_warned: true
|
||||
|
||||
# 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
|
||||
#default_secondary_index: legacy_local_table
|
||||
|
||||
# Whether a default secondary index implementation is allowed. If this is "false", CREATE INDEX must
|
||||
# specify an index implementation via USING.
|
||||
#default_secondary_index_enabled: true
|
||||
|
||||
# Startup Checks are executed as part of Cassandra startup process, not all of them
|
||||
# are configurable (so you can disable them) but these which are enumerated bellow.
|
||||
# Uncomment the startup checks and configure them appropriately to cover your needs.
|
||||
|
|
|
|||
|
|
@ -513,7 +513,8 @@ __Sample:__
|
|||
bc(sample).
|
||||
CREATE INDEX userIndex ON NerdMovies (user);
|
||||
CREATE INDEX ON Mutants (abilityId);
|
||||
CREATE INDEX ON users (keys(favs));
|
||||
CREATE INDEX ON users (KEYS(favs));
|
||||
CREATE INDEX ON users (age) USING 'sai';
|
||||
CREATE CUSTOM INDEX ON users (email) USING 'path.to.the.IndexClass';
|
||||
CREATE CUSTOM INDEX ON users (email) USING 'path.to.the.IndexClass' WITH OPTIONS = {'storage': '/mnt/ssd/indexes/'};
|
||||
|
||||
|
|
@ -521,6 +522,15 @@ The @CREATE INDEX@ statement is used to create a new (automatic) secondary index
|
|||
|
||||
Attempting to create an already existing index will return an error unless the @IF NOT EXISTS@ option is used. If it is used, the statement will be a no-op if the index already exists.
|
||||
|
||||
h4(#usingIndex). Index Types
|
||||
|
||||
The @USING@ keyword optionally specifies an index type. There are two built-in types:
|
||||
|
||||
* legacy_local_table - (default) legacy secondary index, implemented as a hidden local table
|
||||
* sai - "storage-attched" index, implemented via optimized SSTable/Memtable-attached indexes
|
||||
|
||||
To create a custom index, a fully qualified class name must be specified.
|
||||
|
||||
h4(#keysIndex). Indexes on Map Keys
|
||||
|
||||
When creating an index on a "map column":#map, you may index either the keys or the values. If the column identifier is placed within the @keys()@ function, the index will be on the map keys, allowing you to use @CONTAINS KEY@ in @WHERE@ clauses. Otherwise, the index will be on the map values.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
create_index_statement::= CREATE [ CUSTOM ] INDEX [ IF NOT EXISTS ] [ index_name ]
|
||||
ON table_name '(' index_identifier ')'
|
||||
[ USING string [ WITH OPTIONS = map_literal ] ]
|
||||
[ USING index_type [ WITH OPTIONS = map_literal ] ]
|
||||
index_identifier::= column_name
|
||||
| ( KEYS | VALUES | ENTRIES | FULL ) '(' column_name ')'
|
||||
index_type::= 'sai' | 'legacy_local_table' | fully_qualified_class_name
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
CREATE INDEX userIndex ON NerdMovies (user);
|
||||
CREATE INDEX ON Mutants (abilityId);
|
||||
CREATE INDEX ON users (keys(favs));
|
||||
CREATE INDEX ON users (KEYS(favs));
|
||||
CREATE INDEX ON users (age) USING 'sai';
|
||||
CREATE CUSTOM INDEX ON users (email)
|
||||
USING 'path.to.the.IndexClass';
|
||||
CREATE CUSTOM INDEX ON users (email)
|
||||
|
|
|
|||
|
|
@ -853,6 +853,11 @@ The `TRUNCATE` statement permanently removes all data from a table.
|
|||
[[createIndexStmt]]
|
||||
==== CREATE INDEX
|
||||
|
||||
The `CREATE INDEX` statement is used to create a new
|
||||
secondary index for a given (existing) column in a given table. A name
|
||||
for the index itself can be specified before the `ON` keyword, if
|
||||
desired.
|
||||
|
||||
_Syntax:_
|
||||
|
||||
bc(syntax).. +
|
||||
|
|
@ -869,20 +874,26 @@ bc(sample). +
|
|||
CREATE INDEX userIndex ON NerdMovies (user); +
|
||||
CREATE INDEX ON Mutants (abilityId); +
|
||||
CREATE INDEX ON users (keys(favs)); +
|
||||
CREATE INDEX ON users (age) USING 'sai'; +
|
||||
CREATE CUSTOM INDEX ON users (email) USING `path.to.the.IndexClass'; +
|
||||
CREATE CUSTOM INDEX ON users (email) USING `path.to.the.IndexClass' WITH
|
||||
OPTIONS = \{’storage’: `/mnt/ssd/indexes/'};
|
||||
|
||||
The `CREATE INDEX` statement is used to create a new (automatic)
|
||||
secondary index for a given (existing) column in a given table. A name
|
||||
for the index itself can be specified before the `ON` keyword, if
|
||||
desired. If data already exists for the column, it will be indexed
|
||||
If data already exists for the column, it will be indexed
|
||||
asynchronously. After the index is created, new data for the column is
|
||||
indexed automatically at insertion time.
|
||||
indexed automatically at insertion time. Attempting to create an already
|
||||
existing index will return an error unless the `IF NOT EXISTS` option is used.
|
||||
If it is used, the statement will be a no-op if the index already exists.
|
||||
|
||||
Attempting to create an already existing index will return an error
|
||||
unless the `IF NOT EXISTS` option is used. If it is used, the statement
|
||||
will be a no-op if the index already exists.
|
||||
[[indexTypes]]
|
||||
===== Index Types
|
||||
|
||||
The `USING` keyword optionally specifies an index type. There are two built-in types:
|
||||
|
||||
- legacy_local_table - (default) legacy secondary index, implemented as a hidden local table
|
||||
- sai - "storage-attched" index, implemented via optimized SSTable/Memtable-attached indexes
|
||||
|
||||
To create a custom index, a fully qualified class name must be specified.
|
||||
|
||||
[[keysIndex]]
|
||||
===== Indexes on Map Keys
|
||||
|
|
|
|||
|
|
@ -12,30 +12,37 @@ include::example$BNF/index_name.bnf[]
|
|||
[[create-index-statement]]
|
||||
== CREATE INDEX
|
||||
|
||||
Creating a secondary index on a table uses the `CREATE INDEX` statement:
|
||||
The `CREATE INDEX` statement is used to create a new
|
||||
secondary index for a given (existing) column in a given table. A name
|
||||
for the index itself can be specified before the `ON` keyword, if
|
||||
desired.
|
||||
|
||||
[source,bnf]
|
||||
----
|
||||
include::example$BNF/create_index_statement.bnf[]
|
||||
----
|
||||
|
||||
For instance:
|
||||
If data already exists for the column, it will be indexed
|
||||
asynchronously. After the index is created, new data for the column is
|
||||
indexed automatically at insertion time. Attempting to create an already
|
||||
existing index will return an error unless the `IF NOT EXISTS` option is used.
|
||||
If it is used, the statement will be a no-op if the index already exists.
|
||||
|
||||
*Examples*:
|
||||
|
||||
[source,cql]
|
||||
----
|
||||
include::example$CQL/create_index.cql[]
|
||||
----
|
||||
|
||||
The `CREATE INDEX` statement is used to create a new (automatic)
|
||||
secondary index for a given (existing) column in a given table. A name
|
||||
for the index itself can be specified before the `ON` keyword, if
|
||||
desired. If data already exists for the column, it will be indexed
|
||||
asynchronously. After the index is created, new data for the column is
|
||||
indexed automatically at insertion time.
|
||||
=== Index Types
|
||||
|
||||
Attempting to create an already existing index will return an error
|
||||
unless the `IF NOT EXISTS` option is used. If it is used, the statement
|
||||
will be a no-op if the index already exists.
|
||||
The `USING` keyword optionally specifies an index type. There are two built-in types:
|
||||
|
||||
- legacy_local_table - (default) legacy secondary index, implemented as a hidden local table
|
||||
- sai - "storage-attched" index, implemented via optimized SSTable/Memtable-attached indexes
|
||||
|
||||
To create a custom index, a fully qualified class name must be specified.
|
||||
|
||||
=== Indexes on Map Keys
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
|
|||
import org.apache.cassandra.audit.AuditLogOptions;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.fql.FullQueryLoggerOptions;
|
||||
import org.apache.cassandra.index.internal.CassandraIndex;
|
||||
import org.apache.cassandra.io.sstable.format.big.BigFormat;
|
||||
import org.apache.cassandra.service.StartupChecks.StartupCheckType;
|
||||
import org.apache.cassandra.utils.StorageCompatibilityMode;
|
||||
|
|
@ -49,7 +50,7 @@ import static org.apache.cassandra.config.CassandraRelevantProperties.SKIP_PAXOS
|
|||
|
||||
/**
|
||||
* A class that contains configuration properties for the cassandra node it runs within.
|
||||
*
|
||||
* <p>
|
||||
* Properties declared as volatile can be mutated via JMX.
|
||||
*/
|
||||
public class Config
|
||||
|
|
@ -864,6 +865,10 @@ public class Config
|
|||
public volatile boolean drop_truncate_table_enabled = true;
|
||||
public volatile boolean drop_keyspace_enabled = true;
|
||||
public volatile boolean secondary_indexes_enabled = true;
|
||||
|
||||
public volatile String default_secondary_index = CassandraIndex.NAME;
|
||||
public volatile boolean default_secondary_index_enabled = true;
|
||||
|
||||
public volatile boolean uncompressed_tables_enabled = true;
|
||||
public volatile boolean compact_tables_enabled = true;
|
||||
public volatile boolean read_before_write_list_operations_enabled = true;
|
||||
|
|
|
|||
|
|
@ -3765,6 +3765,26 @@ public class DatabaseDescriptor
|
|||
conf.sasi_indexes_enabled = enableSASIIndexes;
|
||||
}
|
||||
|
||||
public static String getDefaultSecondaryIndex()
|
||||
{
|
||||
return conf.default_secondary_index;
|
||||
}
|
||||
|
||||
public static void setDefaultSecondaryIndex(String name)
|
||||
{
|
||||
conf.default_secondary_index = name;
|
||||
}
|
||||
|
||||
public static boolean getDefaultSecondaryIndexEnabled()
|
||||
{
|
||||
return conf.default_secondary_index_enabled;
|
||||
}
|
||||
|
||||
public static void setDefaultSecondaryIndexEnabled(boolean enabled)
|
||||
{
|
||||
conf.default_secondary_index_enabled = enabled;
|
||||
}
|
||||
|
||||
public static boolean isTransientReplicationEnabled()
|
||||
{
|
||||
return conf.transient_replication_enabled;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import org.apache.cassandra.db.Keyspace;
|
|||
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||
import org.apache.cassandra.db.marshal.MapType;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.index.internal.CassandraIndex;
|
||||
import org.apache.cassandra.index.sasi.SASIIndex;
|
||||
import org.apache.cassandra.schema.*;
|
||||
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
|
||||
|
|
@ -77,6 +78,7 @@ public final class CreateIndexStatement extends AlterSchemaStatement
|
|||
public static final String INDEX_DUPLICATE_OF_EXISTING = "Index %s is a duplicate of existing index %s";
|
||||
public static final String KEYSPACE_DOES_NOT_MATCH_TABLE = "Keyspace name '%s' doesn't match table name '%s'";
|
||||
public static final String KEYSPACE_DOES_NOT_MATCH_INDEX = "Keyspace name '%s' doesn't match index name '%s'";
|
||||
public static final String MUST_SPECIFY_INDEX_IMPLEMENTATION = "Must specify index implementation via USING";
|
||||
|
||||
private final String indexName;
|
||||
private final String tableName;
|
||||
|
|
@ -316,6 +318,27 @@ public final class CreateIndexStatement extends AlterSchemaStatement
|
|||
|
||||
if (indexName.hasKeyspace() && !keyspaceName.equals(indexName.getKeyspace()))
|
||||
throw ire(KEYSPACE_DOES_NOT_MATCH_INDEX, keyspaceName, tableName);
|
||||
|
||||
// Set the configured default 2i implementation if one isn't specified with USING:
|
||||
if (attrs.customClass == null)
|
||||
{
|
||||
if (DatabaseDescriptor.getDefaultSecondaryIndexEnabled())
|
||||
attrs.customClass = DatabaseDescriptor.getDefaultSecondaryIndex();
|
||||
else
|
||||
// However, operators may require an implementation be specified
|
||||
throw ire(MUST_SPECIFY_INDEX_IMPLEMENTATION);
|
||||
}
|
||||
|
||||
// If we explicitly specify the index type "legacy_local_table", we can just clear the custom class, and the
|
||||
// non-custom 2i creation process will begin. Otherwise, if an index type has been specified with
|
||||
// USING, make sure the appropriate custom index is created.
|
||||
if (attrs.customClass != null)
|
||||
{
|
||||
if (!attrs.isCustom && attrs.customClass.equalsIgnoreCase(CassandraIndex.NAME))
|
||||
attrs.customClass = null;
|
||||
else
|
||||
attrs.isCustom = true;
|
||||
}
|
||||
|
||||
return new CreateIndexStatement(keyspaceName, tableName.getName(), indexName.getName(), rawIndexTargets, attrs, ifNotExists);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse
|
|||
*/
|
||||
public abstract class CassandraIndex implements Index
|
||||
{
|
||||
public static final String NAME = "legacy_local_table";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CassandraIndex.class);
|
||||
|
||||
public final ColumnFamilyStore baseCfs;
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ Follow the instructions to build and start Cassandra in README.asc in root folde
|
|||
|
||||
`CREATE TABLE person (id int, name text, age int, PRIMARY KEY (id));`
|
||||
|
||||
`CREATE CUSTOM INDEX ON person (name) USING 'StorageAttachedIndex' WITH OPTIONS = {'case_sensitive': false};`
|
||||
`CREATE INDEX ON person (name) USING 'sai' WITH OPTIONS = {'case_sensitive': false};`
|
||||
|
||||
`CREATE CUSTOM INDEX ON person (age) USING 'StorageAttachedIndex';`
|
||||
`CREATE INDEX ON person (age) USING 'sai';`
|
||||
|
||||
2.) Add some data.
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ import org.apache.cassandra.utils.concurrent.OpOrder;
|
|||
|
||||
public class StorageAttachedIndex implements Index
|
||||
{
|
||||
public static final String NAME = "sai";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StorageAttachedIndex.class);
|
||||
|
||||
private static class StorageAttachedIndexBuildingSupport implements IndexBuildingSupport
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ public final class IndexMetadata
|
|||
|
||||
static
|
||||
{
|
||||
indexNameAliases.put(StorageAttachedIndex.class.getSimpleName(), StorageAttachedIndex.class.getCanonicalName());
|
||||
indexNameAliases.put(StorageAttachedIndex.NAME, StorageAttachedIndex.class.getCanonicalName());
|
||||
indexNameAliases.put(StorageAttachedIndex.class.getSimpleName().toLowerCase(), StorageAttachedIndex.class.getCanonicalName());
|
||||
indexNameAliases.put(SASIIndex.class.getSimpleName(), SASIIndex.class.getCanonicalName());
|
||||
}
|
||||
|
||||
|
|
@ -154,7 +155,7 @@ public final class IndexMetadata
|
|||
if (isCustom())
|
||||
{
|
||||
String className = options.get(IndexTarget.CUSTOM_INDEX_OPTION_NAME);
|
||||
return indexNameAliases.getOrDefault(className, className);
|
||||
return indexNameAliases.getOrDefault(className.toLowerCase(), className);
|
||||
}
|
||||
return CassandraIndex.class.getName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,12 @@ import java.util.concurrent.CountDownLatch;
|
|||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.index.internal.CassandraIndex;
|
||||
import org.apache.cassandra.index.sai.StorageAttachedIndex;
|
||||
import org.apache.cassandra.schema.ColumnMetadata;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
|
|
@ -70,6 +73,12 @@ import static org.junit.Assert.fail;
|
|||
public class SecondaryIndexTest extends CQLTester
|
||||
{
|
||||
public static final int TOO_BIG = 1024 * 65;
|
||||
|
||||
@BeforeClass
|
||||
public static void setDefaultSecondaryIndex()
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndex(CassandraIndex.NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndDropIndex() throws Throwable
|
||||
|
|
@ -161,6 +170,43 @@ public class SecondaryIndexTest extends CQLTester
|
|||
return StringUtils.remove(indexName, '\"');
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCassandraIndexExplicitly() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (userid uuid PRIMARY KEY, firstname text, lastname text, age int)");
|
||||
createIndex("CREATE INDEX byAge ON %s(age) USING 'legacy_local_table'");
|
||||
|
||||
SecondaryIndexManager indexManager = getCurrentColumnFamilyStore().indexManager;
|
||||
assertTrue(indexManager.getIndexByName("byage") instanceof CassandraIndex);
|
||||
|
||||
UUID id1 = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||
execute("INSERT INTO %s (userid, firstname, lastname, age) VALUES (?, 'Frodo', 'Baggins', 32)", id1);
|
||||
assertEmpty(execute("SELECT firstname FROM %s WHERE userid = ? AND age = 33", id1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateCassandraIndexWhenNotDefault() throws Throwable
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndex(StorageAttachedIndex.NAME);
|
||||
|
||||
try
|
||||
{
|
||||
createTable("CREATE TABLE %s (userid uuid PRIMARY KEY, firstname text, lastname text, age int)");
|
||||
createIndex("CREATE INDEX byAge ON %s(age) USING 'legacy_local_table'");
|
||||
|
||||
SecondaryIndexManager indexManager = getCurrentColumnFamilyStore().indexManager;
|
||||
assertTrue(indexManager.getIndexByName("byage") instanceof CassandraIndex);
|
||||
|
||||
UUID id1 = UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
|
||||
execute("INSERT INTO %s (userid, firstname, lastname, age) VALUES (?, 'Frodo', 'Baggins', 32)", id1);
|
||||
assertEmpty(execute("SELECT firstname FROM %s WHERE userid = ? AND age = 33", id1));
|
||||
}
|
||||
finally
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndex(CassandraIndex.NAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that you can query for an indexed column even with a key EQ clause,
|
||||
* migrated from cql_tests.py:TestCQL.static_cf_test()
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ public abstract class SAITester extends CQLTester
|
|||
|
||||
protected static final String CREATE_TABLE_TEMPLATE = "CREATE TABLE %s (id1 TEXT PRIMARY KEY, v1 INT, v2 TEXT) WITH compaction = " +
|
||||
"{'class' : 'SizeTieredCompactionStrategy', 'enabled' : false }";
|
||||
protected static final String CREATE_INDEX_TEMPLATE = "CREATE CUSTOM INDEX IF NOT EXISTS ON %%s(%s) USING 'StorageAttachedIndex'";
|
||||
protected static final String CREATE_INDEX_TEMPLATE = "CREATE INDEX IF NOT EXISTS ON %%s(%s) USING 'sai'";
|
||||
|
||||
protected static final ColumnIdentifier V1_COLUMN_IDENTIFIER = ColumnIdentifier.getInterned("v1", true);
|
||||
protected static final ColumnIdentifier V2_COLUMN_IDENTIFIER = ColumnIdentifier.getInterned("v2", true);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ import org.apache.cassandra.index.sai.SAITester;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
// This test is primarily handling edge conditions, error conditions
|
||||
// and basic functionality. Comprehensive type testing of collections
|
||||
// is in the cql/types/collections package
|
||||
/**
|
||||
* This test is primarily handling edge conditions, error conditions
|
||||
* and basic functionality. Comprehensive type testing of collections
|
||||
* is in the cql/types/collections package
|
||||
*/
|
||||
public class CollectionIndexingTest extends SAITester
|
||||
{
|
||||
@Before
|
||||
|
|
@ -42,28 +44,28 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexMap() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(value) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("value"));
|
||||
assertEquals(2, execute("SELECT * FROM %s WHERE value CONTAINS 'v1'").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapKeys() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(KEYS(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("KEYS(value)"));
|
||||
assertEquals(2, execute("SELECT * FROM %s WHERE value CONTAINS KEY 1").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapValues() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(VALUES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("VALUES(value)"));
|
||||
assertEquals(2, execute("SELECT * FROM %s WHERE value CONTAINS 'v1'").size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapEntries() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(ENTRIES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("ENTRIES(value)"));
|
||||
assertEquals(2, execute("SELECT * FROM %s WHERE value[1] = 'v1'").size());
|
||||
assertEquals(1, execute("SELECT * FROM %s WHERE value[1] = 'v1' AND value[2] = 'v2'").size());
|
||||
}
|
||||
|
|
@ -71,14 +73,14 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexFrozenList() throws Throwable
|
||||
{
|
||||
createPopulatedFrozenList("CREATE CUSTOM INDEX ON %s(FULL(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedFrozenList(createIndexDDL("FULL(value)"));
|
||||
assertEquals(2, execute("SELECT * FROM %s WHERE value = ?", Arrays.asList(1, 2, 3)).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexFrozenMap() throws Throwable
|
||||
{
|
||||
createPopulatedFrozenMap("CREATE CUSTOM INDEX ON %s(FULL(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedFrozenMap(createIndexDDL("FULL(value)"));
|
||||
assertEquals(1, execute("SELECT * FROM %s WHERE value = ?", new HashMap<Integer, String>() {{
|
||||
put(1, "v1");
|
||||
put(2, "v2");
|
||||
|
|
@ -89,21 +91,21 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexFrozenMapQueryKeys() throws Throwable
|
||||
{
|
||||
createPopulatedFrozenMap("CREATE CUSTOM INDEX ON %s(FULL(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedFrozenMap(createIndexDDL("FULL(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value contains key 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexFrozenMapQueryValues() throws Throwable
|
||||
{
|
||||
createPopulatedFrozenMap("CREATE CUSTOM INDEX ON %s(FULL(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedFrozenMap(createIndexDDL("FULL(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value contains 'v1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexFrozenMapQueryEntries() throws Throwable
|
||||
{
|
||||
createPopulatedFrozenMap("CREATE CUSTOM INDEX ON %s(FULL(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedFrozenMap(createIndexDDL("FULL(value)"));
|
||||
assertInvalidMessage("Map-entry equality predicates on frozen map column value are not supported",
|
||||
"SELECT * FROM %s WHERE value[1] = 'v1'");
|
||||
}
|
||||
|
|
@ -111,7 +113,7 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexMapEntriesQueryEq() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(ENTRIES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("ENTRIES(value)"));
|
||||
assertInvalidMessage("Collection column 'value' (map<int, text>) cannot be restricted by a '=' relation",
|
||||
"SELECT * FROM %s WHERE value = ?", Arrays.asList(1, 2));
|
||||
}
|
||||
|
|
@ -119,21 +121,21 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexMapEntriesQueryKeys() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(ENTRIES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("ENTRIES(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value contains key 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapEntriesQueryValues() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(ENTRIES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("ENTRIES(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value contains 'v1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapKeysQueryEq() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(KEYS(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("KEYS(value)"));
|
||||
assertInvalidMessage("Collection column 'value' (map<int, text>) cannot be restricted by a '=' relation",
|
||||
"SELECT * FROM %s WHERE value = ?", Arrays.asList(1, 2));
|
||||
}
|
||||
|
|
@ -141,21 +143,21 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexMapKeysQueryValues() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(KEYS(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("KEYS(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value contains 'v1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapKeysQueryEntries() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(KEYS(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("KEYS(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value[1] = 'v1'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapValuesQueryEq() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(VALUES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("VALUES(value)"));
|
||||
assertInvalidMessage("Collection column 'value' (map<int, text>) cannot be restricted by a '=' relation",
|
||||
"SELECT * FROM %s WHERE value = ?", Arrays.asList(1, 2));
|
||||
}
|
||||
|
|
@ -163,18 +165,18 @@ public class CollectionIndexingTest extends SAITester
|
|||
@Test
|
||||
public void indexMapValuesQueryKeys() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(VALUES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("VALUES(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value contains key 1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexMapValuesQueryEntries() throws Throwable
|
||||
{
|
||||
createPopulatedMap("CREATE CUSTOM INDEX ON %s(VALUES(value)) USING 'StorageAttachedIndex'");
|
||||
createPopulatedMap(createIndexDDL("VALUES(value)"));
|
||||
assertUnsupportedIndexOperator(2, "SELECT * FROM %s WHERE value[1] = 'v1'");
|
||||
}
|
||||
|
||||
private void createPopulatedMap(String createIndex) throws Throwable
|
||||
private void createPopulatedMap(String createIndex)
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int primary key, value map<int, text>)");
|
||||
createIndex(createIndex);
|
||||
|
|
@ -204,7 +206,7 @@ public class CollectionIndexingTest extends SAITester
|
|||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void createPopulatedFrozenList(String createIndex) throws Throwable
|
||||
private void createPopulatedFrozenList(String createIndex)
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int primary key, value frozen<list<int>>)");
|
||||
createIndex(createIndex);
|
||||
|
|
@ -220,4 +222,9 @@ public class CollectionIndexingTest extends SAITester
|
|||
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE, query, values);
|
||||
assertEquals(expectedSize, execute(query + " ALLOW FILTERING").size());
|
||||
}
|
||||
|
||||
private static String createIndexDDL(String target)
|
||||
{
|
||||
return "CREATE INDEX ON %s(" + target + ") USING 'sai'";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,20 +30,20 @@ public class CompositePartitionKeyIndexTest extends SAITester
|
|||
public void createTableAndIndex()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk1 int, pk2 text, val int, PRIMARY KEY((pk1, pk2)))");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(pk1) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(pk2) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX ON %s(pk1) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(pk2) USING 'sai'");
|
||||
|
||||
disableCompaction();
|
||||
}
|
||||
|
||||
private void insertData1() throws Throwable
|
||||
private void insertData1()
|
||||
{
|
||||
execute("INSERT INTO %s (pk1, pk2, val) VALUES (1, '1', 1)");
|
||||
execute("INSERT INTO %s (pk1, pk2, val) VALUES (2, '2', 2)");
|
||||
execute("INSERT INTO %s (pk1, pk2, val) VALUES (3, '3', 3)");
|
||||
}
|
||||
|
||||
private void insertData2() throws Throwable
|
||||
private void insertData2()
|
||||
{
|
||||
execute("INSERT INTO %s (pk1, pk2, val) VALUES (4, '4', 4)");
|
||||
execute("INSERT INTO %s (pk1, pk2, val) VALUES (5, '5', 5)");
|
||||
|
|
@ -51,7 +51,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void queryFromMemtable() throws Throwable
|
||||
public void queryFromMemtable()
|
||||
{
|
||||
insertData1();
|
||||
insertData2();
|
||||
|
|
@ -62,7 +62,7 @@ public class CompositePartitionKeyIndexTest extends SAITester
|
|||
return row(index, Integer.toString(index), index);
|
||||
}
|
||||
|
||||
private void runQueries() throws Throwable
|
||||
private void runQueries()
|
||||
{
|
||||
assertRowsIgnoringOrder(execute("SELECT * FROM %s WHERE pk1 = 2"),
|
||||
expectedRow(2));
|
||||
|
|
|
|||
|
|
@ -25,26 +25,26 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
public class MultipleColumnIndexTest extends SAITester
|
||||
{
|
||||
// Note: Full testing of multiple map index types is done in the
|
||||
// types/collections/maps/MultiMap*Test tests
|
||||
// This is just testing that the indexes can be created
|
||||
@Test
|
||||
public void canCreateMultipleMapIndexesOnSameColumn()
|
||||
{
|
||||
// Note: Full testing of multiple map index types is done in the
|
||||
// types/collections/maps/MultiMap*Test tests
|
||||
// This is just testing that the indexes can be created
|
||||
createTable("CREATE TABLE %s (pk int, ck int, value map<int,int>, PRIMARY KEY(pk, ck))");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(KEYS(value)) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(VALUES(value)) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(ENTRIES(value)) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX ON %s(KEYS(value)) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(VALUES(value)) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(ENTRIES(value)) USING 'sai'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indexNamedAsColumnWillCoExistWithGeneratedIndexNames() throws Throwable
|
||||
public void indexNamedAsColumnWillCoExistWithGeneratedIndexNames()
|
||||
{
|
||||
createTable("CREATE TABLE %s(id int PRIMARY KEY, text_map map<text, text>)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX text_map ON %s(keys(text_map)) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(values(text_map)) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(entries(text_map)) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX text_map ON %s(keys(text_map)) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(values(text_map)) USING 'sai'");
|
||||
createIndex("CREATE INDEX ON %s(entries(text_map)) USING 'sai'");
|
||||
|
||||
execute("INSERT INTO %s(id, text_map) values (1, {'k1':'v1', 'k2':'v2'})");
|
||||
execute("INSERT INTO %s(id, text_map) values (2, {'k1':'v1', 'k3':'v3'})");
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.datastax.driver.core.ResultSet;
|
|||
import com.datastax.driver.core.exceptions.InvalidConfigurationInQueryException;
|
||||
import com.datastax.driver.core.exceptions.InvalidQueryException;
|
||||
import com.datastax.driver.core.exceptions.ReadFailureException;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.CQL3Type;
|
||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||
import org.apache.cassandra.cql3.restrictions.IndexRestrictions;
|
||||
|
|
@ -50,6 +51,7 @@ import org.apache.cassandra.db.marshal.AbstractType;
|
|||
import org.apache.cassandra.db.marshal.Int32Type;
|
||||
import org.apache.cassandra.db.marshal.ReversedType;
|
||||
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.index.Index;
|
||||
import org.apache.cassandra.index.SecondaryIndexManager;
|
||||
import org.apache.cassandra.index.sai.IndexContext;
|
||||
|
|
@ -145,7 +147,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
|
||||
try
|
||||
{
|
||||
executeNet(String.format("CREATE CUSTOM INDEX ON %%s(%s) USING 'StorageAttachedIndex'", cql3Type));
|
||||
executeNet(String.format("CREATE INDEX ON %%s(%s) USING 'sai'", cql3Type));
|
||||
assertTrue("Index creation on unsupported type " + cql3Type + " should have failed.", supported);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
|
|
@ -161,7 +163,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
public void shouldFailCreationOnPartitionKey()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(id) USING 'StorageAttachedIndex'"))
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(id) USING 'sai'"))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining(String.format(CreateIndexStatement.ONLY_PARTITION_KEY, "id"));
|
||||
}
|
||||
|
|
@ -171,8 +173,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val) USING " +
|
||||
"'StorageAttachedIndex' WITH OPTIONS = { 'mode' : 'CONTAINS' }")).isInstanceOf(InvalidConfigurationInQueryException.class);
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val) USING 'sai' " +
|
||||
"WITH OPTIONS = { 'mode' : 'CONTAINS' }")).isInstanceOf(InvalidConfigurationInQueryException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -180,8 +182,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val) " +
|
||||
"USING 'StorageAttachedIndex' " +
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val) " +
|
||||
"USING 'sai' " +
|
||||
"WITH OPTIONS = { 'analyzer_class' : 'org.apache.cassandra.index.sai.analyzer.NonTokenizingAnalyzer' }"))
|
||||
.isInstanceOf(InvalidConfigurationInQueryException.class);
|
||||
}
|
||||
|
|
@ -191,8 +193,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val) " +
|
||||
"USING 'StorageAttachedIndex' " +
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val) " +
|
||||
"USING 'sai' " +
|
||||
"WITH OPTIONS = { 'case-sensitive' : true }")).isInstanceOf(InvalidConfigurationInQueryException.class);
|
||||
}
|
||||
|
||||
|
|
@ -201,8 +203,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val int)");
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val) " +
|
||||
"USING 'StorageAttachedIndex' " +
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val) " +
|
||||
"USING 'sai' " +
|
||||
"WITH OPTIONS = { 'case_sensitive' : true }")).isInstanceOf(InvalidQueryException.class);
|
||||
}
|
||||
|
||||
|
|
@ -211,8 +213,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val int)");
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val) " +
|
||||
"USING 'StorageAttachedIndex' " +
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val) " +
|
||||
"USING 'sai' " +
|
||||
"WITH OPTIONS = { 'normalize' : true }")).isInstanceOf(InvalidQueryException.class);
|
||||
}
|
||||
|
||||
|
|
@ -222,8 +224,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
String typeName = createType("CREATE TYPE %s (a text, b int, c double)");
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val " + typeName + ')');
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val) " +
|
||||
"USING 'StorageAttachedIndex'")).isInstanceOf(InvalidQueryException.class);
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val) " +
|
||||
"USING 'sai'")).isInstanceOf(InvalidQueryException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -231,7 +233,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val tuple<text, int, double>)");
|
||||
|
||||
executeNet("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex'");
|
||||
executeNet("CREATE INDEX ON %s(val) USING 'sai'");
|
||||
|
||||
TableMetadata metadata = currentTableMetadata();
|
||||
AbstractType<?> tuple = metadata.getColumn(ColumnIdentifier.getInterned("val", false)).type;
|
||||
|
|
@ -246,8 +248,8 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
String invalidColumn = "/invalid";
|
||||
createTable(String.format("CREATE TABLE %%s (id text PRIMARY KEY, \"%s\" text)", invalidColumn));
|
||||
|
||||
assertThatThrownBy(() -> executeNet(String.format("CREATE CUSTOM INDEX ON %%s(\"%s\")" +
|
||||
" USING 'StorageAttachedIndex'", invalidColumn)))
|
||||
assertThatThrownBy(() -> executeNet(String.format("CREATE INDEX ON %%s(\"%s\")" +
|
||||
" USING 'sai'", invalidColumn)))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessage(String.format(CreateIndexStatement.INVALID_CUSTOM_INDEX_TARGET, invalidColumn, SchemaConstants.NAME_LENGTH));
|
||||
}
|
||||
|
|
@ -256,20 +258,65 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
public void shouldCreateIndexIfExists()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX IF NOT EXISTS ON %s(val) USING 'StorageAttachedIndex' ");
|
||||
|
||||
createIndexAsync("CREATE CUSTOM INDEX IF NOT EXISTS ON %s(val) USING 'StorageAttachedIndex' ");
|
||||
createIndex("CREATE INDEX IF NOT EXISTS ON %s(val) USING 'sai' ");
|
||||
createIndexAsync("CREATE INDEX IF NOT EXISTS ON %s(val) USING 'sai' ");
|
||||
|
||||
assertEquals(1, saiCreationCounter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIndexCaseInsensitive()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val1 text, val2 text)");
|
||||
createIndex("CREATE INDEX mixed_case_val ON %s(val1) USING 'Sai' ");
|
||||
createIndex("CREATE INDEX upper_case_val ON %s(val2) USING 'SAI' ");
|
||||
|
||||
assertEquals(2, saiCreationCounter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIndexWithClassName()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'StorageAttachedIndex' ");
|
||||
assertEquals(1, saiCreationCounter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIndexWithDefault()
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndex(StorageAttachedIndex.NAME);
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
createIndex("CREATE INDEX ON %s(val)");
|
||||
assertEquals(1, saiCreationCounter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailWithDefaultIndexDisabled()
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndex(StorageAttachedIndex.NAME);
|
||||
boolean original = DatabaseDescriptor.getDefaultSecondaryIndexEnabled();
|
||||
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndexEnabled(false);
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
assertThatThrownBy(() -> createIndex("CREATE INDEX ON %s(val)")).hasRootCauseInstanceOf(InvalidRequestException.class)
|
||||
.hasRootCauseMessage(CreateIndexStatement.MUST_SPECIFY_INDEX_IMPLEMENTATION);
|
||||
assertEquals(0, saiCreationCounter.get());
|
||||
}
|
||||
finally
|
||||
{
|
||||
DatabaseDescriptor.setDefaultSecondaryIndexEnabled(original);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBeCaseSensitiveByDefault()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai'");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Camel')");
|
||||
|
||||
|
|
@ -283,7 +330,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex' WITH OPTIONS = { 'case_sensitive' : true }");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai' WITH OPTIONS = { 'case_sensitive' : true }");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Camel')");
|
||||
|
||||
|
|
@ -297,7 +344,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex' WITH OPTIONS = { 'case_sensitive' : false }");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai' WITH OPTIONS = { 'case_sensitive' : false }");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Camel')");
|
||||
|
||||
|
|
@ -309,7 +356,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai'");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Cam\u00E1l')");
|
||||
|
||||
|
|
@ -324,7 +371,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex' WITH OPTIONS = { 'normalize' : false }");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai' WITH OPTIONS = { 'normalize' : false }");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Cam\u00E1l')");
|
||||
|
||||
|
|
@ -339,7 +386,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex' WITH OPTIONS = { 'normalize' : true }");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai' WITH OPTIONS = { 'normalize' : true }");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Cam\u00E1l')");
|
||||
|
||||
|
|
@ -351,7 +398,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex' WITH OPTIONS = { 'normalize' : true, 'case_sensitive' : false}");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai' WITH OPTIONS = { 'normalize' : true, 'case_sensitive' : false}");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Cam\u00E1l')");
|
||||
|
||||
|
|
@ -363,7 +410,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex' WITH OPTIONS = { 'ascii' : true, 'case_sensitive' : false}");
|
||||
createIndex("CREATE INDEX ON %s(val) USING 'sai' WITH OPTIONS = { 'ascii' : true, 'case_sensitive' : false}");
|
||||
|
||||
execute("INSERT INTO %s (id, val) VALUES ('1', 'Éppinger')");
|
||||
|
||||
|
|
@ -371,11 +418,11 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIndexOnReversedType() throws Throwable
|
||||
public void shouldCreateIndexOnReversedType()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text, ck1 text, val text, PRIMARY KEY (id,ck1)) WITH CLUSTERING ORDER BY (ck1 desc)");
|
||||
|
||||
String indexNameCk1 = createIndex("CREATE CUSTOM INDEX ON %s(ck1) USING 'StorageAttachedIndex'");
|
||||
String indexNameCk1 = createIndex("CREATE INDEX ON %s(ck1) USING 'sai'");
|
||||
|
||||
execute("insert into %s(id, ck1, val) values('1', '2', '3')");
|
||||
execute("insert into %s(id, ck1, val) values('1', '3', '4')");
|
||||
|
|
@ -392,11 +439,10 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateIndexWithAlias()
|
||||
public void shouldCreateIndexWithFullClassName()
|
||||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val text)");
|
||||
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'org.apache.cassandra.index.sai.StorageAttachedIndex'");
|
||||
|
||||
assertEquals(1, saiCreationCounter.get());
|
||||
}
|
||||
|
|
@ -406,7 +452,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
* Not putting in {@link MixedIndexImplementationsTest} because it uses CQLTester which doesn't load SAI dependency.
|
||||
*/
|
||||
@Test
|
||||
public void shouldCreateSASI() throws Throwable
|
||||
public void shouldCreateSASI()
|
||||
{
|
||||
createTable(CREATE_TABLE_TEMPLATE);
|
||||
|
||||
|
|
@ -433,7 +479,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
{
|
||||
createTable("CREATE TABLE %s (id text PRIMARY KEY, val1 text, val2 text)");
|
||||
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(val1, val2) USING 'StorageAttachedIndex'"))
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(val1, val2) USING 'sai'"))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining("storage-attached index cannot be created over multiple columns");
|
||||
}
|
||||
|
|
@ -445,21 +491,21 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
execute("INSERT INTO %s (id, v1) VALUES(1, '1')");
|
||||
flush();
|
||||
|
||||
executeNet("CREATE CUSTOM INDEX index_1 ON %s(v1) USING 'StorageAttachedIndex'");
|
||||
executeNet("CREATE INDEX index_1 ON %s(v1) USING 'sai'");
|
||||
waitForTableIndexesQueryable();
|
||||
|
||||
// same name
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX index_1 ON %s(v1) USING 'StorageAttachedIndex'"))
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX index_1 ON %s(v1) USING 'sai'"))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining(String.format(CreateIndexStatement.INDEX_ALREADY_EXISTS, "index_1"));
|
||||
|
||||
// different name, same option
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX index_2 ON %s(v1) USING 'StorageAttachedIndex'"))
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX index_2 ON %s(v1) USING 'sai'"))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining(String.format(CreateIndexStatement.INDEX_DUPLICATE_OF_EXISTING, "index_2", "index_1"));
|
||||
|
||||
// different name, different option, same target.
|
||||
assertThatThrownBy(() -> executeNet("CREATE CUSTOM INDEX ON %s(v1) USING 'StorageAttachedIndex' WITH OPTIONS = { 'case_sensitive' : true }"))
|
||||
assertThatThrownBy(() -> executeNet("CREATE INDEX ON %s(v1) USING 'sai' WITH OPTIONS = { 'case_sensitive' : true }"))
|
||||
.isInstanceOf(InvalidQueryException.class)
|
||||
.hasMessageContaining("Cannot create more than one storage-attached index on the same column: v1" );
|
||||
|
||||
|
|
@ -496,7 +542,7 @@ public class StorageAttachedIndexDDLTest extends SAITester
|
|||
|
||||
// Create the index, but do not allow the initial index build to begin:
|
||||
Injections.inject(delayInitializationTask);
|
||||
createIndexAsync("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex'");
|
||||
createIndexAsync("CREATE INDEX ON %s(val) USING 'sai'");
|
||||
|
||||
// Flush the Memtable's contents, which will feed data to the index as the SSTable is written:
|
||||
flush();
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public abstract class IndexingTypeSupport extends SAITester
|
|||
if (scenario != Scenario.POST_BUILD_QUERY)
|
||||
{
|
||||
for (String index : dataset.decorateIndexColumn("value"))
|
||||
createIndex(String.format("CREATE CUSTOM INDEX ON %%s(%s) USING 'StorageAttachedIndex'", index));
|
||||
createIndex(String.format("CREATE INDEX ON %%s(%s) USING 'sai'", index));
|
||||
}
|
||||
|
||||
insertData(this, allRows, scenario);
|
||||
|
|
@ -104,14 +104,14 @@ public abstract class IndexingTypeSupport extends SAITester
|
|||
case POST_BUILD_QUERY:
|
||||
flush();
|
||||
for (String index : dataset.decorateIndexColumn("value"))
|
||||
createIndex(String.format("CREATE CUSTOM INDEX ON %%s(%s) USING 'StorageAttachedIndex'", index));
|
||||
createIndex(String.format("CREATE INDEX ON %%s(%s) USING 'sai'", index));
|
||||
break;
|
||||
}
|
||||
|
||||
dataset.querySet().runQueries(this, allRows);
|
||||
}
|
||||
|
||||
public static void insertData(SAITester tester, Object[][] allRows, Scenario scenario) throws Throwable
|
||||
public static void insertData(SAITester tester, Object[][] allRows, Scenario scenario)
|
||||
{
|
||||
int sstableCounter = 0;
|
||||
int sstableIncrement = NUMBER_OF_VALUES / 8;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class GroupComponentsTest extends SAITester
|
|||
public void testInvalidateWithoutObsolete()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int primary key, value text)");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(value) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX ON %s(value) USING 'sai'");
|
||||
execute("INSERT INTO %s (pk) VALUES (1)");
|
||||
flush();
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ public class GroupComponentsTest extends SAITester
|
|||
public void getLiveComponentsForEmptyIndex()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int primary key, value text)");
|
||||
createIndex("CREATE CUSTOM INDEX ON %s(value) USING 'StorageAttachedIndex'");
|
||||
createIndex("CREATE INDEX ON %s(value) USING 'sai'");
|
||||
execute("INSERT INTO %s (pk) VALUES (1)");
|
||||
flush();
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ public class GroupComponentsTest extends SAITester
|
|||
public void getLiveComponentsForPopulatedIndex()
|
||||
{
|
||||
createTable("CREATE TABLE %s (pk int primary key, value text)");
|
||||
IndexContext indexContext = createIndexContext(createIndex("CREATE CUSTOM INDEX ON %s(value) USING 'StorageAttachedIndex'"), UTF8Type.instance);
|
||||
IndexContext indexContext = createIndexContext(createIndex("CREATE INDEX ON %s(value) USING 'sai'"), UTF8Type.instance);
|
||||
execute("INSERT INTO %s (pk, value) VALUES (1, '1')");
|
||||
flush();
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public class SSTablesSystemViewTest extends SAITester
|
|||
public void testVirtualTableThroughIndexLifeCycle() throws Throwable
|
||||
{
|
||||
createTable("CREATE TABLE %s (k text, c text, v1 text, v2 text, PRIMARY KEY (k, c))");
|
||||
String v1IndexName = createIndex("CREATE CUSTOM INDEX ON %s(v1) USING 'StorageAttachedIndex'");
|
||||
String v1IndexName = createIndex("CREATE INDEX ON %s(v1) USING 'sai'");
|
||||
|
||||
String insert = "INSERT INTO %s(k, c, v1, v2) VALUES (?, ?, ?, ?)";
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ public class SSTablesSystemViewTest extends SAITester
|
|||
assertRowsIgnoringOrder(execute(SELECT), row1, row2);
|
||||
|
||||
// create a second index, this should create a new additional entry in the table for each sstable
|
||||
String v2IndexName = createIndex("CREATE CUSTOM INDEX ON %s(v2) USING 'StorageAttachedIndex'");
|
||||
String v2IndexName = createIndex("CREATE INDEX ON %s(v2) USING 'sai'");
|
||||
Object[] row3 = readRow(v2IndexName, id1, "v2", 1L, 0L, 0L);
|
||||
Object[] row4 = readRow(v2IndexName, id2, "v2", 2L, 0L, 1L);
|
||||
assertRowsIgnoringOrder(execute(SELECT), row1, row2, row3, row4);
|
||||
|
|
|
|||
Loading…
Reference in New Issue