Skip to content

Commit 25a09dc

Browse files
committed
Merge pull request apache#284 from datastax/PYTHON-241
PYTHON-241 - Meta indexes for Index Meta
2 parents e8dc87f + a84d4c0 commit 25a09dc

2 files changed

Lines changed: 134 additions & 9 deletions

File tree

cassandra/metadata.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,8 @@ def rebuild_schema(self, ks_results, type_results, cf_results, col_results, trig
122122
keyspace_col_rows = col_def_rows.get(keyspace_meta.name, {})
123123
keyspace_trigger_rows = trigger_rows.get(keyspace_meta.name, {})
124124
for table_row in cf_def_rows.get(keyspace_meta.name, []):
125-
table_meta = self._build_table_metadata(
126-
keyspace_meta, table_row, keyspace_col_rows,
127-
keyspace_trigger_rows)
128-
keyspace_meta.tables[table_meta.name] = table_meta
125+
table_meta = self._build_table_metadata(keyspace_meta, table_row, keyspace_col_rows, keyspace_trigger_rows)
126+
keyspace_meta._add_table_metadata(table_meta)
129127

130128
for usertype_row in usertype_rows.get(keyspace_meta.name, []):
131129
usertype = self._build_usertype(keyspace_meta.name, usertype_row)
@@ -160,6 +158,7 @@ def keyspace_changed(self, keyspace, ks_results):
160158
if old_keyspace_meta:
161159
keyspace_meta.tables = old_keyspace_meta.tables
162160
keyspace_meta.user_types = old_keyspace_meta.user_types
161+
keyspace_meta.indexes = old_keyspace_meta.indexes
163162
if (keyspace_meta.replication_strategy != old_keyspace_meta.replication_strategy):
164163
self._keyspace_updated(keyspace)
165164
else:
@@ -184,12 +183,11 @@ def table_changed(self, keyspace, table, cf_results, col_results, triggers_resul
184183

185184
if not cf_results:
186185
# the table was removed
187-
keyspace_meta.tables.pop(table, None)
186+
keyspace_meta._drop_table_metadata(table)
188187
else:
189188
assert len(cf_results) == 1
190-
keyspace_meta.tables[table] = self._build_table_metadata(
191-
keyspace_meta, cf_results[0], {table: col_results},
192-
{table: triggers_result})
189+
table_meta = self._build_table_metadata(keyspace_meta, cf_results[0], {table: col_results}, {table: triggers_result})
190+
keyspace_meta._add_table_metadata(table_meta)
193191

194192
def _keyspace_added(self, ksname):
195193
if self.token_map:
@@ -385,6 +383,8 @@ def _build_column_metadata(self, table_metadata, row):
385383
column_meta = ColumnMetadata(table_metadata, name, data_type, is_static=is_static)
386384
index_meta = self._build_index_metadata(column_meta, row)
387385
column_meta.index = index_meta
386+
if index_meta:
387+
table_metadata.indexes[index_meta.name] = index_meta
388388
return column_meta
389389

390390
def _build_index_metadata(self, column_metadata, row):
@@ -733,6 +733,11 @@ class KeyspaceMetadata(object):
733733
A map from table names to instances of :class:`~.TableMetadata`.
734734
"""
735735

736+
indexes = None
737+
"""
738+
A dict mapping index names to :class:`.IndexMetadata` instances.
739+
"""
740+
736741
user_types = None
737742
"""
738743
A map from user-defined type names to instances of :class:`~cassandra.metadata..UserType`.
@@ -745,6 +750,7 @@ def __init__(self, name, durable_writes, strategy_class, strategy_options):
745750
self.durable_writes = durable_writes
746751
self.replication_strategy = ReplicationStrategy.create(strategy_class, strategy_options)
747752
self.tables = {}
753+
self.indexes = {}
748754
self.user_types = {}
749755

750756
def export_as_string(self):
@@ -780,6 +786,18 @@ def resolve_user_types(self, key, types, user_type_strings):
780786
self.resolve_user_types(field_type.typename, types, user_type_strings)
781787
user_type_strings.append(user_type.as_cql_query(formatted=True))
782788

789+
def _add_table_metadata(self, table_metadata):
790+
self._drop_table_metadata(table_metadata.name)
791+
792+
self.tables[table_metadata.name] = table_metadata
793+
for index_name, index_metadata in six.iteritems(table_metadata.indexes):
794+
self.indexes[index_name] = index_metadata
795+
796+
def _drop_table_metadata(self, table_name):
797+
table_meta = self.tables.pop(table_name, None)
798+
if table_meta:
799+
for index_name in table_meta.indexes:
800+
self.indexes.pop(index_name, None)
783801

784802
class UserType(object):
785803
"""
@@ -884,6 +902,11 @@ def primary_key(self):
884902
A dict mapping column names to :class:`.ColumnMetadata` instances.
885903
"""
886904

905+
indexes = None
906+
"""
907+
A dict mapping index names to :class:`.IndexMetadata` instances.
908+
"""
909+
887910
is_compact_storage = False
888911

889912
options = None
@@ -945,6 +968,7 @@ def __init__(self, keyspace_metadata, name, partition_key=None, clustering_key=N
945968
self.partition_key = [] if partition_key is None else partition_key
946969
self.clustering_key = [] if clustering_key is None else clustering_key
947970
self.columns = OrderedDict() if columns is None else columns
971+
self.indexes = {}
948972
self.options = options
949973
self.comparator = None
950974
self.triggers = OrderedDict() if triggers is None else triggers
@@ -1242,6 +1266,12 @@ def as_cql_query(self):
12421266
protect_name(self.column.name),
12431267
self.index_options["class_name"])
12441268

1269+
def export_as_string(self):
1270+
"""
1271+
Returns a CQL query string that can be used to recreate this index.
1272+
"""
1273+
return self.as_cql_query() + ';'
1274+
12451275

12461276
class TokenMap(object):
12471277
"""

tests/integration/standard/test_metadata.py

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from cassandra import AlreadyExists
2626

2727
from cassandra.cluster import Cluster
28-
from cassandra.metadata import (Metadata, KeyspaceMetadata, TableMetadata,
28+
from cassandra.metadata import (Metadata, KeyspaceMetadata, TableMetadata, IndexMetadata,
2929
Token, MD5Token, TokenMap, murmur3)
3030
from cassandra.policies import SimpleConvictionPolicy
3131
from cassandra.pool import Host
@@ -900,3 +900,98 @@ def test_keyspace_alter(self):
900900
new_keyspace_meta = self.cluster.metadata.keyspaces[name]
901901
self.assertNotEqual(original_keyspace_meta, new_keyspace_meta)
902902
self.assertEqual(new_keyspace_meta.durable_writes, False)
903+
904+
905+
class IndexMapTests(unittest.TestCase):
906+
907+
keyspace_name = 'index_map_tests'
908+
909+
@property
910+
def table_name(self):
911+
return self._testMethodName.lower()
912+
913+
@classmethod
914+
def setup_class(cls):
915+
cls.cluster = Cluster(protocol_version=PROTOCOL_VERSION)
916+
cls.session = cls.cluster.connect()
917+
try:
918+
if cls.keyspace_name in cls.cluster.metadata.keyspaces:
919+
cls.session.execute("DROP KEYSPACE %s" % cls.keyspace_name)
920+
921+
cls.session.execute(
922+
"""
923+
CREATE KEYSPACE %s
924+
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};
925+
""" % cls.keyspace_name)
926+
cls.session.set_keyspace(cls.keyspace_name)
927+
except Exception:
928+
cls.cluster.shutdown()
929+
raise
930+
931+
@classmethod
932+
def teardown_class(cls):
933+
try:
934+
cls.session.execute("DROP KEYSPACE %s" % cls.keyspace_name)
935+
finally:
936+
cls.cluster.shutdown()
937+
938+
def create_basic_table(self):
939+
self.session.execute("CREATE TABLE %s (k int PRIMARY KEY, a int)" % self.table_name)
940+
941+
def drop_basic_table(self):
942+
self.session.execute("DROP TABLE %s" % self.table_name)
943+
944+
def test_index_updates(self):
945+
self.create_basic_table()
946+
947+
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
948+
table_meta = ks_meta.tables[self.table_name]
949+
self.assertNotIn('a_idx', ks_meta.indexes)
950+
self.assertNotIn('b_idx', ks_meta.indexes)
951+
self.assertNotIn('a_idx', table_meta.indexes)
952+
self.assertNotIn('b_idx', table_meta.indexes)
953+
954+
self.session.execute("CREATE INDEX a_idx ON %s (a)" % self.table_name)
955+
self.session.execute("ALTER TABLE %s ADD b int" % self.table_name)
956+
self.session.execute("CREATE INDEX b_idx ON %s (b)" % self.table_name)
957+
958+
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
959+
table_meta = ks_meta.tables[self.table_name]
960+
self.assertIsInstance(ks_meta.indexes['a_idx'], IndexMetadata)
961+
self.assertIsInstance(ks_meta.indexes['b_idx'], IndexMetadata)
962+
self.assertIsInstance(table_meta.indexes['a_idx'], IndexMetadata)
963+
self.assertIsInstance(table_meta.indexes['b_idx'], IndexMetadata)
964+
965+
# both indexes updated when index dropped
966+
self.session.execute("DROP INDEX a_idx")
967+
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
968+
table_meta = ks_meta.tables[self.table_name]
969+
self.assertNotIn('a_idx', ks_meta.indexes)
970+
self.assertIsInstance(ks_meta.indexes['b_idx'], IndexMetadata)
971+
self.assertNotIn('a_idx', table_meta.indexes)
972+
self.assertIsInstance(table_meta.indexes['b_idx'], IndexMetadata)
973+
974+
# keyspace index updated when table dropped
975+
self.drop_basic_table()
976+
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
977+
self.assertNotIn(self.table_name, ks_meta.tables)
978+
self.assertNotIn('a_idx', ks_meta.indexes)
979+
self.assertNotIn('b_idx', ks_meta.indexes)
980+
981+
def test_index_follows_alter(self):
982+
self.create_basic_table()
983+
984+
idx = self.table_name + '_idx'
985+
self.session.execute("CREATE INDEX %s ON %s (a)" % (idx, self.table_name))
986+
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
987+
table_meta = ks_meta.tables[self.table_name]
988+
self.assertIsInstance(ks_meta.indexes[idx], IndexMetadata)
989+
self.assertIsInstance(table_meta.indexes[idx], IndexMetadata)
990+
self.session.execute('ALTER KEYSPACE %s WITH durable_writes = false' % self.keyspace_name)
991+
old_meta = ks_meta
992+
ks_meta = self.cluster.metadata.keyspaces[self.keyspace_name]
993+
self.assertIsNot(ks_meta, old_meta)
994+
table_meta = ks_meta.tables[self.table_name]
995+
self.assertIsInstance(ks_meta.indexes[idx], IndexMetadata)
996+
self.assertIsInstance(table_meta.indexes[idx], IndexMetadata)
997+
self.drop_basic_table()

0 commit comments

Comments
 (0)