mirror of https://github.com/apache/cassandra
Fix (some) 2i on composite components omissions
patch by Aleksey Yeschenko; reviewed by Jonathan Ellis for CASSANDRA-5851
This commit is contained in:
parent
1c606bb8af
commit
703bd684ca
|
|
@ -2,6 +2,7 @@
|
|||
* enable vnodes by default (CASSANDRA-5869)
|
||||
* fix CAS contention timeout (CASSANDRA-5830)
|
||||
* fix HsHa to respect max frame size (CASSANDRA-4573)
|
||||
* Fix (some) 2i on composite components omissions (CASSANDRA-5851)
|
||||
Merged from 1.2:
|
||||
* Correctly validate sparse composite cells in scrub (CASSANDRA-5855)
|
||||
* Add KeyCacheHitRate metric to CF metrics (CASSANDRA-5868)
|
||||
|
|
|
|||
41
bin/cqlsh
41
bin/cqlsh
|
|
@ -1090,14 +1090,8 @@ class Shell(cmd.Cmd):
|
|||
layout = self.get_columnfamily_layout(ksname, cfname)
|
||||
cfname = self.cql_protect_name(layout.name)
|
||||
out.write("CREATE TABLE %s (\n" % cfname)
|
||||
keycol = layout.columns[0]
|
||||
out.write(" %s %s" % (self.cql_protect_name(keycol.name),
|
||||
keycol.cqltype.cql_parameterized_type()))
|
||||
if len(layout.primary_key_components) == 1:
|
||||
out.write(" PRIMARY KEY")
|
||||
|
||||
indexed_columns = []
|
||||
for col in layout.columns[1:]:
|
||||
for col in layout.columns:
|
||||
colname = self.cql_protect_name(col.name)
|
||||
coltype = col.cqltype
|
||||
|
||||
|
|
@ -1105,29 +1099,26 @@ class Shell(cmd.Cmd):
|
|||
if issubclass(coltype, ReversedType):
|
||||
coltype = coltype.subtypes[0]
|
||||
|
||||
out.write(",\n %s %s" % (colname, coltype.cql_parameterized_type()))
|
||||
if col.index_name is not None:
|
||||
indexed_columns.append(col)
|
||||
out.write(" %s %s,\n" % (colname, coltype.cql_parameterized_type()))
|
||||
|
||||
if len(layout.primary_key_components) > 1:
|
||||
out.write(",\n PRIMARY KEY (")
|
||||
partkeynames = self.cql_protect_names(layout.partition_key_components)
|
||||
if len(partkeynames) > 1:
|
||||
partkey = "(%s)" % ', '.join(partkeynames)
|
||||
else:
|
||||
partkey = partkeynames[0]
|
||||
pk_parts = [partkey] + self.cql_protect_names(layout.column_aliases)
|
||||
out.write(', '.join(pk_parts) + ')')
|
||||
out.write(" PRIMARY KEY (")
|
||||
partkeynames = self.cql_protect_names(layout.partition_key_columns)
|
||||
if len(partkeynames) > 1:
|
||||
partkey = "(%s)" % ', '.join(partkeynames)
|
||||
else:
|
||||
partkey = partkeynames[0]
|
||||
pk_parts = [partkey] + self.cql_protect_names(layout.clustering_key_columns)
|
||||
out.write(', '.join(pk_parts) + ')')
|
||||
|
||||
out.write("\n)")
|
||||
joiner = 'WITH'
|
||||
|
||||
if layout.compact_storage:
|
||||
if layout.is_compact_storage():
|
||||
out.write(' WITH COMPACT STORAGE')
|
||||
joiner = 'AND'
|
||||
|
||||
# check if we need a CLUSTERING ORDER BY clause
|
||||
if layout.column_aliases:
|
||||
if layout.clustering_key_columns:
|
||||
# get a list of clustering component types
|
||||
if issubclass(layout.comparator, CompositeType):
|
||||
clustering_types = layout.comparator.subtypes
|
||||
|
|
@ -1136,13 +1127,13 @@ class Shell(cmd.Cmd):
|
|||
|
||||
# only write CLUSTERING ORDER clause of we have >= 1 DESC item
|
||||
if any(issubclass(t, ReversedType) for t in clustering_types):
|
||||
if layout.compact_storage:
|
||||
if layout.is_compact_storage():
|
||||
out.write(' AND\n ')
|
||||
else:
|
||||
out.write(' WITH')
|
||||
out.write(' CLUSTERING ORDER BY (')
|
||||
|
||||
clustering_names = self.cql_protect_names(layout.column_aliases)
|
||||
clustering_names = self.cql_protect_names(layout.clustering_key_columns)
|
||||
|
||||
inner = []
|
||||
for colname, coltype in zip(clustering_names, clustering_types):
|
||||
|
|
@ -1195,7 +1186,7 @@ class Shell(cmd.Cmd):
|
|||
joiner = 'AND'
|
||||
out.write(";\n")
|
||||
|
||||
for col in indexed_columns:
|
||||
for col in [ c for c in layout.columns if c.index_name is not None ]:
|
||||
out.write('\n')
|
||||
if col.index_type != 'CUSTOM':
|
||||
out.write('CREATE INDEX %s ON %s (%s);\n'
|
||||
|
|
@ -1454,7 +1445,7 @@ class Shell(cmd.Cmd):
|
|||
rowmap[name] = self.cql_protect_value(value)
|
||||
else:
|
||||
rowmap[name] = value
|
||||
elif name in layout.column_aliases and not type.empty_binary_ok:
|
||||
elif name in layout.clustering_key_columns and not type.empty_binary_ok:
|
||||
rowmap[name] = 'blobAs%s(0x)' % cqltype.title()
|
||||
else:
|
||||
rowmap[name] = 'null'
|
||||
|
|
|
|||
|
|
@ -589,7 +589,7 @@ def select_order_column_completer(ctxt, cass):
|
|||
if not keyname:
|
||||
return [Hint("Can't ORDER BY here: need to specify partition key in WHERE clause")]
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
order_by_candidates = layout.column_aliases[:]
|
||||
order_by_candidates = layout.clustering_key_columns[:]
|
||||
if len(order_by_candidates) > len(prev_order_cols):
|
||||
return [maybe_escape_name(order_by_candidates[len(prev_order_cols)])]
|
||||
return [Hint('No more orderable columns here.')]
|
||||
|
|
@ -601,21 +601,21 @@ def relation_token_word_completer(ctxt, cass):
|
|||
@completer_for('relation', 'rel_tokname')
|
||||
def relation_token_subject_completer(ctxt, cass):
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
return [layout.partition_key_components[0]]
|
||||
return [layout.partition_key_columns[0]]
|
||||
|
||||
@completer_for('relation', 'rel_lhs')
|
||||
def select_relation_lhs_completer(ctxt, cass):
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
filterable = set((layout.partition_key_components[0], layout.column_aliases[0]))
|
||||
filterable = set((layout.partition_key_columns[0], layout.clustering_key_columns[0]))
|
||||
already_filtered_on = map(dequote_name, ctxt.get_binding('rel_lhs'))
|
||||
for num in range(1, len(layout.partition_key_components)):
|
||||
if layout.partition_key_components[num - 1] in already_filtered_on:
|
||||
filterable.add(layout.partition_key_components[num])
|
||||
for num in range(1, len(layout.partition_key_columns)):
|
||||
if layout.partition_key_columns[num - 1] in already_filtered_on:
|
||||
filterable.add(layout.partition_key_columns[num])
|
||||
else:
|
||||
break
|
||||
for num in range(1, len(layout.column_aliases)):
|
||||
if layout.column_aliases[num - 1] in already_filtered_on:
|
||||
filterable.add(layout.column_aliases[num])
|
||||
for num in range(1, len(layout.clustering_key_columns)):
|
||||
if layout.clustering_key_columns[num - 1] in already_filtered_on:
|
||||
filterable.add(layout.clustering_key_columns[num])
|
||||
else:
|
||||
break
|
||||
for cd in layout.columns:
|
||||
|
|
@ -647,11 +647,11 @@ syntax_rules += r'''
|
|||
def insert_colname_completer(ctxt, cass):
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
colnames = set(map(dequote_name, ctxt.get_binding('colname', ())))
|
||||
keycols = layout.primary_key_components
|
||||
keycols = layout.primary_key_columns
|
||||
for k in keycols:
|
||||
if k not in colnames:
|
||||
return [maybe_escape_name(k)]
|
||||
normalcols = set([c.name for c in layout.columns]) - set(keycols) - colnames
|
||||
normalcols = set(layout.regular_columns) - colnames
|
||||
return map(maybe_escape_name, normalcols)
|
||||
|
||||
@completer_for('insertStatement', 'newval')
|
||||
|
|
@ -714,9 +714,7 @@ def insert_option_completer(ctxt, cass):
|
|||
@completer_for('assignment', 'updatecol')
|
||||
def update_col_completer(ctxt, cass):
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
normals = set([cm.name for cm in layout.columns]) \
|
||||
- set(layout.primary_key_components)
|
||||
return map(maybe_escape_name, normals)
|
||||
return map(maybe_escape_name, layout.regular_columns)
|
||||
|
||||
@completer_for('assignment', 'update_rhs')
|
||||
def update_countername_completer(ctxt, cass):
|
||||
|
|
@ -790,10 +788,8 @@ def delete_opt_completer(ctxt, cass):
|
|||
|
||||
@completer_for('deleteSelector', 'delcol')
|
||||
def delete_delcol_completer(ctxt, cass):
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
cols = set([c.name for c in layout.columns
|
||||
if c not in layout.primary_key_components])
|
||||
return map(maybe_escape_name, cols)
|
||||
layout = get_cf_layout(ctxt, cass)
|
||||
return map(maybe_escape_name, layout.regular_columns)
|
||||
|
||||
syntax_rules += r'''
|
||||
<batchStatement> ::= "BEGIN" ( "UNLOGGED" | "COUNTER" )? "BATCH"
|
||||
|
|
@ -1123,72 +1119,11 @@ def username_name_completer(ctxt, cass):
|
|||
|
||||
CqlRuleSet.append_rules(syntax_rules)
|
||||
|
||||
|
||||
|
||||
# current assumption is that all valid CQL tables match the rules in the
|
||||
# following table.
|
||||
#
|
||||
# non-empty non-empty multiple composite
|
||||
# value_alias column_aliases key_aliases comparator
|
||||
# ---------------------+----------------------------------------------------
|
||||
# A: single-column PK, |
|
||||
# compact storage | either no no no
|
||||
# ---------------------+----------------------------------------------------
|
||||
# B: single-column PK, |
|
||||
# dynamic storage | no no no yes
|
||||
# ---------------------+----------------------------------------------------
|
||||
# C: compound PK, |
|
||||
# plain part. key, | yes[1] yes no either
|
||||
# compact storage |
|
||||
# ---------------------+----------------------------------------------------
|
||||
# D: compound PK, |
|
||||
# plain part. key, | no yes no yes
|
||||
# dynamic storage |
|
||||
# ---------------------+----------------------------------------------------
|
||||
# E: compound PK, |
|
||||
# multipart part. key, |
|
||||
# all key components | either no yes no
|
||||
# go in part. key, |
|
||||
# compact storage |
|
||||
# ---------------------+----------------------------------------------------
|
||||
# F: compound PK, |
|
||||
# multipart part. key, |
|
||||
# all key components | no no yes yes
|
||||
# go in part. key, |
|
||||
# dynamic storage |
|
||||
# ---------------------+----------------------------------------------------
|
||||
# G: compound PK, |
|
||||
# multipart part. key, |
|
||||
# some key components | yes[1] yes yes either
|
||||
# not in part. key, |
|
||||
# compact storage |
|
||||
# ---------------------+----------------------------------------------------
|
||||
# H: compound PK, |
|
||||
# multipart part. key, |
|
||||
# some key components | no yes yes yes
|
||||
# not in part. key, |
|
||||
# dynamic storage |
|
||||
# ---------------------+----------------------------------------------------
|
||||
#
|
||||
# [1] the value_alias may be blank, but not null.
|
||||
|
||||
# for compact storage:
|
||||
#
|
||||
# if no column aliases:
|
||||
# comparator will be UTF8Type
|
||||
# elif one column alias:
|
||||
# comparator will be type of that column
|
||||
# else:
|
||||
# comparator will be composite of types of all column_aliases
|
||||
#
|
||||
# for dynamic storage:
|
||||
#
|
||||
# comparator is composite of types of column_aliases, followed by UTF8Type,
|
||||
# followed by one CTCT if there are collections.
|
||||
|
||||
class CqlColumnDef:
|
||||
index_name = None
|
||||
index_type = None
|
||||
component_type = 'regular'
|
||||
component_index = None
|
||||
index_options = {}
|
||||
|
||||
def __init__(self, name, cqltype):
|
||||
|
|
@ -1198,11 +1133,11 @@ class CqlColumnDef:
|
|||
|
||||
@classmethod
|
||||
def from_layout(cls, layout):
|
||||
try:
|
||||
colname = layout[u'column_name']
|
||||
except KeyError:
|
||||
colname = layout[u'column']
|
||||
c = cls(colname, lookup_casstype(layout[u'validator']))
|
||||
c = cls(layout[u'column_name'], lookup_casstype(layout[u'validator']))
|
||||
c.component_type = layout[u'type']
|
||||
idx = layout[u'component_index'] # can be None
|
||||
if idx:
|
||||
c.component_index = int(idx)
|
||||
c.index_name = layout[u'index_name']
|
||||
c.index_type = layout[u'index_type']
|
||||
if c.index_type == 'CUSTOM':
|
||||
|
|
@ -1215,24 +1150,19 @@ class CqlColumnDef:
|
|||
__repr__ = __str__
|
||||
|
||||
class CqlTableDef:
|
||||
json_attrs = ('column_aliases', 'compaction_strategy_options', 'compression_parameters',
|
||||
'key_aliases')
|
||||
colname_type = UTF8Type
|
||||
column_class = CqlColumnDef
|
||||
|
||||
"""True if this CF has compact storage (isn't a CQL3 table)"""
|
||||
compact_storage = False
|
||||
|
||||
"""Names of all columns which are part of the primary key, whether or not
|
||||
they are grouped into the partition key"""
|
||||
primary_key_components = ()
|
||||
|
||||
"""Names of all columns which are grouped into the partition key"""
|
||||
partition_key_components = ()
|
||||
partition_key_columns = ()
|
||||
|
||||
"""Names of all columns which are part of the primary key, but not grouped
|
||||
into the partition key"""
|
||||
column_aliases = ()
|
||||
clustering_key_columns = ()
|
||||
|
||||
"""Names of all columns which are part of the primary key, whether or not
|
||||
they are grouped into the partition key"""
|
||||
primary_key_columns = ()
|
||||
|
||||
"""Names of all columns which aren't part of the primary key"""
|
||||
regular_columns = ()
|
||||
|
||||
"""CqlColumnDef objects for all columns. Use .get_column() to access one
|
||||
by name."""
|
||||
|
|
@ -1248,41 +1178,34 @@ class CqlTableDef:
|
|||
of system.schema_columnfamilies, and a sequence of similar dictionaries
|
||||
from corresponding rows in system.schema_columns.
|
||||
"""
|
||||
try:
|
||||
cfname = layout[u'columnfamily_name']
|
||||
ksname = layout[u'keyspace_name']
|
||||
except KeyError:
|
||||
cfname = layout[u'columnfamily']
|
||||
ksname = layout[u'keyspace']
|
||||
cf = cls(name=cfname)
|
||||
cf = cls(name=layout[u'columnfamily_name'])
|
||||
cf.keyspace = layout[u'keyspace_name']
|
||||
for attr, val in layout.items():
|
||||
setattr(cf, attr.encode('ascii'), val)
|
||||
cf.keyspace = ksname
|
||||
for attr in cls.json_attrs:
|
||||
try:
|
||||
val = getattr(cf, attr)
|
||||
# cfs created in 1.1 may not have key_aliases defined
|
||||
if attr == 'key_aliases' and val is None:
|
||||
val = '[]'
|
||||
setattr(cf, attr, json.loads(val))
|
||||
except AttributeError:
|
||||
pass
|
||||
cf.partition_key_validator = lookup_casstype(cf.key_validator)
|
||||
cf.comparator = lookup_casstype(cf.comparator)
|
||||
cf.default_validator = lookup_casstype(cf.default_validator)
|
||||
cf.coldefs = cf.filter_regular_coldefs(coldefs)
|
||||
cf.compact_storage = cf.is_compact_storage()
|
||||
cf.key_aliases = cf.get_key_aliases()
|
||||
cf.partition_key_components = cf.key_aliases
|
||||
cf.column_aliases = cf.get_column_aliases()
|
||||
cf.primary_key_components = cf.key_aliases + list(cf.column_aliases)
|
||||
cf.columns = cf.get_columns()
|
||||
for attr in ('compaction_strategy_options', 'compression_parameters'):
|
||||
setattr(cf, attr, json.loads(getattr(cf, attr)))
|
||||
|
||||
# deal with columns
|
||||
columns = map(CqlColumnDef.from_layout, coldefs)
|
||||
|
||||
partition_key_cols = filter(lambda c: c.component_type == u'partition_key', columns)
|
||||
partition_key_cols.sort(key=lambda c: c.component_index)
|
||||
cf.partition_key_columns = map(lambda c: c.name, partition_key_cols)
|
||||
|
||||
clustering_key_cols = filter(lambda c: c.component_type == u'clustering_key', columns)
|
||||
clustering_key_cols.sort(key=lambda c: c.component_index)
|
||||
cf.clustering_key_columns = map(lambda c: c.name, clustering_key_cols)
|
||||
|
||||
cf.primary_key_columns = cf.partition_key_columns + cf.clustering_key_columns
|
||||
|
||||
regular_cols = list(set(columns) - set(partition_key_cols) - set(clustering_key_cols))
|
||||
regular_cols.sort(key=lambda c: c.name)
|
||||
cf.regular_columns = map(lambda c: c.name, regular_cols)
|
||||
|
||||
cf.columns = partition_key_cols + clustering_key_cols + regular_cols
|
||||
return cf
|
||||
|
||||
def filter_regular_coldefs(self, cols):
|
||||
return [ c for c in cols if c.get('type', 'regular') == 'regular' ]
|
||||
|
||||
|
||||
# not perfect, but good enough; please read CFDefinition constructor comments
|
||||
# returns False if we are dealing with a CQL3 table, True otherwise.
|
||||
# 'compact' here means 'needs WITH COMPACT STORAGE option for CREATE TABLE in CQL3'.
|
||||
|
|
@ -1292,111 +1215,11 @@ class CqlTableDef:
|
|||
for subtype in self.comparator.subtypes:
|
||||
if issubclass(subtype, ColumnToCollectionType):
|
||||
return False
|
||||
if len(self.column_aliases) == len(self.comparator.subtypes) - 1:
|
||||
if len(self.clustering_key_columns) == len(self.comparator.subtypes) - 1:
|
||||
if self.comparator.subtypes[-1] is UTF8Type:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_key_aliases(self):
|
||||
if not issubclass(self.partition_key_validator, CompositeType):
|
||||
return self.key_aliases or (self.key_alias and [self.key_alias]) or [u'key']
|
||||
expected = len(self.partition_key_validator.subtypes)
|
||||
# key, key2, key3, ..., keyN
|
||||
aliases = [u'key'] + [ u'key' + str(i) for i in range(2, expected + 1) ]
|
||||
# append the missing (non-renamed) aliases (if any)
|
||||
return self.key_aliases + aliases[len(self.key_aliases):]
|
||||
|
||||
def get_column_aliases(self):
|
||||
# CQL3 table
|
||||
if not self.compact_storage:
|
||||
return self.column_aliases
|
||||
if not issubclass(self.comparator, CompositeType):
|
||||
# static cf
|
||||
if self.coldefs:
|
||||
return []
|
||||
else:
|
||||
return self.column_aliases or [u'column1']
|
||||
expected = len(self.comparator.subtypes)
|
||||
# column1, column2, column3, ..., columnN
|
||||
aliases = [ u'column' + str(i) for i in range(1, expected + 1) ]
|
||||
# append the missing (non-renamed) aliases (if any)
|
||||
return self.column_aliases + aliases[len(self.column_aliases):]
|
||||
|
||||
def get_columns(self):
|
||||
if self.compact_storage:
|
||||
return self.get_columns_compact()
|
||||
else:
|
||||
return self.get_columns_cql3()
|
||||
|
||||
# dense composite or dynamic cf or static cf (technically not compact).
|
||||
def get_columns_compact(self):
|
||||
if issubclass(self.partition_key_validator, CompositeType):
|
||||
partkey_types = self.partition_key_validator.subtypes
|
||||
else:
|
||||
partkey_types = [self.partition_key_validator]
|
||||
partkey_cols = map(self.column_class, self.partition_key_components, partkey_types)
|
||||
|
||||
if len(self.column_aliases) == 0:
|
||||
if self.comparator is not UTF8Type:
|
||||
warn(UnexpectedTableStructure("Compact storage CF %s has no column aliases,"
|
||||
" but comparator is not UTF8Type." % (self.name,)))
|
||||
colalias_types = []
|
||||
elif issubclass(self.comparator, CompositeType):
|
||||
colalias_types = self.comparator.subtypes
|
||||
else:
|
||||
colalias_types = [self.comparator]
|
||||
if len(colalias_types) != len(self.column_aliases):
|
||||
warn(UnexpectedTableStructure("Compact storage CF comparator-types %r is not"
|
||||
" the same length as its column_aliases %r"
|
||||
% (colalias_types, self.column_aliases)))
|
||||
colalias_cols = map(self.column_class, self.column_aliases, colalias_types)
|
||||
|
||||
if self.value_alias is not None:
|
||||
if self.coldefs:
|
||||
warn(UnexpectedTableStructure("Compact storage CF has both a value_alias"
|
||||
" (%r) and entries in system.schema_columns"
|
||||
% (self.value_alias,)))
|
||||
if self.value_alias == '':
|
||||
value_cols = []
|
||||
else:
|
||||
value_cols = [self.column_class(self.value_alias, self.default_validator)]
|
||||
elif self.value_alias is None and not self.coldefs:
|
||||
value_cols = [self.column_class("value", self.default_validator)]
|
||||
else:
|
||||
value_cols = map(self.column_class.from_layout, self.coldefs)
|
||||
value_cols.sort(key=lambda c: c.name)
|
||||
|
||||
return partkey_cols + colalias_cols + value_cols
|
||||
|
||||
# sparse composite (CQL3 table).
|
||||
def get_columns_cql3(self):
|
||||
if issubclass(self.partition_key_validator, CompositeType):
|
||||
partkey_types = self.partition_key_validator.subtypes
|
||||
else:
|
||||
partkey_types = [self.partition_key_validator]
|
||||
partkey_cols = map(self.column_class, self.partition_key_components, partkey_types)
|
||||
|
||||
for subtype in self.comparator.subtypes[:-1]:
|
||||
if issubclass(subtype, ColumnToCollectionType):
|
||||
warn(UnexpectedTableStructure("ColumnToCollectionType found, but not in "
|
||||
"last position inside composite comparator"))
|
||||
coltypes = list(self.comparator.subtypes)
|
||||
if issubclass(coltypes[-1], ColumnToCollectionType):
|
||||
# all this information should be available in schema_columns
|
||||
coltypes.pop(-1)
|
||||
if len(coltypes) != len(self.column_aliases) + 1 or coltypes[-1] is not UTF8Type:
|
||||
warn(UnexpectedTableStructure("CQL3 CF does not have UTF8Type"
|
||||
" added to comparator"))
|
||||
colalias_cols = map(self.column_class, self.column_aliases, coltypes[:-1])
|
||||
|
||||
if self.value_alias is not None:
|
||||
warn(UnexpectedTableStructure("CQL3 CF has a value_alias (%r)"
|
||||
% (self.value_alias,)))
|
||||
value_cols = map(self.column_class.from_layout, self.coldefs)
|
||||
value_cols.sort(key=lambda c: c.name)
|
||||
|
||||
return partkey_cols + colalias_cols + value_cols
|
||||
|
||||
def is_counter_col(self, colname):
|
||||
try:
|
||||
return bool(self.get_column(colname).cqltype is CounterColumnType)
|
||||
|
|
|
|||
|
|
@ -638,7 +638,7 @@ class TestCqlshOutput(BaseTestCase):
|
|||
table_desc3 = dedent("""
|
||||
|
||||
CREATE TABLE has_all_types (
|
||||
num int PRIMARY KEY,
|
||||
num int,
|
||||
asciicol ascii,
|
||||
bigintcol bigint,
|
||||
blobcol blob,
|
||||
|
|
@ -651,7 +651,8 @@ class TestCqlshOutput(BaseTestCase):
|
|||
timestampcol timestamp,
|
||||
uuidcol uuid,
|
||||
varcharcol text,
|
||||
varintcol varint
|
||||
varintcol varint,
|
||||
PRIMARY KEY (num)
|
||||
) WITH
|
||||
bloom_filter_fp_chance=0.010000 AND
|
||||
caching='KEYS_ONLY' AND
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public class CreateIndexStatement extends SchemaAlteringStatement
|
|||
if (cd.getValidator().isCollection() && !isCustom)
|
||||
throw new InvalidRequestException("Indexes on collections are no yet supported");
|
||||
|
||||
if (cd.type == ColumnDefinition.Type.PARTITION_KEY && (cd.componentIndex == null || cd.componentIndex == 0))
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ public class SelectStatement implements CQLStatement
|
|||
|
||||
RowPosition startKey = RowPosition.forKey(startKeyBytes, p);
|
||||
RowPosition finishKey = RowPosition.forKey(finishKeyBytes, p);
|
||||
|
||||
if (startKey.compareTo(finishKey) > 0 && !finishKey.isMinimum(p))
|
||||
return null;
|
||||
|
||||
|
|
@ -488,6 +489,12 @@ public class SelectStatement implements CQLStatement
|
|||
|
||||
private ByteBuffer getKeyBound(Bound b, List<ByteBuffer> variables) throws InvalidRequestException
|
||||
{
|
||||
// Deal with unrestricted partition key components (special-casing is required to deal with 2i queries on the first
|
||||
// component of a composite partition key).
|
||||
for (int i = 0; i < keyRestrictions.length; i++)
|
||||
if (keyRestrictions[i] == null)
|
||||
return ByteBufferUtil.EMPTY_BYTE_BUFFER;
|
||||
|
||||
// We deal with IN queries for keys in other places, so we know buildBound will return only one result
|
||||
return buildBound(b, cfDef.keys.values(), keyRestrictions, false, cfDef.getKeyNameBuilder(), variables).get(0);
|
||||
}
|
||||
|
|
@ -744,6 +751,7 @@ public class SelectStatement implements CQLStatement
|
|||
// We don't allow restricting a VALUE_ALIAS for now in prepare.
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
if (restriction.isEquality())
|
||||
{
|
||||
assert restriction.eqValues.size() == 1; // IN is not supported for indexed columns.
|
||||
|
|
@ -1073,6 +1081,7 @@ public class SelectStatement implements CQLStatement
|
|||
* - The value_alias cannot be restricted in any way (we don't support wide rows with indexed value in CQL so far)
|
||||
*/
|
||||
boolean hasQueriableIndex = false;
|
||||
boolean hasQueriableClusteringColumnIndex = false;
|
||||
for (Relation rel : whereClause)
|
||||
{
|
||||
CFDefinition.Name name = cfDef.get(rel.getEntity());
|
||||
|
|
@ -1086,10 +1095,11 @@ public class SelectStatement implements CQLStatement
|
|||
|
||||
ColumnDefinition def = cfDef.cfm.getColumnDefinition(name.name.key);
|
||||
stmt.restrictedNames.add(name);
|
||||
if (def.isIndexed())
|
||||
if (def.isIndexed() && rel.operator() == Relation.Type.EQ)
|
||||
{
|
||||
if (rel.operator() == Relation.Type.EQ)
|
||||
hasQueriableIndex = true;
|
||||
hasQueriableIndex = true;
|
||||
if (name.kind == CFDefinition.Name.Kind.COLUMN_ALIAS)
|
||||
hasQueriableClusteringColumnIndex = true;
|
||||
}
|
||||
|
||||
switch (name.kind)
|
||||
|
|
@ -1116,13 +1126,12 @@ public class SelectStatement implements CQLStatement
|
|||
// But we still need to know 2 things:
|
||||
// - If we don't have a queriable index, is the query ok
|
||||
// - Is it queriable without 2ndary index, which is always more efficient
|
||||
// If a component of the partition key is restricted by a non-EQ relation, all preceding
|
||||
// components must have a EQ, and all following must have no restriction
|
||||
boolean shouldBeDone = false;
|
||||
// If a component of the partition key is restricted by a relation, all preceding
|
||||
// components must have a EQ. Only the last partition key component can be in IN relation.
|
||||
boolean canRestrictFurtherComponents = true;
|
||||
CFDefinition.Name previous = null;
|
||||
stmt.keyIsInRelation = false;
|
||||
Iterator<CFDefinition.Name> iter = cfDef.keys.values().iterator();
|
||||
int lastRestrictedPartitionKey = stmt.keyRestrictions.length - 1;
|
||||
for (int i = 0; i < stmt.keyRestrictions.length; i++)
|
||||
{
|
||||
CFDefinition.Name cname = iter.next();
|
||||
|
|
@ -1130,27 +1139,25 @@ public class SelectStatement implements CQLStatement
|
|||
|
||||
if (restriction == null)
|
||||
{
|
||||
if (!shouldBeDone)
|
||||
lastRestrictedPartitionKey = i - 1;
|
||||
|
||||
if (stmt.onToken)
|
||||
throw new InvalidRequestException("The token() function must be applied to all partition key components or none of them");
|
||||
|
||||
// The only time not restricting a key part is allowed is if none are restricted
|
||||
if (i > 0 && stmt.keyRestrictions[i-1] != null)
|
||||
// The only time not restricting a key part is allowed is if none are restricted or an index is used.
|
||||
if (i > 0 && stmt.keyRestrictions[i - 1] != null)
|
||||
{
|
||||
if (hasQueriableIndex)
|
||||
{
|
||||
stmt.usesSecondaryIndexing = true;
|
||||
stmt.isKeyRange = true;
|
||||
break;
|
||||
}
|
||||
throw new InvalidRequestException(String.format("Partition key part %s must be restricted since preceding part is", cname));
|
||||
}
|
||||
|
||||
stmt.isKeyRange = true;
|
||||
shouldBeDone = true;
|
||||
canRestrictFurtherComponents = false;
|
||||
}
|
||||
else if (shouldBeDone)
|
||||
else if (!canRestrictFurtherComponents)
|
||||
{
|
||||
if (hasQueriableIndex)
|
||||
{
|
||||
|
|
@ -1188,13 +1195,17 @@ public class SelectStatement implements CQLStatement
|
|||
previous = cname;
|
||||
}
|
||||
|
||||
// If a cluster key column is restricted by a non-EQ relation, all preceding
|
||||
// All (or none) of the partition key columns have been specified;
|
||||
// hence there is no need to turn these restrictions into index expressions.
|
||||
if (!stmt.usesSecondaryIndexing)
|
||||
stmt.restrictedNames.removeAll(cfDef.keys.values());
|
||||
|
||||
// If a clustering key column is restricted by a non-EQ relation, all preceding
|
||||
// columns must have a EQ, and all following must have no restriction. Unless
|
||||
// the column is indexed that is.
|
||||
shouldBeDone = false;
|
||||
canRestrictFurtherComponents = true;
|
||||
previous = null;
|
||||
iter = cfDef.columns.values().iterator();
|
||||
int lastRestrictedClusteringKey = stmt.columnRestrictions.length - 1;
|
||||
for (int i = 0; i < stmt.columnRestrictions.length; i++)
|
||||
{
|
||||
CFDefinition.Name cname = iter.next();
|
||||
|
|
@ -1202,67 +1213,56 @@ public class SelectStatement implements CQLStatement
|
|||
|
||||
if (restriction == null)
|
||||
{
|
||||
if (!shouldBeDone)
|
||||
lastRestrictedClusteringKey = i - 1;
|
||||
shouldBeDone = true;
|
||||
canRestrictFurtherComponents = false;
|
||||
}
|
||||
else
|
||||
else if (!canRestrictFurtherComponents)
|
||||
{
|
||||
if (shouldBeDone)
|
||||
if (hasQueriableIndex)
|
||||
{
|
||||
if (hasQueriableIndex)
|
||||
{
|
||||
stmt.usesSecondaryIndexing = true;
|
||||
break;
|
||||
}
|
||||
throw new InvalidRequestException(String.format("PRIMARY KEY part %s cannot be restricted (preceding part %s is either not restricted or by a non-EQ relation)", cname, previous));
|
||||
}
|
||||
else if (!restriction.isEquality())
|
||||
{
|
||||
lastRestrictedClusteringKey = i;
|
||||
shouldBeDone = true;
|
||||
// For non-composite slices, we don't support internally the difference between exclusive and
|
||||
// inclusive bounds, so we deal with it manually.
|
||||
if (!cfDef.isComposite && (!restriction.isInclusive(Bound.START) || !restriction.isInclusive(Bound.END)))
|
||||
stmt.sliceRestriction = restriction;
|
||||
stmt.usesSecondaryIndexing = true; // handle gaps and non-keyrange cases.
|
||||
break;
|
||||
}
|
||||
throw new InvalidRequestException(String.format("PRIMARY KEY part %s cannot be restricted (preceding part %s is either not restricted or by a non-EQ relation)", cname, previous));
|
||||
}
|
||||
else if (!restriction.isEquality())
|
||||
{
|
||||
canRestrictFurtherComponents = false;
|
||||
// For non-composite slices, we don't support internally the difference between exclusive and
|
||||
// inclusive bounds, so we deal with it manually.
|
||||
if (!cfDef.isComposite && (!restriction.isInclusive(Bound.START) || !restriction.isInclusive(Bound.END)))
|
||||
stmt.sliceRestriction = restriction;
|
||||
}
|
||||
else if (restriction.isINRestriction())
|
||||
{
|
||||
// We only support IN for the last name and for compact storage so far
|
||||
// TODO: #3885 allows us to extend to non compact as well, but that remains to be done
|
||||
else if (restriction.isINRestriction())
|
||||
{
|
||||
if (i != stmt.columnRestrictions.length - 1)
|
||||
throw new InvalidRequestException(String.format("PRIMARY KEY part %s cannot be restricted by IN relation", cname));
|
||||
else if (stmt.selectACollection())
|
||||
throw new InvalidRequestException(String.format("Cannot restrict PRIMARY KEY part %s by IN relation as a collection is selected by the query", cname));
|
||||
}
|
||||
if (i != stmt.columnRestrictions.length - 1)
|
||||
throw new InvalidRequestException(String.format("PRIMARY KEY part %s cannot be restricted by IN relation", cname));
|
||||
else if (stmt.selectACollection())
|
||||
throw new InvalidRequestException(String.format("Cannot restrict PRIMARY KEY part %s by IN relation as a collection is selected by the query", cname));
|
||||
}
|
||||
|
||||
previous = cname;
|
||||
}
|
||||
|
||||
// Covers indexes on the first clustering column (among others).
|
||||
if (stmt.isKeyRange && hasQueriableClusteringColumnIndex)
|
||||
stmt.usesSecondaryIndexing = true;
|
||||
|
||||
if (!stmt.usesSecondaryIndexing)
|
||||
stmt.restrictedNames.removeAll(cfDef.columns.values());
|
||||
|
||||
// Even if usesSecondaryIndexing is false at this point, we'll still have to use one if
|
||||
// there is restrictions not covered by the PK.
|
||||
if (!stmt.metadataRestrictions.isEmpty())
|
||||
{
|
||||
if (!hasQueriableIndex)
|
||||
throw new InvalidRequestException("No indexed columns present in by-columns clause with Equal operator");
|
||||
|
||||
stmt.usesSecondaryIndexing = true;
|
||||
}
|
||||
|
||||
if (stmt.usesSecondaryIndexing)
|
||||
{
|
||||
if (stmt.keyIsInRelation)
|
||||
throw new InvalidRequestException("Select on indexed columns and with IN clause for the PRIMARY KEY are not supported");
|
||||
}
|
||||
|
||||
iter = cfDef.keys.values().iterator();
|
||||
for (int i = 0; i < lastRestrictedPartitionKey + 1; i++)
|
||||
stmt.restrictedNames.remove(iter.next());
|
||||
|
||||
iter = cfDef.columns.values().iterator();
|
||||
for (int i = 0; i < lastRestrictedClusteringKey + 1; i++)
|
||||
stmt.restrictedNames.remove(iter.next());
|
||||
if (stmt.usesSecondaryIndexing && stmt.keyIsInRelation)
|
||||
throw new InvalidRequestException("Select on indexed columns and with IN clause for the PRIMARY KEY are not supported");
|
||||
|
||||
if (!stmt.parameters.orderings.isEmpty())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,9 +76,10 @@ public class CompositesIndexOnClusteringKey extends CompositesIndex
|
|||
ByteBuffer[] components = baseComparator.split(columnName);
|
||||
CompositeType.Builder builder = getIndexComparator().builder();
|
||||
builder.add(rowKey);
|
||||
for (int i = 0; i < columnDef.componentIndex; i++)
|
||||
|
||||
for (int i = 0; i < Math.min(components.length, columnDef.componentIndex); i++)
|
||||
builder.add(components[i]);
|
||||
for (int i = columnDef.componentIndex + 1; i < ckCount; i++)
|
||||
for (int i = columnDef.componentIndex + 1; i < Math.min(components.length, ckCount); i++)
|
||||
builder.add(components[i]);
|
||||
return builder;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue