Add DESCRIBE MATERIALIZED VIEW to cqlsh

patch by Stefania Alborghetti; reviewed by Benjamin Lerer for CASSANDRA-9961
This commit is contained in:
Stefania Alborghetti 2015-09-18 11:33:54 +02:00 committed by blerer
parent e6f23e63c5
commit aeee9f74d9
4 changed files with 96 additions and 8 deletions

View File

@ -289,12 +289,13 @@ cqlsh_extra_syntax_rules = r'''
| "KEYSPACE" ksname=<keyspaceName>?
| ( "COLUMNFAMILY" | "TABLE" ) cf=<columnFamilyName>
| "INDEX" idx=<indexName>
| "MATERIALIZED" "VIEW" mv=<materializedViewName>
| ( "COLUMNFAMILIES" | "TABLES" )
| "FULL"? "SCHEMA"
| "CLUSTER"
| "TYPES"
| "TYPE" ut=<userTypeName>
| (ksname=<keyspaceName> | cf=<columnFamilyName> | idx=<indexName>))
| (ksname=<keyspaceName> | cf=<columnFamilyName> | idx=<indexName> | mv=<materializedViewName>))
;
<consistencyCommand> ::= "CONSISTENCY" ( level=<consistencyLevel> )?
@ -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 <viewname>
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 <objname>
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

View File

@ -283,6 +283,8 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
<columnFamilyName> ::= ( ksname=<cfOrKsName> dot="." )? cfname=<cfOrKsName> ;
<materializedViewName> ::= ( ksname=<cfOrKsName> dot="." )? mvname=<cfOrKsName> ;
<userTypeName> ::= ( ksname=<cfOrKsName> dot="." )? utname=<cfOrKsName> ;
<keyspaceName> ::= ksname=<cfOrKsName> ;
@ -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'''
<useStatement> ::= "USE" <keyspaceName>
;
<selectStatement> ::= "SELECT" ( "JSON" )? <selectClause>
"FROM" cf=<columnFamilyName>
"FROM" (cf=<columnFamilyName> | mv=<materializedViewName>)
( "WHERE" <whereClause> )?
( "ORDER" "BY" <orderByClause> ( "," <orderByClause> )* )?
( "LIMIT" limit=<wholenumber> )?
@ -1133,7 +1151,7 @@ syntax_rules += r'''
( "USING" <stringLiteral> ( "WITH" "OPTIONS" "=" <mapLiteral> )? )?
;
<createMaterializedViewStatement> ::= "CREATE" "MATERIALIZED" "VIEW" ("IF" "NOT" "EXISTS")? <columnFamilyName>?
<createMaterializedViewStatement> ::= "CREATE" "MATERIALIZED" "VIEW" ("IF" "NOT" "EXISTS")? <materializedViewName>?
"AS" <selectStatement>
"PRIMARY" "KEY" <pkDef>
;
@ -1195,7 +1213,7 @@ syntax_rules += r'''
<dropIndexStatement> ::= "DROP" "INDEX" ("IF" "EXISTS")? idx=<indexName>
;
<dropMaterializedViewStatement> ::= "DROP" "MATERIALIZED" "VIEW" ("IF" "EXISTS")? mv=<columnFamilyName>
<dropMaterializedViewStatement> ::= "DROP" "MATERIALIZED" "VIEW" ("IF" "EXISTS")? mv=<materializedViewName>
;
<dropUserTypeStatement> ::= "DROP" "TYPE" ut=<userTypeName>

View File

@ -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)