@@ -1021,9 +1021,8 @@ def all_as_cql(self):
10211021 ret = self .as_cql_query (formatted = True )
10221022 ret += ";"
10231023
1024- for col_meta in self .columns .values ():
1025- if col_meta .index :
1026- ret += "\n %s;" % (col_meta .index .as_cql_query (),)
1024+ for index in self .indexes .values ():
1025+ ret += "\n %s;" % index .as_cql_query ()
10271026
10281027 for trigger_meta in self .triggers .values ():
10291028 ret += "\n %s;" % (trigger_meta .as_cql_query (),)
@@ -1223,64 +1222,84 @@ class IndexMetadata(object):
12231222 A representation of a secondary index on a column.
12241223 """
12251224
1226- column = None
1227- """
1228- The column (:class:`.ColumnMetadata`) this index is on.
1229- """
1225+ table = None
1226+ """ The :class:`.TableMetadata` this column belongs to. """
12301227
12311228 name = None
12321229 """ A string name for the index. """
12331230
1231+ columns = None
1232+ """
1233+ The set of columns (:class:`.ColumnMetadata`) this index is on
1234+ (may be empty for per-row indexes).
1235+
1236+ .. versionchanged:: 3.0.0
1237+
1238+ Was previously a singular column
1239+ """
1240+
12341241 index_type = None
12351242 """ A string representing the type of index. """
12361243
1237- index_options = {}
1244+ index_options = None
12381245 """ A dict of index options. """
12391246
1240- def __init__ (self , column_metadata , index_name = None , index_type = None , index_options = {}):
1241- self .column = column_metadata
1247+ target_type = None
1248+ """
1249+ String target type (COLUMN, ROW, etc), if available
1250+
1251+ .. versionadded:: 3.0.0
1252+ """
1253+
1254+ def __init__ (self , table_metadata , index_name = None , index_type = None , index_options = None , target_columns = None , target_type = None ):
1255+ self .table = table_metadata
12421256 self .name = index_name
1257+ self .columns = set (table_metadata .columns [col_name ] for col_name in target_columns ) if target_columns else set ()
12431258 self .index_type = index_type
1244- self .index_options = index_options
1259+ self .index_options = index_options or {}
1260+ self .target_type = target_type
12451261
12461262 def as_cql_query (self ):
12471263 """
12481264 Returns a CQL query that can be used to recreate this index.
12491265 """
1250- table = self .column .table
12511266 if self .index_type != "CUSTOM" :
1252- index_target = protect_name (self .column .name )
1253- if self .index_options is not None :
1254- option_keys = self .index_options .keys ()
1255- if "index_keys" in option_keys :
1256- index_target = 'keys(%s)' % (index_target ,)
1257- elif "index_values" in option_keys :
1258- # don't use any "function" for collection values
1259- pass
1260- else :
1261- # it might be a "full" index on a frozen collection, but
1262- # we need to check the data type to verify that, because
1263- # there is no special index option for full-collection
1264- # indexes.
1265- data_type = self .column .data_type
1266- collection_types = ('map' , 'set' , 'list' )
1267- if data_type .typename == "frozen" and data_type .subtypes [0 ].typename in collection_types :
1268- # no index option for full-collection index
1269- index_target = 'full(%s)' % (index_target ,)
1270-
1271- return "CREATE INDEX %s ON %s.%s (%s)" % (
1267+ target_fmt = "CREATE INDEX %s ON %s.%s (%%s)" % (
12721268 self .name , # Cassandra doesn't like quoted index names for some reason
1273- protect_name (table .keyspace .name ),
1274- protect_name (table .name ),
1275- index_target )
1269+ protect_name (self .table .keyspace .name ),
1270+ protect_name (self .table .name ))
12761271 else :
1277- return "CREATE CUSTOM INDEX %s ON %s.%s (%s) USING '%s'" % (
1272+ target_fmt = "CREATE CUSTOM INDEX %s ON %s.%s (% %s) USING '%s'" % (
12781273 self .name , # Cassandra doesn't like quoted index names for some reason
1279- protect_name (table .keyspace .name ),
1280- protect_name (table .name ),
1281- protect_name (self .column .name ),
1274+ protect_name (self .table .keyspace .name ),
1275+ protect_name (self .table .name ),
12821276 self .index_options ["class_name" ])
12831277
1278+ # TODO: when CASSANDRA-10124 lands, we'll need to loop over self.columns
1279+ # not updating yet because it seems like index_options will have to change to
1280+ # specify different options per column(?)
1281+ # Also, grammar will be changing for indexes with no target columns. This is TBD
1282+ col = tuple (self .columns )[0 ] if self .columns else None
1283+ index_target = protect_name (col .name ) if col else ''
1284+ if col and self .index_options is not None :
1285+ option_keys = self .index_options .keys ()
1286+ if "index_keys" in option_keys :
1287+ index_target = 'keys(%s)' % (index_target ,)
1288+ elif "index_values" in option_keys :
1289+ # don't use any "function" for collection values
1290+ pass
1291+ else :
1292+ # it might be a "full" index on a frozen collection, but
1293+ # we need to check the data type to verify that, because
1294+ # there is no special index option for full-collection
1295+ # indexes.
1296+ data_type = col .data_type
1297+ collection_types = ('map' , 'set' , 'list' )
1298+ if data_type .typename == "frozen" and data_type .subtypes [0 ].typename in collection_types :
1299+ # no index option for full-collection index
1300+ index_target = 'full(%s)' % (index_target ,)
1301+ return target_fmt % index_target
1302+
12841303 def export_as_string (self ):
12851304 """
12861305 Returns a CQL query string that can be used to recreate this index.
@@ -1605,7 +1624,6 @@ def get_table(self, keyspace, table):
16051624 def get_type (self , keyspace , type ):
16061625 where_clause = " WHERE keyspace_name = '%s' AND type_name = '%s'" % (keyspace , type )
16071626 return self ._query_build_row (self ._SELECT_TYPES + where_clause , self ._build_user_type )
1608-
16091627 def get_function (self , keyspace , function ):
16101628 where_clause = " WHERE keyspace_name = '%s' AND function_name = '%s' AND signature = [%s]" \
16111629 % (keyspace , function .name , ',' .join ("'%s'" % t for t in function .type_signature ))
@@ -1793,6 +1811,10 @@ def _build_table_metadata(self, row, col_rows=None, trigger_rows=None):
17931811 for col_row in col_rows :
17941812 column_meta = self ._build_column_metadata (table_meta , col_row )
17951813 table_meta .columns [column_meta .name ] = column_meta
1814+ index_meta = self ._build_index_metadata (column_meta , col_row )
1815+ column_meta .index = index_meta
1816+ if index_meta :
1817+ table_meta .indexes [index_meta .name ] = index_meta
17961818
17971819 for trigger_row in trigger_rows :
17981820 trigger_meta = self ._build_trigger_metadata (table_meta , trigger_row )
@@ -1823,10 +1845,6 @@ def _build_column_metadata(self, table_metadata, row):
18231845 data_type = types .lookup_casstype (row ["validator" ])
18241846 is_static = row .get ("type" , None ) == "static"
18251847 column_meta = ColumnMetadata (table_metadata , name , data_type , is_static = is_static )
1826- index_meta = self ._build_index_metadata (column_meta , row )
1827- column_meta .index = index_meta
1828- if index_meta :
1829- table_metadata .indexes [index_meta .name ] = index_meta
18301848 return column_meta
18311849
18321850 @staticmethod
@@ -1835,8 +1853,8 @@ def _build_index_metadata(column_metadata, row):
18351853 index_type = row .get ("index_type" )
18361854 if index_name or index_type :
18371855 options = row .get ("index_options" )
1838- index_options = json .loads (options ) if options else {}
1839- return IndexMetadata (column_metadata , index_name , index_type , index_options )
1856+ index_options = json .loads (options ) if options else None
1857+ return IndexMetadata (column_metadata . table , index_name , index_type , index_options , ( column_metadata . name ,) )
18401858 else :
18411859 return None
18421860
@@ -2073,10 +2091,10 @@ def _build_index_metadata(table_metadata, row):
20732091 index_name = row .get ("index_name" )
20742092 index_type = row .get ("index_type" )
20752093 if index_name or index_type :
2076- index_options = dict ( row .get ("options" ) or {} )
2077- target_columns = row .get ('target_columns' ) or set ()
2094+ index_options = row .get ("options" )
2095+ target_columns = row .get ('target_columns' )
20782096 target_type = row .get ('target_type' )
2079- return IndexMetadataV3 (table_metadata , index_name , index_type , index_options , target_columns , target_type )
2097+ return IndexMetadata (table_metadata , index_name , index_type , index_options , target_columns , target_type )
20802098 else :
20812099 return None
20822100
@@ -2150,47 +2168,6 @@ def _make_option_strings(self):
21502168 return list (sorted (ret ))
21512169
21522170
2153- class IndexMetadataV3 (IndexMetadata ):
2154-
2155- table = None
2156- """
2157- The table (:class:`.TableMetadata`) this index is on.
2158- """
2159-
2160- columns = None
2161- """
2162- The set of columns (:class:`.ColumnMetadata`) this index is on
2163- (may be empty for per-row indexes).
2164- """
2165-
2166- name = None
2167- """ A string name for the index. """
2168-
2169- index_type = None
2170- """ A string representing the type of index. """
2171-
2172- index_options = {}
2173- """ A dict of index options. """
2174-
2175- target_columns = set ()
2176- """ A set of target columns """
2177-
2178- target_type = None
2179-
2180- def __init__ (self , table_metadata , index_name , index_type , index_options , target_columns , target_type ):
2181- self .table = table_metadata
2182- self .name = index_name
2183- self .index_type = index_type
2184- self .index_options = index_options
2185- self .target_columns = set (target_columns )
2186- self .target_type = target_type
2187-
2188- # TODO: temporary until we diverge more with multiple column indexes
2189- # giving the base class what it expects
2190- self .column = table_metadata .columns [tuple (target_columns )[0 ]]
2191- self .column .index = self
2192-
2193-
21942171def get_schema_parser (connection , timeout ):
21952172 server_version = connection .server_version
21962173 if server_version .startswith ('3' ):
0 commit comments