You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sm...@apache.org on 2022/11/07 17:04:20 UTC

[cassandra] branch trunk updated: Add missing cqlsh completion and round out cqlsh completion test coverage

This is an automated email from the ASF dual-hosted git repository.

smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new addc5748a7 Add missing cqlsh completion and round out cqlsh completion test coverage
addc5748a7 is described below

commit addc5748a74680b432ffabfe248245020902112c
Author: Brad Schoening <57...@users.noreply.github.com>
AuthorDate: Tue Oct 4 16:21:10 2022 -0400

    Add missing cqlsh completion and round out cqlsh completion test coverage
    
    IF (NOT) EXISTS is now also offered in cqlsh completion for CREATE TYPE, DROP TYPE, CREATE ROLE and DROP ROLE
    
    patch by Brad Schoening; reviewed by Stefan Miklosovic and Brandon Williams for CASSANDRA-16640
---
 CHANGES.txt                                  |   1 +
 pylib/cqlshlib/cql3handling.py               |  10 +--
 pylib/cqlshlib/cqlshhandling.py              |   4 +-
 pylib/cqlshlib/test/test_cqlsh_completion.py | 104 +++++++++++++++++++++------
 4 files changed, 91 insertions(+), 28 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index f557500753..74055ab158 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.2
+ * Offer IF (NOT) EXISTS in cqlsh completion for CREATE TYPE, DROP TYPE, CREATE ROLE and DROP ROLE (CASSANDRA-16640)
  * Nodetool bootstrap resume will now return an error if the operation fails (CASSANDRA-16491)
  * Disable resumable bootstrap by default (CASSANDRA-17679)
  * Include Git SHA in --verbose flag for nodetool version (CASSANDRA-17753)
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index 4675ecfd2b..c23e2d397d 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -1313,7 +1313,7 @@ syntax_rules += r'''
                                       ( "WITH" <cfamProperty> ( "AND" <cfamProperty> )* )?
                                     ;
 
-<createUserTypeStatement> ::= "CREATE" "TYPE" ( ks=<nonSystemKeyspaceName> dot="." )? typename=<cfOrKsName> "(" newcol=<cident> <storageType>
+<createUserTypeStatement> ::= "CREATE" "TYPE" ("IF" "NOT" "EXISTS")? ( ks=<nonSystemKeyspaceName> dot="." )? typename=<cfOrKsName> "(" newcol=<cident> <storageType>
                                 ( "," [newcolname]=<cident> <storageType> )*
                             ")"
                          ;
@@ -1377,7 +1377,7 @@ syntax_rules += r'''
 <dropMaterializedViewStatement> ::= "DROP" "MATERIALIZED" "VIEW" ("IF" "EXISTS")? mv=<materializedViewName>
                                   ;
 
-<dropUserTypeStatement> ::= "DROP" "TYPE" ut=<userTypeName>
+<dropUserTypeStatement> ::= "DROP" "TYPE" ( "IF" "EXISTS" )? ut=<userTypeName>
                           ;
 
 <dropFunctionStatement> ::= "DROP" "FUNCTION" ( "IF" "EXISTS" )? <userFunctionName>
@@ -1488,12 +1488,12 @@ syntax_rules += r'''
              | <unreservedKeyword>
              ;
 
-<createRoleStatement> ::= "CREATE" "ROLE" <rolename>
+<createRoleStatement> ::= "CREATE" "ROLE" ("IF" "NOT" "EXISTS")? <rolename>
                               ( "WITH" <roleProperty> ("AND" <roleProperty>)*)?
                         ;
 
 <alterRoleStatement> ::= "ALTER" "ROLE" ("IF" "EXISTS")? <rolename>
-                              ( "WITH" <roleProperty> ("AND" <roleProperty>)*)?
+                              ( "WITH" <roleProperty> ("AND" <roleProperty>)*)
                        ;
 
 <roleProperty> ::= (("HASHED")? "PASSWORD") "=" <stringLiteral>
@@ -1504,7 +1504,7 @@ syntax_rules += r'''
                  | "ACCESS" "TO" "ALL" "DATACENTERS"
                  ;
 
-<dropRoleStatement> ::= "DROP" "ROLE" <rolename>
+<dropRoleStatement> ::= "DROP" "ROLE" ("IF" "EXISTS")? <rolename>
                       ;
 
 <grantRoleStatement> ::= "GRANT" <rolename> "TO" <rolename>
diff --git a/pylib/cqlshlib/cqlshhandling.py b/pylib/cqlshlib/cqlshhandling.py
index cc8590a44f..57ec28b763 100644
--- a/pylib/cqlshlib/cqlshhandling.py
+++ b/pylib/cqlshlib/cqlshhandling.py
@@ -141,7 +141,7 @@ cqlsh_source_cmd_syntax_rules = r'''
 '''
 
 cqlsh_capture_cmd_syntax_rules = r'''
-<captureCommand> ::= "CAPTURE" ( fname=( <stringLiteral> | "OFF" ) )?
+<captureCommand> ::= "CAPTURE" ( fname=( <stringLiteral>) | "OFF" )?
                    ;
 '''
 
@@ -188,7 +188,7 @@ cqlsh_expand_cmd_syntax_rules = r'''
 '''
 
 cqlsh_paging_cmd_syntax_rules = r'''
-<pagingCommand> ::= "PAGING" ( switch=( "ON" | "OFF" | /[0-9]+/) )?
+<pagingCommand> ::= "PAGING" ( switch=( "ON" | "OFF" | <wholenumber>) )?
                   ;
 '''
 
diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py b/pylib/cqlshlib/test/test_cqlsh_completion.py
index 0619cf2781..c52624b1ed 100644
--- a/pylib/cqlshlib/test/test_cqlsh_completion.py
+++ b/pylib/cqlshlib/test/test_cqlsh_completion.py
@@ -128,7 +128,7 @@ class CqlshCompletionCase(BaseTestCase):
                                           split_completed_lines=split_completed_lines)
 
         if immediate:
-            msg = 'cqlsh completed %r, but we expected %r' % (completed, immediate)
+            msg = 'cqlsh completed %r (%d), but we expected %r (%d)' % (completed, len(completed), immediate, len(immediate))
             self.assertEqual(completed, immediate, msg=msg)
             return
 
@@ -489,8 +489,10 @@ class TestCqlshCompletion(CqlshCompletionCase):
                              "b = 'eggs'"),
                             choices=['AND', 'IF', ';'])
 
-    def test_complete_in_batch(self):
-        pass
+    def test_complete_in_begin_batch(self):
+        self.trycompletions('BEGIN ', choices=['BATCH', 'COUNTER', 'UNLOGGED'])
+        self.trycompletions('BEGIN BATCH ', choices=['DELETE', 'INSERT', 'UPDATE', 'USING'])
+        self.trycompletions('BEGIN BATCH INSERT ', immediate='INTO ' )
 
     def test_complete_in_create_keyspace(self):
         self.trycompletions('create keyspace ', '', choices=('<identifier>', '<quotedName>', 'IF'))
@@ -566,6 +568,23 @@ class TestCqlshCompletion(CqlshCompletionCase):
         self.trycompletions('DROP KEYSPACE I',
                             immediate='F EXISTS ' + self.cqlsh.keyspace + ' ;')
 
+    def test_complete_in_create_type(self):
+        self.trycompletions('CREATE TYPE foo ', choices=['(', '.'])
+
+    def test_complete_in_drop_type(self):
+        self.trycompletions('DROP TYPE ', choices=['IF', 'system_views.',
+                                                    'tags', 'system_traces.', 'system_distributed.',
+                                                    'phone_number', 'band_info_type', 'address', 'system.', 'system_schema.',
+                                                    'system_auth.', 'system_virtual_schema.', self.cqlsh.keyspace + '.'
+                                                    ])
+
+    def test_complete_in_create_trigger(self):
+        self.trycompletions('CREATE TRIGGER ', choices=['<identifier>', '<quotedName>', 'IF' ])
+        self.trycompletions('CREATE TRIGGER foo ', immediate='ON ' )
+        self.trycompletions('CREATE TRIGGER foo ON ', choices=['system.', 'system_auth.',
+                                           'system_distributed.', 'system_schema.', 'system_traces.', 'system_views.',
+                                           'system_virtual_schema.' ], other_choices_ok=True)
+
     def create_columnfamily_table_template(self, name):
         """Parameterized test for CREATE COLUMNFAMILY and CREATE TABLE. Since
         they're synonyms, they should have the same completion behavior, so this
@@ -827,24 +846,28 @@ class TestCqlshCompletion(CqlshCompletionCase):
                                      'aggmax'],
                             other_choices_ok=True)
 
-    # TODO: CASSANDRA-16640
-    # def test_complete_in_drop_columnfamily(self):
-    #     pass
-    #
-    # def test_complete_in_truncate(self):
-    #     pass
-    #
-    # def test_complete_in_alter_columnfamily(self):
-    #     pass
-    #
-    # def test_complete_in_use(self):
-    #     pass
-    #
-    # def test_complete_in_create_index(self):
-    #     pass
-    #
-    # def test_complete_in_drop_index(self):
-    #     pass
+    def test_complete_in_drop_table(self):
+        self.trycompletions('DROP T', choices=['TABLE', 'TRIGGER', 'TYPE'])
+        self.trycompletions('DROP TA', immediate='BLE ')
+
+    def test_complete_in_truncate(self):
+        self.trycompletions('TR', choices=['TRACING', 'TRUNCATE'])
+        self.trycompletions('TRU', immediate='NCATE ')
+        self.trycompletions('TRUNCATE T', choices=['TABLE', 'twenty_rows_composite_table', 'twenty_rows_table'])
+
+    def test_complete_in_use(self):
+        self.trycompletions('US', immediate='E ')
+        self.trycompletions('USE ', choices=[self.cqlsh.keyspace, 'system', 'system_auth',
+                                           'system_distributed', 'system_schema', 'system_traces', 'system_views',
+                                           'system_virtual_schema' ])
+
+    def test_complete_in_create_index(self):
+        self.trycompletions('CREATE I', immediate='NDEX ')
+        self.trycompletions('CREATE INDEX ', choices=['<new_index_name>', 'IF', 'ON'])
+        self.trycompletions('CREATE INDEX example ', immediate='ON ')
+
+    def test_complete_in_drop_index(self):
+        self.trycompletions('DROP I', immediate='NDEX ')
 
     def test_complete_in_alter_keyspace(self):
         self.trycompletions('ALTER KEY', 'SPACE ')
@@ -955,5 +978,44 @@ class TestCqlshCompletion(CqlshCompletionCase):
     def test_complete_in_alter_user(self):
         self.trycompletions('ALTER USER ', choices=['<identifier>', 'IF', '<pgStringLiteral>', '<quotedStringLiteral>'])
 
+    def test_complete_in_create_role(self):
+        self.trycompletions('CREATE ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])
+        self.trycompletions('CREATE ROLE IF ', immediate='NOT EXISTS ');
+        self.trycompletions('CREATE ROLE foo WITH ', choices=['ACCESS', 'HASHED', 'LOGIN', 'OPTIONS', 'PASSWORD', 'SUPERUSER'])
+        self.trycompletions('CREATE ROLE foo WITH HASHED ', immediate='PASSWORD = ');
+        self.trycompletions('CREATE ROLE foo WITH ACCESS TO ', choices=['ALL', 'DATACENTERS'])
+        self.trycompletions('CREATE ROLE foo WITH ACCESS TO ALL ', immediate='DATACENTERS ')
+
     def test_complete_in_alter_role(self):
         self.trycompletions('ALTER ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])
+        self.trycompletions('ALTER ROLE foo ', immediate='WITH ')
+        self.trycompletions('ALTER ROLE foo WITH ', choices=['ACCESS', 'HASHED', 'LOGIN', 'OPTIONS', 'PASSWORD', 'SUPERUSER'])
+        self.trycompletions('ALTER ROLE foo WITH ACCESS TO ', choices=['ALL', 'DATACENTERS'])
+
+    def test_complete_in_drop_role(self):
+        self.trycompletions('DROP ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])
+
+
+    def test_complete_in_list(self):
+        self.trycompletions('LIST ', choices=['ALL', 'AUTHORIZE', 'DESCRIBE', 'EXECUTE', 'ROLES', 'USERS', 'ALTER', 'CREATE', 'DROP', 'MODIFY', 'SELECT'])
+
+
+    # Non-CQL Shell Commands
+
+    def test_complete_in_capture(self):
+        self.trycompletions('CAPTURE ', choices=['OFF', ';', '<enter>'], other_choices_ok=True)
+
+    def test_complete_in_paging(self):
+        self.trycompletions('PAGING ', choices=['ON', 'OFF', ';', '<enter>', '<wholenumber>' ] )
+        self.trycompletions('PAGING 50 ', choices=[';', '<enter>' ] )
+
+    def test_complete_in_serial(self):
+        self.trycompletions('SERIAL CONSISTENCY ', choices=[';', '<enter>', 'LOCAL_SERIAL', 'SERIAL'])
+
+    def test_complete_in_show(self):
+        self.trycompletions('SHOW ', choices=['HOST', 'REPLICAS', 'SESSION', 'VERSION'])
+        self.trycompletions('SHOW SESSION ', choices=['<uuid>'])
+        self.trycompletions('SHOW REPLICAS ', choices=['-', '<wholenumber>'])
+
+    def test_complete_in_tracing(self):
+        self.trycompletions('TRACING ', choices=[';', '<enter>', 'OFF', 'ON'])


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org