Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the tab-completion support of SERVICE_LEVEL statements #48

Merged
merged 2 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ jobs:
pip install -r ./pylib/requirements.txt
pytest ./cqlshlib/test

integration_test_scylla_enterprise:
name: Integration Tests (Scylla Enterprise)
if: "!contains(github.event.pull_request.labels.*.name, 'disable-integration-test')"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Start Scylla
run: |
export DOCKER_ID=$(docker run -d scylladb/scylla-enterprise:latest --cluster-name test )
export CQL_TEST_HOST=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' ${DOCKER_ID})
while ! nc -z ${CQL_TEST_HOST} 9042; do
sleep 0.1 # wait for 1/10 of the second before check again
done

echo "CQL_TEST_HOST=${CQL_TEST_HOST}" >> $GITHUB_ENV

- name: pytest
run: |
pip install -r ./pylib/requirements.txt
pytest ./cqlshlib/test

integration_test_scylla_cloud_bundle:
name: Integration Tests (Scylla Cloud Bundle)
if: "!contains(github.event.pull_request.labels.*.name, 'disable-integration-test')"
Expand Down
45 changes: 43 additions & 2 deletions pylib/cqlshlib/cql3handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ def __str__(self):


SYSTEM_KEYSPACES = ('system', 'system_schema', 'system_traces', 'system_auth', 'system_distributed', 'system_views',
'system_virtual_schema', 'system_distributed_everywhere')
NONALTERBALE_KEYSPACES = ('system', 'system_schema', 'system_views', 'system_virtual_schema', 'system_distributed_everywhere')
'system_virtual_schema', 'system_distributed_everywhere', 'system_replicated_keys')
NONALTERBALE_KEYSPACES = ('system', 'system_schema', 'system_views', 'system_virtual_schema', 'system_distributed_everywhere', 'system_replicated_keys')


class Cql3ParsingRuleSet(CqlParsingRuleSet):
Expand Down Expand Up @@ -296,6 +296,12 @@ def dequote_value(cqlword):
| <alterRoleStatement>
| <dropRoleStatement>
| <listRolesStatement>
| <createSlaStatement>
| <alterSlaStatement>
| <dropSlaStatement>
| <listSlaStatement>
| <attachSlaStatement>
| <detachRSlaStatement>
;

<authorizationStatement> ::= <grantStatement>
Expand Down Expand Up @@ -1501,6 +1507,41 @@ def alter_type_field_completer(ctxt, cass):
;
'''

syntax_rules += r'''
<slaName> ::= <identifier>
| <quotedName>
| <unreservedKeyword>
;

<createSlaStatement> ::= "CREATE" "SERVICE_LEVEL" ( "IF" "NOT" "EXISTS" )? <slaName>
( "WITH" <slaProperty> ("AND" <slaProperty>)*)?
;

<alterSlaStatement> ::= "ALTER" "SERVICE_LEVEL" ("IF" "EXISTS")? <slaName>
( "WITH" <slaProperty> ("AND" <slaProperty>)*)?
;

<slaProperty> ::= "WORKLOAD_TYPE" "=" <stringLiteral>
| "TIMEOUT" "=" <wholenumber>
| "SHARES" "=" <wholenumber>
;

<dropSlaStatement> ::= "DROP" "SERVICE_LEVEL" ("IF" "EXISTS")? <slaName>
;

<listSlaStatement> ::= ("LIST" "SERVICE_LEVEL" <slaName> )
| ("LIST" "ATTACHED" "SERVICE_LEVEL" "OF" <rolename> )
| ("LIST" "ALL" "SERVICE_LEVELS" )
| ("LIST" "ALL" "ATTACHED" "SERVICE_LEVELS" )
;

<attachSlaStatement> ::= "ATTACH" "SERVICE_LEVEL" <slaName> "TO" <rolename>
;

<detachRSlaStatement> ::= "DETACH" "SERVICE_LEVEL" <slaName> "FROM" <rolename>
;
'''

syntax_rules += r'''
<grantStatement> ::= "GRANT" <permissionExpr> "ON" <resource> "TO" <rolename>
;
Expand Down
72 changes: 63 additions & 9 deletions pylib/cqlshlib/test/test_cqlsh_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@
import locale
import os
import re

from cassandra import InvalidRequest
from packaging.version import Version

from .basecase import BaseTestCase
from .cassconnect import create_db, remove_db, testrun_cqlsh
from .cassconnect import create_db, remove_db, testrun_cqlsh, get_cassandra_connection
from .run_cqlsh import TimeoutError
from cqlshlib.cql3handling import CqlRuleSet

Expand All @@ -48,6 +52,14 @@ def setUpClass(cls):
output = c.cmd_and_response("SELECT * FROM system_schema.scylla_tables LIMIT 1;")
cls.is_scylla = '1 rows' in output

with get_cassandra_connection().connect() as session:
try:
result = session.execute("SELECT version FROM system.versions WHERE key = 'local' LIMIT 1")
cls.scylla_version = Version(result.one().version.rsplit('.', 2)[0])
cls.is_scylla_enterprise = cls.scylla_version > Version('2018.1')
except InvalidRequest:
cls.is_scylla_enterprise = False

@classmethod
def tearDownClass(cls):
remove_db()
Expand All @@ -63,6 +75,18 @@ def setUp(self):
def tearDown(self):
self.cqlsh_runner.__exit__(None, None, None)

def _system_keyspaces(self):
tables = []

if self.is_scylla:
tables += ['system_distributed_everywhere.']
if self.is_scylla_enterprise:
tables += ['system_replicated_keys.']
else:
tables += ['system_views.', 'system_virtual_schema.']

return tables

def _get_completions(self, inputstring, split_completed_lines=True):
"""
Get results of tab completion in cqlsh. Returns a bare string if a
Expand Down Expand Up @@ -167,7 +191,7 @@ def test_complete_on_empty_string(self):
'COPY', 'CREATE', 'DEBUG', 'DELETE', 'DESC', 'DESCRIBE',
'DROP', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING', 'REVOKE',
'SELECT', 'SHOW', 'SOURCE', 'TRACING', 'EXPAND', 'SERIAL', 'TRUNCATE',
'UPDATE', 'USE', 'exit', 'quit', 'CLEAR', 'CLS'))
'UPDATE', 'USE', 'exit', 'quit', 'CLEAR', 'CLS', 'ATTACH', 'DETACH'))

def test_complete_command_words(self):
self.trycompletions('alt', '\b\b\bALTER ')
Expand Down Expand Up @@ -254,7 +278,7 @@ def test_complete_in_insert(self):
'EXPAND', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING',
'REVOKE', 'SELECT', 'SHOW', 'SOURCE', 'SERIAL', 'TRACING',
'TRUNCATE', 'UPDATE', 'USE', 'exit', 'quit',
'CLEAR', 'CLS'])
'CLEAR', 'CLS', 'ATTACH', 'DETACH'])

self.trycompletions(
("INSERT INTO twenty_rows_composite_table (a, b, c) "
Expand Down Expand Up @@ -555,7 +579,7 @@ def test_complete_in_drop(self):
self.trycompletions('DROP ',
choices=['AGGREGATE', 'COLUMNFAMILY', 'FUNCTION',
'INDEX', 'KEYSPACE', 'ROLE', 'TABLE',
'TRIGGER', 'TYPE', 'USER', 'MATERIALIZED'])
'TRIGGER', 'TYPE', 'USER', 'MATERIALIZED', 'SERVICE_LEVEL'])

def test_complete_in_drop_keyspace(self):
self.trycompletions('DROP K', immediate='EYSPACE ')
Expand Down Expand Up @@ -927,9 +951,8 @@ def test_complete_in_alter_table(self):
'utf8_with_special_chars',
'system_traces.', 'songs',
'system_schema.', 'system_distributed.',
self.cqlsh.keyspace + '.'] +
(['system_distributed_everywhere.'] if self.is_scylla else
['system_views.', 'system_virtual_schema.']))
self.cqlsh.keyspace + '.'] + self._system_keyspaces()
)
self.trycompletions('ALTER TABLE IF EXISTS new_table ADD ', choices=['<new_column_name>', 'IF'])
self.trycompletions('ALTER TABLE IF EXISTS new_table ADD IF NOT EXISTS ', choices=['<new_column_name>'])
self.trycompletions('ALTER TABLE new_table ADD IF NOT EXISTS ', choices=['<new_column_name>'])
Expand All @@ -943,8 +966,7 @@ def test_complete_in_alter_type(self):
'tags', 'system_traces.', 'system_distributed.',
'phone_number', 'band_info_type', 'address', 'system.', 'system_schema.',
'system_auth.', self.cqlsh.keyspace + '.'
] + (['system_distributed_everywhere.'] if self.is_scylla else
['system_views.', 'system_virtual_schema.']))
] + self._system_keyspaces())
self.trycompletions('ALTER TYPE IF EXISTS new_type ADD ', choices=['<new_field_name>', 'IF'])
self.trycompletions('ALTER TYPE IF EXISTS new_type ADD IF NOT EXISTS ', choices=['<new_field_name>'])
self.trycompletions('ALTER TYPE IF EXISTS new_type RENAME ', choices=['IF', '<quotedName>', '<identifier>'])
Expand All @@ -954,3 +976,35 @@ def test_complete_in_alter_user(self):

def test_complete_in_alter_role(self):
self.trycompletions('ALTER ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])

def test_complete_in_alter_service_level(self):
self.trycompletions('ALTER SERVICE_LEVEL ', choices=['<identifier>', 'IF', '<quotedName>'])

def test_complete_in_create_service_level(self):
self.trycompletions('CREATE SERVICE_LEVEL "sla" WITH ',
choices=['WORKLOAD_TYPE', 'TIMEOUT', 'SHARES'])

def test_complete_in_attach_service_level(self):
self.trycompletions('ATTACH ',
immediate="SERVICE_LEVEL ")
self.trycompletions('ATTACH SERVICE_LEVEL "sla" ',
immediate="TO ")
self.trycompletions('ATTACH SERVICE_LEVEL "sla" TO ',
choices=['<identifier>', '<quotedName>'])

def test_complete_in_list_service_levels(self):

self.trycompletions("LIST ALL ",
choices={'ON', 'NORECURSIVE', 'OF', 'ATTACHED', ';', 'SERVICE_LEVELS', 'PERMISSIONS'})

self.trycompletions("LIST ALL SERVICE_LEVELS ",
choices={';'})

self.trycompletions("LIST ALL ATTACHED ",
immediate='SERVICE_LEVELS ;')

self.trycompletions("LIST ATTACHED ",
immediate='SERVICE_LEVEL OF ')

self.trycompletions("LIST SERVICE_LEVEL ",
choices={'<quotedName>', '<identifier>'})
20 changes: 15 additions & 5 deletions pylib/cqlshlib/test/test_cqlsh_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ def get_cassandra_metadata(cls):
cls.is_scylla = len(output.all()) == 1
except InvalidRequest:
cls.is_scylla = False
try:
result = curs.execute("SELECT version FROM system.versions WHERE key = 'local' LIMIT 1")
cls.scylla_version = Version(result.one().version.rsplit('.', 2)[0])
cls.is_scylla_enterprise = cls.scylla_version > Version('2018.1')
except InvalidRequest:
cls.is_scylla_enterprise = False

@property
def default_compaction_strategy(self):
return 'IncrementalCompactionStrategy' if self.is_scylla_enterprise else 'SizeTieredCompactionStrategy'

def setUp(self):
env = os.environ.copy()
Expand Down Expand Up @@ -684,7 +694,7 @@ def test_describe_columnfamily_output(self):
AND read_repair = 'BLOCKING'
AND speculative_retry = '99p';""" % quote_name(get_keyspace()))

scylla_table_desc = dedent("""
scylla_table_desc = dedent(f"""
CREATE TABLE %s.has_all_types (
num int PRIMARY KEY,
asciicol ascii,
Expand All @@ -703,10 +713,10 @@ def test_describe_columnfamily_output(self):
varcharcol text,
varintcol varint
) WITH bloom_filter_fp_chance = 0.01
AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'}
AND caching = {{'keys': 'ALL', 'rows_per_partition': 'ALL'}}
AND comment = ''
AND compaction = {'class': 'SizeTieredCompactionStrategy'}
AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND compaction = {{'class': '{self.default_compaction_strategy}'}}
AND compression = {{'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}}
AND crc_check_chance = 1.0
AND dclocal_read_repair_chance = 0.0
AND default_time_to_live = 0
Expand Down Expand Up @@ -1001,7 +1011,7 @@ def test_scylla_tags(self):
) WITH bloom_filter_fp_chance = 0.01
AND caching = {{'keys': 'ALL', 'rows_per_partition': 'ALL'}}
AND comment = ''
AND compaction = {{'class': 'SizeTieredCompactionStrategy'}}
AND compaction = {{'class': '{self.default_compaction_strategy}'}}
AND compression = {{'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}}
AND crc_check_chance = 1.0
AND dclocal_read_repair_chance = 0.0
Expand Down
Loading