From 61333964f42e27ec78fec5b4ec25d9313dfc4eee Mon Sep 17 00:00:00 2001 From: Brad Schoening Date: Fri, 19 May 2023 13:52:33 -0400 Subject: [PATCH] Add HISTORY command for CQLSH patch by Brad Schoening; reviewed by Stefan Miklosovic and Brandon Williams for CASSANDRA-15046 --- CHANGES.txt | 1 + .../cassandra/pages/managing/tools/cqlsh.adoc | 14 ++++ pylib/cqlshlib/cqlshhandling.py | 10 ++- pylib/cqlshlib/cqlshmain.py | 65 ++++++++++++------- pylib/cqlshlib/test/test_cqlsh_completion.py | 4 +- 5 files changed, 69 insertions(+), 25 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a5a6dffab7..12741fad68 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0 + * Add HISTORY command for CQLSH (CASSANDRA-15046) * Fix sstable formats configuration (CASSANDRA-18441) * Add guardrail to bound timestamps (CASSANDRA-18352) * Add keyspace_name column to system_views.clients (CASSANDRA-18525) diff --git a/doc/modules/cassandra/pages/managing/tools/cqlsh.adoc b/doc/modules/cassandra/pages/managing/tools/cqlsh.adoc index afafa1d875..b5209e9b4c 100644 --- a/doc/modules/cassandra/pages/managing/tools/cqlsh.adoc +++ b/doc/modules/cassandra/pages/managing/tools/cqlsh.adoc @@ -266,6 +266,20 @@ Gives information about cqlsh commands. To see available topics, enter `HELP `. Also see the `--browser` argument for controlling what browser is used to display help. +=== `HISTORY` + +Prints to the screen the last `n` cqlsh commands executed on the server. +The number of lines defaults to 50 if not specified. `n` is set for +the current CQL session so if you set it e.g. to `10`, from that point +there will be at most 10 last commands returned to you. + +`Usage`: + +[source,none] +---- +HISTORY +---- + === `TRACING` Enables or disables tracing for queries. When tracing is enabled, once a diff --git a/pylib/cqlshlib/cqlshhandling.py b/pylib/cqlshlib/cqlshhandling.py index 57ec28b763..e6a121fd80 100644 --- a/pylib/cqlshlib/cqlshhandling.py +++ b/pylib/cqlshlib/cqlshhandling.py @@ -37,7 +37,8 @@ my_commands_ending_with_newline = ( 'exit', 'quit', 'clear', - 'cls' + 'cls', + 'history' ) cqlsh_syntax_completers = [] @@ -73,6 +74,7 @@ cqlsh_special_cmd_command_syntax_rules = r''' | | | + | ; ''' @@ -207,6 +209,11 @@ cqlsh_clear_cmd_syntax_rules = r''' ; ''' +cqlsh_history_cmd_syntax_rules = r''' + ::= "history" (n=)? + ; +''' + cqlsh_question_mark = r''' ::= "?" ; ''' @@ -232,6 +239,7 @@ cqlsh_extra_syntax_rules = cqlsh_cmd_syntax_rules + \ cqlsh_login_cmd_syntax_rules + \ cqlsh_exit_cmd_syntax_rules + \ cqlsh_clear_cmd_syntax_rules + \ + cqlsh_history_cmd_syntax_rules + \ cqlsh_question_mark diff --git a/pylib/cqlshlib/cqlshmain.py b/pylib/cqlshlib/cqlshmain.py index c550ed84c6..7cffd68e0e 100755 --- a/pylib/cqlshlib/cqlshmain.py +++ b/pylib/cqlshlib/cqlshmain.py @@ -1836,6 +1836,26 @@ class Shell(cmd.Cmd): else: self.printerr("*** No help on %s" % (t,)) + def do_history(self, parsed): + """ + HISTORY [cqlsh only] + + Displays the most recent commands executed in cqlsh + + HISTORY () + + If n is specified, the history display length is set to n for this session + """ + + history_length = readline.get_current_history_length() + + n = parsed.get_binding('n') + if (n is not None): + self.max_history_length_shown = int(n) + + for index in range(max(1, history_length - self.max_history_length_shown), history_length): + print(readline.get_history_item(index)) + def do_unicode(self, parsed): """ Textual input/output @@ -1913,6 +1933,27 @@ class Shell(cmd.Cmd): self.cov.save() self.cov = None + def init_history(self): + if readline is not None: + try: + readline.read_history_file(HISTORY) + except IOError: + pass + delims = readline.get_completer_delims() + delims.replace("'", "") + delims += '.' + readline.set_completer_delims(delims) + + # configure length of history shown + self.max_history_length_shown = 50 + + def save_history(self): + if readline is not None: + try: + readline.write_history_file(HISTORY) + except IOError: + pass + class SwitchCommand(object): command = None @@ -2193,26 +2234,6 @@ def setup_docspath(path): CASSANDRA_CQL_HTML = CASSANDRA_CQL_HTML_FALLBACK -def init_history(): - if readline is not None: - try: - readline.read_history_file(HISTORY) - except IOError: - pass - delims = readline.get_completer_delims() - delims.replace("'", "") - delims += '.' - readline.set_completer_delims(delims) - - -def save_history(): - if readline is not None: - try: - readline.write_history_file(HISTORY) - except IOError: - pass - - def insert_driver_hooks(): class DateOverFlowWarning(RuntimeWarning): @@ -2245,7 +2266,6 @@ def main(cmdline, pkgpath): setup_docspath(pkgpath) setup_cqlruleset(options.cqlmodule) setup_cqldocs(options.cqlmodule) - init_history() csv.field_size_limit(options.field_size_limit) if options.file is None: @@ -2343,8 +2363,9 @@ def main(cmdline, pkgpath): signal.signal(signal.SIGHUP, handle_sighup) + shell.init_history() shell.cmdloop() - save_history() + shell.save_history() if shell.batch_mode and shell.statement_error: sys.exit(2) diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py b/pylib/cqlshlib/test/test_cqlsh_completion.py index 61a95b9389..b0f0b87a1c 100644 --- a/pylib/cqlshlib/test/test_cqlsh_completion.py +++ b/pylib/cqlshlib/test/test_cqlsh_completion.py @@ -164,7 +164,7 @@ class TestCqlshCompletion(CqlshCompletionCase): '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', 'history')) def test_complete_command_words(self): self.trycompletions('alt', '\b\b\bALTER ') @@ -250,7 +250,7 @@ class TestCqlshCompletion(CqlshCompletionCase): 'CREATE', 'DEBUG', 'DELETE', 'DESC', 'DESCRIBE', 'DROP', 'EXPAND', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING', 'REVOKE', 'SELECT', 'SHOW', 'SOURCE', 'SERIAL', 'TRACING', - 'TRUNCATE', 'UPDATE', 'USE', 'exit', 'quit', + 'TRUNCATE', 'UPDATE', 'USE', 'exit', 'history', 'quit', 'CLEAR', 'CLS']) self.trycompletions(