Unify IndexMetadat model btw C* 3 and earlier.

PYTHON-404
This commit is contained in:
Adam Holmberg
2015-09-30 16:16:29 -05:00
parent 4ce576f9bb
commit a82f25e0b9
2 changed files with 61 additions and 112 deletions

View File

@@ -1300,64 +1300,51 @@ class IndexMetadata(object):
"""
A representation of a secondary index on a column.
"""
keyspace_name = None
""" A string name of the keyspace. """
column = None
"""
The column (:class:`.ColumnMetadata`) this index is on.
"""
table_name = None
""" A string name of the table this index is on. """
name = None
""" A string name for the index. """
index_type = None
""" A string representing the type of index. """
kind = None
""" A string representing the kind of index (COMPOSITE, CUSTOM,...). """
index_options = {}
""" A dict of index options. """
def __init__(self, column_metadata, index_name=None, index_type=None, index_options={}):
self.column = column_metadata
def __init__(self, keyspace_name, table_name, index_name, kind, index_options):
self.keyspace_name = keyspace_name
self.table_name = table_name
self.name = index_name
self.index_type = index_type
self.kind = kind
self.index_options = index_options
def as_cql_query(self):
"""
Returns a CQL query that can be used to recreate this index.
"""
table = self.column.table
if self.index_type != "CUSTOM":
index_target = protect_name(self.column.name)
if self.index_options is not None:
option_keys = self.index_options.keys()
if "index_keys" in option_keys:
index_target = 'keys(%s)' % (index_target,)
elif "index_values" in option_keys:
# don't use any "function" for collection values
pass
else:
# it might be a "full" index on a frozen collection, but
# we need to check the data type to verify that, because
# there is no special index option for full-collection
# indexes.
data_type = self.column.data_type
collection_types = ('map', 'set', 'list')
if data_type.typename == "frozen" and data_type.subtypes[0].typename in collection_types:
# no index option for full-collection index
index_target = 'full(%s)' % (index_target,)
options = dict(self.index_options)
index_target = options.pop("target")
if self.kind != "CUSTOM":
return "CREATE INDEX %s ON %s.%s (%s)" % (
self.name, # Cassandra doesn't like quoted index names for some reason
protect_name(table.keyspace.name),
protect_name(table.name),
protect_name(self.keyspace_name),
protect_name(self.table_name),
index_target)
else:
return "CREATE CUSTOM INDEX %s ON %s.%s (%s) USING '%s'" % (
class_name = options.pop("class_name")
ret = "CREATE CUSTOM INDEX %s ON %s.%s (%s) USING '%s'" % (
self.name, # Cassandra doesn't like quoted index names for some reason
protect_name(table.keyspace.name),
protect_name(table.name),
protect_name(self.column.name),
self.index_options["class_name"])
protect_name(self.keyspace_name),
protect_name(self.table_name),
index_target,
class_name)
if options:
ret += " WITH OPTIONS = %s" % Encoder().cql_encode_all_types(options)
return ret
def export_as_string(self):
"""
@@ -1932,13 +1919,32 @@ class SchemaParserV22(_SchemaParser):
@staticmethod
def _build_index_metadata(column_metadata, row):
index_name = row.get("index_name")
index_type = row.get("index_type")
if index_name or index_type:
kind = row.get("index_type")
if index_name or kind:
options = row.get("index_options")
index_options = json.loads(options) if options else None
return IndexMetadata(column_metadata, index_name, index_type, index_options)
else:
return None
options = json.loads(options) if options else {}
options = options or {} # if the json parsed to None, init empty dict
# generate a CQL index identity string
target = protect_name(column_metadata.name)
if kind != "CUSTOM":
if "index_keys" in options:
target = 'keys(%s)' % (target,)
elif "index_values" in options:
# don't use any "function" for collection values
pass
else:
# it might be a "full" index on a frozen collection, but
# we need to check the data type to verify that, because
# there is no special index option for full-collection
# indexes.
data_type = column_metadata.data_type
collection_types = ('map', 'set', 'list')
if data_type.typename == "frozen" and data_type.subtypes[0].typename in collection_types:
# no index option for full-collection index
target = 'full(%s)' % (target,)
options['target'] = target
return IndexMetadata(column_metadata.table.keyspace_name, column_metadata.table.name, index_name, kind, options)
@staticmethod
def _build_trigger_metadata(table_metadata, row):
@@ -2220,7 +2226,7 @@ class SchemaParserV3(SchemaParserV22):
kind = row.get("kind")
if index_name or kind:
index_options = row.get("options")
return IndexMetadataV3(table_metadata.keyspace_name, table_metadata.name, index_name, kind, index_options)
return IndexMetadata(table_metadata.keyspace_name, table_metadata.name, index_name, kind, index_options)
else:
return None
@@ -2310,63 +2316,6 @@ class TableMetadataV3(TableMetadata):
return list(sorted(ret))
class IndexMetadataV3(object):
"""
A representation of a secondary index on a column.
"""
keyspace_name = None
""" A string name of the keyspace. """
table_name = None
""" A string name of the table this index is on. """
name = None
""" A string name for the index. """
kind = None
""" A string representing the kind of index (COMPOSITE, CUSTOM,...). """
index_options = {}
""" A dict of index options. """
def __init__(self, keyspace_name, table_name, index_name, kind, index_options):
self.keyspace_name = keyspace_name
self.table_name = table_name
self.name = index_name
self.kind = kind
self.index_options = index_options
def as_cql_query(self):
"""
Returns a CQL query that can be used to recreate this index.
"""
options = dict(self.index_options)
index_target = options.pop("target")
if self.kind != "CUSTOM":
return "CREATE INDEX %s ON %s.%s (%s)" % (
self.name, # Cassandra doesn't like quoted index names for some reason
protect_name(self.keyspace_name),
protect_name(self.table_name),
index_target)
else:
class_name = options.pop("class_name")
ret = "CREATE CUSTOM INDEX %s ON %s.%s (%s) USING '%s'" % (
self.name, # Cassandra doesn't like quoted index names for some reason
protect_name(self.keyspace_name),
protect_name(self.table_name),
index_target,
class_name)
if options:
ret += " WITH OPTIONS = %s" % Encoder().cql_encode_all_types(options)
return ret
def export_as_string(self):
"""
Returns a CQL query string that can be used to recreate this index.
"""
return self.as_cql_query() + ';'
class MaterializedViewMetadata(object):
"""
A representation of a materialized view on a table

View File

@@ -27,7 +27,7 @@ from cassandra import AlreadyExists, SignatureDescriptor, UserFunctionDescriptor
from cassandra.cluster import Cluster
from cassandra.cqltypes import DoubleType, Int32Type, ListType, UTF8Type, MapType, LongType
from cassandra.encoder import Encoder
from cassandra.metadata import (Metadata, KeyspaceMetadata, IndexMetadata, IndexMetadataV3,
from cassandra.metadata import (Metadata, KeyspaceMetadata, IndexMetadata,
Token, MD5Token, TokenMap, murmur3, Function, Aggregate, protect_name, protect_names,
get_schema_parser)
from cassandra.policies import SimpleConvictionPolicy
@@ -1301,10 +1301,10 @@ class IndexMapTests(unittest.TestCase):
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
table_meta = ks_meta.tables[self.table_name]
self.assertIsInstance(ks_meta.indexes['a_idx'], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(ks_meta.indexes['b_idx'], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(table_meta.indexes['a_idx'], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(table_meta.indexes['b_idx'], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(ks_meta.indexes['a_idx'], IndexMetadata)
self.assertIsInstance(ks_meta.indexes['b_idx'], IndexMetadata)
self.assertIsInstance(table_meta.indexes['a_idx'], IndexMetadata)
self.assertIsInstance(table_meta.indexes['b_idx'], IndexMetadata)
# both indexes updated when index dropped
self.session.execute("DROP INDEX a_idx")
@@ -1315,9 +1315,9 @@ class IndexMapTests(unittest.TestCase):
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
table_meta = ks_meta.tables[self.table_name]
self.assertNotIn('a_idx', ks_meta.indexes)
self.assertIsInstance(ks_meta.indexes['b_idx'], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(ks_meta.indexes['b_idx'], IndexMetadata)
self.assertNotIn('a_idx', table_meta.indexes)
self.assertIsInstance(table_meta.indexes['b_idx'], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(table_meta.indexes['b_idx'], IndexMetadata)
# keyspace index updated when table dropped
self.drop_basic_table()
@@ -1333,15 +1333,15 @@ class IndexMapTests(unittest.TestCase):
self.session.execute("CREATE INDEX %s ON %s (a)" % (idx, self.table_name))
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
table_meta = ks_meta.tables[self.table_name]
self.assertIsInstance(ks_meta.indexes[idx], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(table_meta.indexes[idx], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(ks_meta.indexes[idx], IndexMetadata)
self.assertIsInstance(table_meta.indexes[idx], IndexMetadata)
self.session.execute('ALTER KEYSPACE %s WITH durable_writes = false' % self.keyspace_name)
old_meta = ks_meta
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
self.assertIsNot(ks_meta, old_meta)
table_meta = ks_meta.tables[self.table_name]
self.assertIsInstance(ks_meta.indexes[idx], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(table_meta.indexes[idx], (IndexMetadata, IndexMetadataV3))
self.assertIsInstance(ks_meta.indexes[idx], IndexMetadata)
self.assertIsInstance(table_meta.indexes[idx], IndexMetadata)
self.drop_basic_table()