diff --git a/cassandra/cqltypes.py b/cassandra/cqltypes.py index 100c2968..15169621 100644 --- a/cassandra/cqltypes.py +++ b/cassandra/cqltypes.py @@ -129,13 +129,13 @@ def parse_casstype_args(typestring): elif tok == ')': types, names = args.pop() prev_types, prev_names = args[-1] - prev_types[-1] = prev_types[-1].apply_parameters(types, names) else: types, names = args[-1] - if ':' in tok: - name, tok = tok.rsplit(':', 1) - names.append(name) + parts = re.split(':|=>', tok) + tok = parts.pop() + if parts: + names.append(parts[0]) else: names.append(None) @@ -294,7 +294,7 @@ class _CassandraType(object): newname = cls.cass_parameterized_type_with(subtypes) if six.PY2 and isinstance(newname, unicode): newname = newname.encode('utf-8') - return type(newname, (cls,), {'subtypes': subtypes, 'cassname': cls.cassname}) + return type(newname, (cls,), {'subtypes': subtypes, 'cassname': cls.cassname, 'fieldnames': names}) @classmethod def cql_parameterized_type(cls): diff --git a/cassandra/metadata.py b/cassandra/metadata.py index 3fdce696..c80e1c33 100644 --- a/cassandra/metadata.py +++ b/cassandra/metadata.py @@ -264,6 +264,10 @@ class Metadata(object): is_compact = True has_value = column_aliases or not cf_col_rows clustering_size = num_column_name_components + + # Some thrift tables define names in composite types (see PYTHON-192) + if not column_aliases and hasattr(comparator, 'fieldnames'): + column_aliases = comparator.fieldnames else: is_compact = True if column_aliases or not cf_col_rows: @@ -356,6 +360,7 @@ class Metadata(object): table_meta.options = self._build_table_options(row) table_meta.is_compact_storage = is_compact + return table_meta def _build_table_options(self, row): @@ -922,6 +927,18 @@ class TableMetadata(object): A dict mapping trigger names to :class:`.TriggerMetadata` instances. """ + @property + def is_cql_compatible(self): + """ + A boolean indicating if this table can be represented as CQL in export + """ + # no such thing as DCT in CQL + incompatible = issubclass(self.comparator, types.DynamicCompositeType) + # no compact storage with more than one column beyond PK + incompatible |= self.is_compact_storage and len(self.columns) > len(self.primary_key) + 1 + + return not incompatible + def __init__(self, keyspace_metadata, name, partition_key=None, clustering_key=None, columns=None, triggers=None, options=None): self.keyspace = keyspace_metadata self.name = name @@ -938,6 +955,18 @@ class TableMetadata(object): along with all indexes on it. The returned string is formatted to be human readable. """ + if self.is_cql_compatible: + ret = self.all_as_cql() + else: + # If we can't produce this table with CQL, comment inline + ret = "/*\nWarning: Table %s.%s omitted because it has constructs not compatible with CQL (was created via legacy API).\n" % \ + (self.keyspace.name, self.name) + ret += "\nApproximate structure, for reference:\n(this should not be used to reproduce this schema)\n\n%s" % self.all_as_cql() + ret += "\n*/" + + return ret + + def all_as_cql(self): ret = self.as_cql_query(formatted=True) ret += ";" @@ -947,7 +976,6 @@ class TableMetadata(object): for trigger_meta in self.triggers.values(): ret += "\n%s;" % (trigger_meta.as_cql_query(),) - return ret def as_cql_query(self, formatted=False): diff --git a/tests/integration/standard/test_metadata.py b/tests/integration/standard/test_metadata.py index d527e22b..344e5491 100644 --- a/tests/integration/standard/test_metadata.py +++ b/tests/integration/standard/test_metadata.py @@ -638,16 +638,22 @@ create column family composite_comp_with_col # is a bit strange, but it replays in CQL with desired results expected_string = """CREATE KEYSPACE legacy WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} AND durable_writes = true; +/* +Warning: Table legacy.composite_comp_with_col omitted because it has constructs not compatible with CQL (was created via legacy API). + +Approximate structure, for reference: +(this should not be used to reproduce this schema) + CREATE TABLE legacy.composite_comp_with_col ( key blob, - column0 't=>org.apache.cassandra.db.marshal.TimeUUIDType', - column1 'b=>org.apache.cassandra.db.marshal.BytesType', - column2 's=>org.apache.cassandra.db.marshal.UTF8Type', + t timeuuid, + b blob, + s text, "b@6869746d65776974686d75736963" blob, "b@6d616d6d616a616d6d61" blob, - PRIMARY KEY (key, column0, column1, column2) + PRIMARY KEY (key, t, b, s) ) WITH COMPACT STORAGE - AND CLUSTERING ORDER BY (column0 ASC, column1 ASC, column2 ASC) + AND CLUSTERING ORDER BY (t ASC, b ASC, s ASC) AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}' AND comment = 'Stores file meta data' AND compaction = {'min_threshold': '4', 'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32'} @@ -662,6 +668,7 @@ CREATE TABLE legacy.composite_comp_with_col ( AND speculative_retry = 'NONE'; CREATE INDEX idx_two ON legacy.composite_comp_with_col ("b@6869746d65776974686d75736963"); CREATE INDEX idx_one ON legacy.composite_comp_with_col ("b@6d616d6d616a616d6d61"); +*/ CREATE TABLE legacy.nested_composite_key ( key 'org.apache.cassandra.db.marshal.CompositeType(org.apache.cassandra.db.marshal.UUIDType, org.apache.cassandra.db.marshal.UTF8Type)', @@ -759,10 +766,16 @@ CREATE TABLE legacy.simple_no_col ( AND read_repair_chance = 0.0 AND speculative_retry = 'NONE'; +/* +Warning: Table legacy.composite_comp_no_col omitted because it has constructs not compatible with CQL (was created via legacy API). + +Approximate structure, for reference: +(this should not be used to reproduce this schema) + CREATE TABLE legacy.composite_comp_no_col ( key blob, - column1 'org.apache.cassandra.db.marshal.DynamicCompositeType(t=>org.apache.cassandra.db.marshal.TimeUUIDType, b=>org.apache.cassandra.db.marshal.BytesType, s=>org.apache.cassandra.db.marshal.UTF8Type)', - column2 's=>org.apache.cassandra.db.marshal.UTF8Type', + column1 'org.apache.cassandra.db.marshal.DynamicCompositeType(org.apache.cassandra.db.marshal.TimeUUIDType, org.apache.cassandra.db.marshal.BytesType, org.apache.cassandra.db.marshal.UTF8Type)', + column2 text, value blob, PRIMARY KEY (key, column1, column1, column2) ) WITH COMPACT STORAGE @@ -778,7 +791,8 @@ CREATE TABLE legacy.composite_comp_no_col ( AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND read_repair_chance = 0.0 - AND speculative_retry = 'NONE';""" + AND speculative_retry = 'NONE'; +*/""" ccm = get_cluster() ccm.run_cli(cli_script)