diff --git a/bin/cqlsh.py b/bin/cqlsh.py index 529b1c8e64..41fa4fdece 100644 --- a/bin/cqlsh.py +++ b/bin/cqlsh.py @@ -289,12 +289,13 @@ cqlsh_extra_syntax_rules = r''' | "KEYSPACE" ksname=? | ( "COLUMNFAMILY" | "TABLE" ) cf= | "INDEX" idx= + | "MATERIALIZED" "VIEW" mv= | ( "COLUMNFAMILIES" | "TABLES" ) | "FULL"? "SCHEMA" | "CLUSTER" | "TYPES" | "TYPE" ut= - | (ksname= | cf= | idx=)) + | (ksname= | cf= | idx= | mv=)) ; ::= "CONSISTENCY" ( level= )? @@ -459,6 +460,10 @@ class IndexNotFound(Exception): pass +class MaterializedViewNotFound(Exception): + pass + + class ObjectNotFound(Exception): pass @@ -792,6 +797,12 @@ class Shell(cmd.Cmd): return map(str, self.get_keyspace_meta(ksname).tables.keys()) + def get_materialized_view_names(self, ksname=None): + if ksname is None: + ksname = self.current_keyspace + + return map(str, self.get_keyspace_meta(ksname).views.keys()) + def get_index_names(self, ksname=None): if ksname is None: ksname = self.current_keyspace @@ -898,6 +909,15 @@ class Shell(cmd.Cmd): return ksmeta.indexes[idxname] + def get_view_meta(self, ksname, viewname): + if ksname is None: + ksname = self.current_keyspace + ksmeta = self.get_keyspace_meta(ksname) + + if viewname not in ksmeta.views: + raise MaterializedViewNotFound("Materialized view %r not found" % viewname) + return ksmeta.views[viewname] + def get_object_meta(self, ks, name): if name is None: if ks and ks in self.conn.metadata.keyspaces: @@ -917,6 +937,8 @@ class Shell(cmd.Cmd): return ksmeta.tables[name] elif name in ksmeta.indexes: return ksmeta.indexes[name] + elif name in ksmeta.views: + return ksmeta.views[name] raise ObjectNotFound("%r not found in keyspace %r" % (name, ks)) @@ -1151,7 +1173,22 @@ class Shell(cmd.Cmd): return result - def parse_for_table_meta(self, query_string): + def parse_for_select_meta(self, query_string): + try: + parsed = cqlruleset.cql_parse(query_string)[1] + except IndexError: + return None + ks = self.cql_unprotect_name(parsed.get_binding('ksname', None)) + name = self.cql_unprotect_name(parsed.get_binding('cfname', None)) + try: + return self.get_table_meta(ks, name) + except ColumnFamilyNotFound: + try: + return self.get_view_meta(ks, name) + except MaterializedViewNotFound: + raise ObjectNotFound("%r not found in keyspace %r" % (name, ks)) + + def parse_for_update_meta(self, query_string): try: parsed = cqlruleset.cql_parse(query_string)[1] except IndexError: @@ -1182,7 +1219,7 @@ class Shell(cmd.Cmd): return False, None if statement.query_string[:6].lower() == 'select': - self.print_result(rows, self.parse_for_table_meta(statement.query_string)) + self.print_result(rows, self.parse_for_select_meta(statement.query_string)) elif statement.query_string.lower().startswith("list users") or statement.query_string.lower().startswith("list roles"): self.print_result(rows, self.get_table_meta('system_auth', 'roles')) elif statement.query_string.lower().startswith("list"): @@ -1190,7 +1227,7 @@ class Shell(cmd.Cmd): elif rows: # CAS INSERT/UPDATE self.writeresult("") - self.print_static_result(rows, self.parse_for_table_meta(statement.query_string)) + self.print_static_result(rows, self.parse_for_update_meta(statement.query_string)) self.flush_output() return True, future @@ -1369,6 +1406,16 @@ class Shell(cmd.Cmd): out.write(self.get_index_meta(ksname, idxname).export_as_string()) out.write("\n") + def print_recreate_materialized_view(self, ksname, viewname, out): + """ + Output CQL commands which should be pasteable back into a CQL session + to recreate the given materialized view. + + Writes output to the given out stream. + """ + out.write(self.get_view_meta(ksname, viewname).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 @@ -1403,6 +1450,15 @@ class Shell(cmd.Cmd): self.print_recreate_index(ksname, idxname, sys.stdout) print + def describe_materialized_view(self, ksname, viewname): + if ksname is None: + ksname = self.current_keyspace + if ksname is None: + raise NoKeyspaceError("No keyspace specified and no current keyspace") + print + self.print_recreate_materialized_view(ksname, viewname, sys.stdout) + print + def describe_object(self, ks, name): print self.print_recreate_object(ks, name, sys.stdout) @@ -1564,6 +1620,12 @@ class Shell(cmd.Cmd): In some cases, there may be index metadata which is not representable and which will not be shown. + DESCRIBE MATERIALIZED VIEW + + Output CQL commands that could be used to recreate the given materialized view. + In some cases, there may be materialized view metadata which is not representable + and which will not be shown. + DESCRIBE CLUSTER Output information about the connected Cassandra cluster, such as the @@ -1596,7 +1658,8 @@ class Shell(cmd.Cmd): 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). + where object can be either a keyspace or a table or an index or a materialized + view (in this order). """ what = parsed.matched[1][1].lower() if what == 'functions': @@ -1631,6 +1694,10 @@ class Shell(cmd.Cmd): 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 == 'materialized' and parsed.matched[2][1].lower() == 'view': + ks = self.cql_unprotect_name(parsed.get_binding('ksname', None)) + mv = self.cql_unprotect_name(parsed.get_binding('mvname')) + self.describe_materialized_view(ks, mv) elif what in ('columnfamilies', 'tables'): self.describe_columnfamilies(self.current_keyspace) elif what == 'types': @@ -1650,6 +1717,8 @@ class Shell(cmd.Cmd): name = self.cql_unprotect_name(parsed.get_binding('cfname')) if not name: name = self.cql_unprotect_name(parsed.get_binding('idxname', None)) + if not name: + name = self.cql_unprotect_name(parsed.get_binding('mvname', None)) self.describe_object(ks, name) do_desc = do_describe diff --git a/lib/cassandra-driver-internal-only-3.0.0a2.post0-9f91a6d.zip b/lib/cassandra-driver-internal-only-3.0.0a2.post0-03085e6.zip similarity index 81% rename from lib/cassandra-driver-internal-only-3.0.0a2.post0-9f91a6d.zip rename to lib/cassandra-driver-internal-only-3.0.0a2.post0-03085e6.zip index ff4d150a35..e672bd9daa 100644 Binary files a/lib/cassandra-driver-internal-only-3.0.0a2.post0-9f91a6d.zip and b/lib/cassandra-driver-internal-only-3.0.0a2.post0-03085e6.zip differ diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py index e4b2bfae09..94b132fe12 100644 --- a/pylib/cqlshlib/cql3handling.py +++ b/pylib/cqlshlib/cql3handling.py @@ -283,6 +283,8 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ; ::= ( ksname= dot="." )? cfname= ; + ::= ( ksname= dot="." )? mvname= ; + ::= ( ksname= dot="." )? utname= ; ::= ksname= ; @@ -559,6 +561,7 @@ def cf_ks_name_completer(ctxt, cass): return [maybe_escape_name(ks) + '.' for ks in cass.get_keyspace_names()] completer_for('columnFamilyName', 'ksname')(cf_ks_name_completer) +completer_for('materializedViewName', 'ksname')(cf_ks_name_completer) def cf_ks_dot_completer(ctxt, cass): @@ -568,6 +571,7 @@ def cf_ks_dot_completer(ctxt, cass): return [] completer_for('columnFamilyName', 'dot')(cf_ks_dot_completer) +completer_for('materializedViewName', 'dot')(cf_ks_dot_completer) @completer_for('columnFamilyName', 'cfname') @@ -583,6 +587,20 @@ def cf_name_completer(ctxt, cass): raise return map(maybe_escape_name, cfnames) + +@completer_for('materializedViewName', 'mvname') +def mv_name_completer(ctxt, cass): + ks = ctxt.get_binding('ksname', None) + if ks is not None: + ks = dequote_name(ks) + try: + mvnames = cass.get_materialized_view_names(ks) + except Exception: + if ks is None: + return () + raise + return map(maybe_escape_name, mvnames) + completer_for('userTypeName', 'ksname')(cf_ks_name_completer) completer_for('userTypeName', 'dot')(cf_ks_dot_completer) @@ -639,7 +657,7 @@ syntax_rules += r''' ::= "USE" ; ::= "SELECT" ( "JSON" )? - "FROM" cf= + "FROM" (cf= | mv=) ( "WHERE" )? ( "ORDER" "BY" ( "," )* )? ( "LIMIT" limit= )? @@ -1133,7 +1151,7 @@ syntax_rules += r''' ( "USING" ( "WITH" "OPTIONS" "=" )? )? ; - ::= "CREATE" "MATERIALIZED" "VIEW" ("IF" "NOT" "EXISTS")? ? + ::= "CREATE" "MATERIALIZED" "VIEW" ("IF" "NOT" "EXISTS")? ? "AS" "PRIMARY" "KEY" ; @@ -1195,7 +1213,7 @@ syntax_rules += r''' ::= "DROP" "INDEX" ("IF" "EXISTS")? idx= ; - ::= "DROP" "MATERIALIZED" "VIEW" ("IF" "EXISTS")? mv= + ::= "DROP" "MATERIALIZED" "VIEW" ("IF" "EXISTS")? mv= ; ::= "DROP" "TYPE" ut= diff --git a/src/java/org/apache/cassandra/service/MigrationListener.java b/src/java/org/apache/cassandra/service/MigrationListener.java index f4b3e7cdad..9e240ead29 100644 --- a/src/java/org/apache/cassandra/service/MigrationListener.java +++ b/src/java/org/apache/cassandra/service/MigrationListener.java @@ -83,6 +83,7 @@ public abstract class MigrationListener public void onDropView(String ksName, String viewName) { + onDropColumnFamily(ksName, viewName); } public void onDropUserType(String ksName, String typeName)