diff --git a/CHANGES.txt b/CHANGES.txt index fded5fc60b..70efc81f0b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,7 @@ 2.1.7 * Allow JMX over SSL directly from nodetool (CASSANDRA-9090) * Fix incorrect result for IN queries where column not found (CASSANDRA-9540) + * Enable describe on indices (CASSANDRA-7814) * ColumnFamilyStore.selectAndReference may block during compaction (CASSANDRA-9637) * Fix bug in cardinality check when compacting (CASSANDRA-9580) * Fix memory leak in Ref due to ConcurrentLinkedQueue.remove() behaviour (CASSANDRA-9549) diff --git a/bin/cqlsh b/bin/cqlsh index a4cc5d49e3..9f872f8d52 100755 --- a/bin/cqlsh +++ b/bin/cqlsh @@ -267,11 +267,13 @@ cqlsh_extra_syntax_rules = r''' ( "KEYSPACES" | "KEYSPACE" ksname=? | ( "COLUMNFAMILY" | "TABLE" ) cf= + | "INDEX" idx= | ( "COLUMNFAMILIES" | "TABLES" ) | "FULL"? "SCHEMA" | "CLUSTER" | "TYPES" - | "TYPE" ut=) + | "TYPE" ut= + | (ksname= | cf= | idx=)) ; ::= "CONSISTENCY" ( level= )? @@ -428,6 +430,11 @@ class KeyspaceNotFound(Exception): class ColumnFamilyNotFound(Exception): pass +class IndexNotFound(Exception): + pass + +class ObjectNotFound(Exception): + pass class VersionNotSupported(Exception): pass @@ -738,12 +745,10 @@ class Shell(cmd.Cmd): return map(str, self.get_keyspace_meta(ksname).tables.keys()) def get_index_names(self, ksname=None): - idxnames = [] - for cfname in self.get_columnfamily_names(ksname=ksname): - for col in self.get_table_meta(ksname, cfname).columns.values(): - if col.index: - idxnames.append(col.index.name) - return idxnames + if ksname is None: + ksname = self.current_keyspace + + return map(str, self.get_keyspace_meta(ksname).indexes.keys()) def get_column_names(self, ksname, cfname): if ksname is None: @@ -801,6 +806,38 @@ class Shell(cmd.Cmd): return ksmeta.tables[tablename] + def get_index_meta(self, ksname, idxname): + if ksname is None: + ksname = self.current_keyspace + ksmeta = self.get_keyspace_meta(ksname) + + if idxname not in ksmeta.indexes: + raise IndexNotFound("Index %r not found" % idxname) + + return ksmeta.indexes[idxname] + + def get_object_meta(self, ks, name): + if name is None: + if ks and ks in self.conn.metadata.keyspaces: + return self.conn.metadata.keyspaces[ks] + elif self.current_keyspace is None: + raise ObjectNotFound("%r not found in keyspaces" % (ks)) + else: + name = ks + ks = self.current_keyspace + + if ks is None: + ks = self.current_keyspace + + ksmeta = self.get_keyspace_meta(ks) + + if name in ksmeta.tables: + return ksmeta.tables[name] + elif name in ksmeta.indexes: + return ksmeta.indexes[name] + + raise ObjectNotFound("%r not found in keyspace %r" % (name, ks)) + def get_usertypes_meta(self): data = self.session.execute("select * from system.schema_usertypes") if not data: @@ -1216,6 +1253,26 @@ class Shell(cmd.Cmd): out.write(self.get_table_meta(ksname, cfname).export_as_string()) out.write("\n") + def print_recreate_index(self, ksname, idxname, out): + """ + Output CQL commands which should be pasteable back into a CQL session + to recreate the given index. + + Writes output to the given out stream. + """ + out.write(self.get_index_meta(ksname, idxname).export_as_string()) + out.write("\n") + + def print_recreate_object(self, ks, name, out): + """ + Output CQL commands which should be pasteable back into a CQL session + to recreate the given object (ks, table or index). + + Writes output to the given out stream. + """ + out.write(self.get_object_meta(ks, name).export_as_string()) + out.write("\n") + def describe_keyspaces(self): print cmd.Cmd.columnize(self, protect_names(self.get_keyspace_names())) @@ -1233,6 +1290,16 @@ class Shell(cmd.Cmd): self.print_recreate_columnfamily(ksname, cfname, sys.stdout) print + def describe_index(self, ksname, idxname): + print + self.print_recreate_index(ksname, idxname, sys.stdout) + print + + def describe_object(self, ks, name): + print + self.print_recreate_object(ks, name, sys.stdout) + print + def describe_columnfamilies(self, ksname): print if ksname is None: @@ -1328,6 +1395,12 @@ class Shell(cmd.Cmd): In some cases, as above, there may be table metadata which is not representable and which will not be shown. + DESCRIBE INDEX + + Output CQL commands that could be used to recreate the given index. + In some cases, there may be index metadata which is not representable + and which will not be shown. + DESCRIBE CLUSTER Output information about the connected Cassandra cluster, such as the @@ -1340,6 +1413,12 @@ class Shell(cmd.Cmd): Output CQL commands that could be used to recreate the entire (non-system) schema. Works as though "DESCRIBE KEYSPACE k" was invoked for each non-system keyspace k. Use DESCRIBE FULL SCHEMA to include the system keyspaces. + + DESCRIBE + + Output CQL commands that could be used to recreate the entire object schema, + where object can be either a keyspace or a table or an index (in this order). + """ what = parsed.matched[1][1].lower() if what == 'keyspaces': @@ -1356,6 +1435,10 @@ class Shell(cmd.Cmd): ks = self.cql_unprotect_name(parsed.get_binding('ksname', None)) cf = self.cql_unprotect_name(parsed.get_binding('cfname')) self.describe_columnfamily(ks, cf) + elif what == 'index': + ks = self.cql_unprotect_name(parsed.get_binding('ksname', None)) + idx = self.cql_unprotect_name(parsed.get_binding('idxname', None)) + self.describe_index(ks, idx) elif what in ('columnfamilies', 'tables'): self.describe_columnfamilies(self.current_keyspace) elif what == 'types': @@ -1370,6 +1453,12 @@ class Shell(cmd.Cmd): self.describe_schema(False) elif what == 'full' and parsed.matched[2][1].lower() == 'schema': self.describe_schema(True) + elif what: + ks = self.cql_unprotect_name(parsed.get_binding('ksname', None)) + name = self.cql_unprotect_name(parsed.get_binding('cfname')) + if not name: + name = self.cql_unprotect_name(parsed.get_binding('idxname', None)) + self.describe_object(ks, name) do_desc = do_describe def do_copy(self, parsed): diff --git a/lib/cassandra-driver-internal-only-2.5.1.zip b/lib/cassandra-driver-internal-only-2.5.1.zip deleted file mode 100644 index ee6ace0f97..0000000000 Binary files a/lib/cassandra-driver-internal-only-2.5.1.zip and /dev/null differ diff --git a/lib/cassandra-driver-internal-only-2.6.0c1.zip b/lib/cassandra-driver-internal-only-2.6.0c1.zip new file mode 100644 index 0000000000..0e77468cc6 Binary files /dev/null and b/lib/cassandra-driver-internal-only-2.6.0c1.zip differ