mirror of https://github.com/apache/cassandra
Allow specifying custom secondary index options in CQL3
patch by Aleksey Yeschenko; reviewed by Sylvain Lebresne for CASSANDRA-6480
This commit is contained in:
parent
ae0a1e0f5a
commit
5fa6055107
|
|
@ -1,5 +1,6 @@
|
|||
2.0.5
|
||||
* Delete unfinished compaction incrementally (CASSANDRA-6086)
|
||||
* Delete unfinished compaction incrementally (CASSANDRA-6086)
|
||||
* Allow specifying custom secondary index options in CQL3 (CASSANDRA-6480)
|
||||
Merged from 1.2:
|
||||
* fsync compression metadata (CASSANDRA-6531)
|
||||
* Validate CF existence on execution for prepared statement (CASSANDRA-6535)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ exit 1
|
|||
from __future__ import with_statement
|
||||
|
||||
description = "CQL Shell for Apache Cassandra"
|
||||
version = "4.1.0"
|
||||
version = "4.1.1"
|
||||
|
||||
from StringIO import StringIO
|
||||
from itertools import groupby
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<link rel="StyleSheet" href="CQL.css" type="text/css" media="screen">
|
||||
|
||||
h1. Cassandra Query Language (CQL) v3.1.3
|
||||
h1. Cassandra Query Language (CQL) v3.1.4
|
||||
|
||||
|
||||
<span id="tableOfContents">
|
||||
|
|
@ -405,7 +405,7 @@ h3(#createIndexStmt). CREATE INDEX
|
|||
__Syntax:__
|
||||
|
||||
bc(syntax). <create-index-stmt> ::= CREATE ( CUSTOM )? INDEX ( IF NOT EXISTS )? <identifier>? ON <tablename> '(' <identifier> ')'
|
||||
( USING <string> )?
|
||||
( USING <string> ( WITH OPTIONS = <map-literal> )? )?
|
||||
|
||||
__Sample:__
|
||||
|
||||
|
|
@ -413,6 +413,7 @@ bc(sample).
|
|||
CREATE INDEX userIndex ON NerdMovies (user);
|
||||
CREATE INDEX ON Mutants (abilityId);
|
||||
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 asynchronously. After the index is created, new data for the column is indexed automatically at insertion time.
|
||||
|
||||
|
|
@ -1102,6 +1103,10 @@ h2(#changes). Changes
|
|||
|
||||
The following describes the addition/changes brought for each version of CQL.
|
||||
|
||||
h3. 3.1.4
|
||||
|
||||
* @CREATE INDEX@ now allows specifying options when creating CUSTOM indexes (see "CREATE INDEX reference":#createIndexStmt).
|
||||
|
||||
h3. 3.1.3
|
||||
|
||||
* Millisecond precision formats have been added to the timestamp parser (see "working with dates":#usingdates).
|
||||
|
|
|
|||
|
|
@ -927,7 +927,7 @@ def create_cf_composite_primary_key_comma_completer(ctxt, cass):
|
|||
syntax_rules += r'''
|
||||
<createIndexStatement> ::= "CREATE" "CUSTOM"? "INDEX" ("IF" "NOT" "EXISTS")? indexname=<identifier>? "ON"
|
||||
cf=<columnFamilyName> "(" col=<cident> ")"
|
||||
( "USING" <stringLiteral> )?
|
||||
( "USING" <stringLiteral> ( "WITH" "OPTIONS" "=" <mapLiteral> )? )?
|
||||
;
|
||||
'''
|
||||
|
||||
|
|
|
|||
|
|
@ -510,13 +510,14 @@ cfamOrdering[CreateTableStatement.RawStatement expr]
|
|||
*/
|
||||
createIndexStatement returns [CreateIndexStatement expr]
|
||||
@init {
|
||||
boolean isCustom = false;
|
||||
IndexPropDefs props = new IndexPropDefs();
|
||||
boolean ifNotExists = false;
|
||||
}
|
||||
: K_CREATE (K_CUSTOM { isCustom = true; })? K_INDEX (K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
|
||||
: K_CREATE (K_CUSTOM { props.isCustom = true; })? K_INDEX (K_IF K_NOT K_EXISTS { ifNotExists = true; } )?
|
||||
(idxName=IDENT)? K_ON cf=columnFamilyName '(' id=cident ')'
|
||||
( K_USING cls=STRING_LITERAL )?
|
||||
{ $expr = new CreateIndexStatement(cf, $idxName.text, id, ifNotExists, isCustom, $cls.text); }
|
||||
(K_USING cls=STRING_LITERAL { props.customClass = $cls.text; })?
|
||||
(K_WITH properties[props])?
|
||||
{ $expr = new CreateIndexStatement(cf, $idxName.text, id, props, ifNotExists); }
|
||||
;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import org.apache.cassandra.utils.SemanticVersion;
|
|||
|
||||
public class QueryProcessor
|
||||
{
|
||||
public static final SemanticVersion CQL_VERSION = new SemanticVersion("3.1.3");
|
||||
public static final SemanticVersion CQL_VERSION = new SemanticVersion("3.1.4");
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(QueryProcessor.class);
|
||||
private static final MemoryMeter meter = new MemoryMeter();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import org.apache.cassandra.auth.Permission;
|
|||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
import org.apache.cassandra.config.Schema;
|
||||
import org.apache.cassandra.db.index.SecondaryIndex;
|
||||
import org.apache.cassandra.exceptions.*;
|
||||
import org.apache.cassandra.cql3.*;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
|
@ -42,18 +41,20 @@ public class CreateIndexStatement extends SchemaAlteringStatement
|
|||
|
||||
private final String indexName;
|
||||
private final ColumnIdentifier columnName;
|
||||
private final IndexPropDefs properties;
|
||||
private final boolean ifNotExists;
|
||||
private final boolean isCustom;
|
||||
private final String indexClass;
|
||||
|
||||
public CreateIndexStatement(CFName name, String indexName, ColumnIdentifier columnName, boolean ifNotExists, boolean isCustom, String indexClass)
|
||||
public CreateIndexStatement(CFName name,
|
||||
String indexName,
|
||||
ColumnIdentifier columnName,
|
||||
IndexPropDefs properties,
|
||||
boolean ifNotExists)
|
||||
{
|
||||
super(name);
|
||||
this.indexName = indexName;
|
||||
this.columnName = columnName;
|
||||
this.properties = properties;
|
||||
this.ifNotExists = ifNotExists;
|
||||
this.isCustom = isCustom;
|
||||
this.indexClass = indexClass;
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
|
|
@ -80,24 +81,20 @@ public class CreateIndexStatement extends SchemaAlteringStatement
|
|||
throw new InvalidRequestException("Index already exists");
|
||||
}
|
||||
|
||||
if (isCustom && indexClass == null)
|
||||
throw new InvalidRequestException("CUSTOM index requires specifiying the index class");
|
||||
|
||||
if (!isCustom && indexClass != null)
|
||||
throw new InvalidRequestException("Cannot specify index class for a non-CUSTOM index");
|
||||
properties.validate();
|
||||
|
||||
// TODO: we could lift that limitation
|
||||
if (cfm.getCfDef().isCompact && cd.type != ColumnDefinition.Type.REGULAR)
|
||||
throw new InvalidRequestException(String.format("Secondary index on %s column %s is not yet supported for compact table", cd.type, columnName));
|
||||
|
||||
if (cd.getValidator().isCollection() && !isCustom)
|
||||
if (cd.getValidator().isCollection() && !properties.isCustom)
|
||||
throw new InvalidRequestException("Indexes on collections are no yet supported");
|
||||
|
||||
if (cd.type == ColumnDefinition.Type.PARTITION_KEY && cd.componentIndex == null)
|
||||
throw new InvalidRequestException(String.format("Cannot add secondary index to already primarily indexed column %s", columnName));
|
||||
}
|
||||
|
||||
public void announceMigration() throws InvalidRequestException, ConfigurationException
|
||||
public void announceMigration() throws RequestValidationException
|
||||
{
|
||||
logger.debug("Updating column {} definition for index {}", columnName, indexName);
|
||||
CFMetaData cfm = Schema.instance.getCFMetaData(keyspace(), columnFamily()).clone();
|
||||
|
|
@ -106,8 +103,8 @@ public class CreateIndexStatement extends SchemaAlteringStatement
|
|||
if (cd.getIndexType() != null && ifNotExists)
|
||||
return;
|
||||
|
||||
if (isCustom)
|
||||
cd.setIndexType(IndexType.CUSTOM, Collections.singletonMap(SecondaryIndex.CUSTOM_INDEX_OPTION_NAME, indexClass));
|
||||
if (properties.isCustom)
|
||||
cd.setIndexType(IndexType.CUSTOM, properties.getOptions());
|
||||
else if (cfm.getCfDef().isComposite)
|
||||
cd.setIndexType(IndexType.COMPOSITES, Collections.<String, String>emptyMap());
|
||||
else
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.cql3.statements;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.cassandra.db.index.SecondaryIndex;
|
||||
import org.apache.cassandra.exceptions.*;
|
||||
|
||||
public class IndexPropDefs extends PropertyDefinitions
|
||||
{
|
||||
public static final String KW_OPTIONS = "options";
|
||||
|
||||
public static final Set<String> keywords = new HashSet<>();
|
||||
public static final Set<String> obsoleteKeywords = new HashSet<>();
|
||||
|
||||
public boolean isCustom;
|
||||
public String customClass;
|
||||
|
||||
static
|
||||
{
|
||||
keywords.add(KW_OPTIONS);
|
||||
}
|
||||
|
||||
public void validate() throws RequestValidationException
|
||||
{
|
||||
validate(keywords, obsoleteKeywords);
|
||||
|
||||
if (isCustom && customClass == null)
|
||||
throw new InvalidRequestException("CUSTOM index requires specifiying the index class");
|
||||
|
||||
if (!isCustom && customClass != null)
|
||||
throw new InvalidRequestException("Cannot specify index class for a non-CUSTOM index");
|
||||
|
||||
if (!isCustom && !properties.isEmpty())
|
||||
throw new InvalidRequestException("Cannot specify options for a non-CUSTOM index");
|
||||
|
||||
if (getRawOptions().containsKey(SecondaryIndex.CUSTOM_INDEX_OPTION_NAME))
|
||||
throw new InvalidRequestException(String.format("Cannot specify %s as a CUSTOM option",
|
||||
SecondaryIndex.CUSTOM_INDEX_OPTION_NAME));
|
||||
}
|
||||
|
||||
public Map<String, String> getRawOptions() throws SyntaxException
|
||||
{
|
||||
Map<String, String> options = getMap(KW_OPTIONS);
|
||||
return options == null ? Collections.<String, String>emptyMap() : options;
|
||||
}
|
||||
|
||||
public Map<String, String> getOptions() throws SyntaxException
|
||||
{
|
||||
Map<String, String> options = new HashMap<>(getRawOptions());
|
||||
options.put(SecondaryIndex.CUSTOM_INDEX_OPTION_NAME, customClass);
|
||||
return options;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue