Add cqlsh autocompletion for the identity mapping feature

Patch by Shalni Sundram; reviewed by Francisco Guerrero, Bernardo Botella for CASSANDRA-20021
This commit is contained in:
Shalni Sundram 2025-10-02 10:39:30 -07:00 committed by Francisco Guerrero
parent c12cc0b026
commit accdaaebdd
3 changed files with 47 additions and 4 deletions

View File

@ -1,4 +1,5 @@
5.1
* Add cqlsh autocompletion for the identity mapping feature (CASSANDRA-20021)
* Add DDL Guardrail enabling administrators to disallow creation/modification of keyspaces with durable_writes = false (CASSANDRA-20913)
* Optimize Counter, Meter and Histogram metrics using thread local counters (CASSANDRA-20250)
* Update snakeyaml to 2.4 (CASSANDRA-20928)

View File

@ -286,6 +286,7 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
| <createFunctionStatement>
| <createAggregateStatement>
| <createTriggerStatement>
| <addIdentityStatement>
| <dropKeyspaceStatement>
| <dropColumnFamilyStatement>
| <dropIndexStatement>
@ -294,6 +295,7 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
| <dropFunctionStatement>
| <dropAggregateStatement>
| <dropTriggerStatement>
| <dropIdentityStatement>
| <alterTableStatement>
| <alterKeyspaceStatement>
| <alterUserTypeStatement>
@ -1749,6 +1751,7 @@ syntax_rules += r'''
"ON" cf=<columnFamilyName>
;
'''
explain_completion('createTriggerStatement', 'class', '\'fully qualified class name\'')
@ -1765,6 +1768,13 @@ def drop_trigger_completer(ctxt, cass):
return list(map(maybe_escape_name, names))
syntax_rules += r'''
<addIdentityStatement> ::= "ADD" "IDENTITY" ("IF" "NOT" "EXISTS")? <stringLiteral> "TO" "ROLE" <rolename>
;
<dropIdentityStatement> ::= "DROP" "IDENTITY" ("IF" "EXISTS")? <stringLiteral>
;
'''
# END SYNTAX/COMPLETION RULE DEFINITIONS
CqlRuleSet.append_rules(syntax_rules)

View File

@ -166,7 +166,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
cqlver = '3.1.6'
def test_complete_on_empty_string(self):
self.trycompletions('', choices=('?', 'ALTER', 'BEGIN', 'CAPTURE', 'CONSISTENCY',
self.trycompletions('', choices=('?', 'ADD', 'ALTER', 'BEGIN', 'CAPTURE', 'CONSISTENCY',
'COPY', 'CREATE', 'DEBUG', 'DELETE', 'DESC', 'DESCRIBE',
'DROP', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING', 'REVOKE',
'SELECT', 'SHOW', 'SOURCE', 'TRACING', 'ELAPSED', 'EXPAND', 'SERIAL', 'TRUNCATE',
@ -288,7 +288,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
self.trycompletions(
("INSERT INTO twenty_rows_composite_table (a, b, c) "
"VALUES ( 'eggs', 'sausage', 'spam');"),
choices=['?', 'ALTER', 'BEGIN', 'CAPTURE', 'CONSISTENCY', 'COPY',
choices=['?', 'ADD', 'ALTER', 'BEGIN', 'CAPTURE', 'CONSISTENCY', 'COPY',
'CREATE', 'DEBUG', 'DELETE', 'DESC', 'DESCRIBE', 'DROP',
'ELAPSED', 'EXPAND', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING',
'REVOKE', 'SELECT', 'SHOW', 'SOURCE', 'SERIAL', 'TRACING',
@ -613,7 +613,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
def test_complete_in_drop(self):
self.trycompletions('DR', immediate='OP ')
self.trycompletions('DROP ',
choices=['AGGREGATE', 'COLUMNFAMILY', 'FUNCTION',
choices=['AGGREGATE', 'COLUMNFAMILY', 'FUNCTION', 'IDENTITY',
'INDEX', 'KEYSPACE', 'ROLE', 'TABLE',
'TRIGGER', 'TYPE', 'USER', 'MATERIALIZED'])
@ -1053,7 +1053,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
self.trycompletions('CREATE INDEX example ', immediate='ON ')
def test_complete_in_drop_index(self):
self.trycompletions('DROP I', immediate='NDEX ')
self.trycompletions('DROP IN', immediate='DEX ')
def test_complete_in_alter_keyspace(self):
self.trycompletions('ALTER KEY', 'SPACE ')
@ -1212,6 +1212,38 @@ class TestCqlshCompletion(CqlshCompletionCase):
def test_complete_in_drop_role(self):
self.trycompletions('DROP ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])
# IDENTITY checks
def test_complete_in_add_identity(self):
self.trycompletions('A', choices=['ADD', 'ALTER'])
self.trycompletions('AD', immediate='D IDENTITY ')
self.trycompletions('ADD ID', immediate='ENTITY ')
self.trycompletions('ADD IDENTITY IF ', immediate='NOT EXISTS ')
self.trycompletions('ADD IDENTITY IF NOT ', immediate='EXISTS ')
self.trycompletions('ADD IDENTITY ',
choices=['<pgStringLiteral>', '<quotedStringLiteral>', 'IF'])
self.trycompletions("ADD IDENTITY 'testIdentifier' TO R", immediate='OLE ')
self.trycompletions("ADD IDENTITY 'testIdentifier' ", immediate='TO ROLE ')
self.trycompletions("ADD IDENTITY 'testIdentifier' TO ROLE ",
choices=['<identifier>', '<quotedName>'], other_choices_ok=True)
self.trycompletions("ADD IDENTITY 'testIdentifier' TO ROLE cassandra ",
choices=[';'])
self.trycompletions("ADD IDENTITY IF NOT EXISTS 'stestIdentifier' TO ROLE cassandra ",
choices=[';'])
def test_complete_in_drop_identity(self):
self.trycompletions('D', choices=['DESCRIBE', 'DEBUG', 'DESC', 'DROP', 'DELETE'])
self.trycompletions('DR', immediate='OP ')
self.trycompletions('DRO', immediate='P ')
self.trycompletions('DROP ID', immediate='ENTITY ')
self.trycompletions('DROP', choices=' ')
self.trycompletions('DROP IDENTITY ',
choices=['<pgStringLiteral>', '<quotedStringLiteral>', 'IF'])
self.trycompletions('DROP IDENTITY IF ', immediate='EXISTS ')
self.trycompletions('DROP IDENTITY IF EXISTS ',
choices=['<pgStringLiteral>', '<quotedStringLiteral>'])
self.trycompletions("DROP IDENTITY 'testIdentifier' ", choices=[';'])
self.trycompletions("DROP IDENTITY IF EXISTS 'testIdentifier' ", choices=[';'])
def test_complete_in_list(self):
self.trycompletions('LIST ',
choices=['ALL', 'AUTHORIZE', 'DESCRIBE', 'EXECUTE', 'ROLES', 'USERS', 'ALTER',