This is an automated email from the ASF dual-hosted git repository. mck pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit ee273af1d64f9f9c54d0cb4c9d38598b83bc3bbd Merge: cd9d6daa9e be0b73ecba Author: mck <[email protected]> AuthorDate: Tue Mar 17 23:08:39 2026 +0100 Merge branch 'cassandra-5.0' into trunk * cassandra-5.0: Add option to disable cqlsh history CHANGES.txt | 1 + conf/cqlshrc.sample | 9 +++++ .../cassandra/pages/managing/tools/cqlsh.adoc | 4 +- pylib/cqlshlib/cqlshmain.py | 47 +++++++++++++++++++--- 4 files changed, 53 insertions(+), 8 deletions(-) diff --cc conf/cqlshrc.sample index c396edfed9,79d719460e..3c957a79a2 --- a/conf/cqlshrc.sample +++ b/conf/cqlshrc.sample @@@ -104,6 -110,17 +104,15 @@@ port = 904 ; max_trace_wait = 10.0 - + [history] + ;; Controls whether command history is saved to ~/.cassandra/cqlsh_history + ;; When disabled, existing history will still be loaded but new commands + ;; will not be saved. This can be useful for security purposes to prevent + ;; sensitive commands (e.g., those containing passwords) from being persisted. + ;; This can also be controlled via the --disable-history command line option. + ; disabled = false + + - ;[ssl] ; certfile = ~/keys/cassandra.cert diff --cc pylib/cqlshlib/cqlshmain.py index 8b26e33078,5c97fa3f74..c13256dd40 --- a/pylib/cqlshlib/cqlshmain.py +++ b/pylib/cqlshlib/cqlshmain.py @@@ -23,8 -24,7 +23,9 @@@ import o import re import subprocess import sys +import time import traceback ++from typing import Any import warnings import webbrowser from contextlib import contextmanager @@@ -120,8 -193,35 +121,18 @@@ try except OSError: print('\nWarning: Cannot create directory at `%s`. Command history will not be saved. Please check what was the environment property CQL_HISTORY set to.\n' % HISTORY_DIR) -# END history config - -DEFAULT_CQLSHRC = os.path.expanduser(os.path.join('~', '.cassandra', 'cqlshrc')) - -if cfarguments.cqlshrc is not None: - CONFIG_FILE = os.path.expanduser(cfarguments.cqlshrc) - if not os.path.exists(CONFIG_FILE): - print('\nWarning: Specified cqlshrc location `%s` does not exist. Using `%s` instead.\n' % (CONFIG_FILE, DEFAULT_CQLSHRC)) - CONFIG_FILE = DEFAULT_CQLSHRC -else: - CONFIG_FILE = DEFAULT_CQLSHRC - -CQL_DIR = os.path.dirname(CONFIG_FILE) - -OLD_CONFIG_FILE = os.path.expanduser(os.path.join('~', '.cqlshrc')) -if os.path.exists(OLD_CONFIG_FILE): - if os.path.exists(CONFIG_FILE): - print('\nWarning: cqlshrc config files were found at both the old location ({0})' - + ' and the new location ({1}), the old config file will not be migrated to the new' - + ' location, and the new location will be used for now. You should manually' - + ' consolidate the config files at the new location and remove the old file.' - .format(OLD_CONFIG_FILE, CONFIG_FILE)) - else: - os.rename(OLD_CONFIG_FILE, CONFIG_FILE) + OLD_HISTORY = os.path.expanduser(os.path.join('~', '.cqlsh_history')) + if os.path.exists(OLD_HISTORY): - os.rename(OLD_HISTORY, HISTORY) ++ if os.path.exists(HISTORY): ++ print('\nWarning: .cqlsh_history files were found at both the old location ({0})' ++ + ' and the new location ({1}), the old file will not be migrated to the new' ++ + ' location, and the new location will be used for now. You should manually' ++ + ' consolidate these files at the new location, and remove the old file.' ++ .format(OLD_HISTORY, HISTORY)) ++ else: ++ os.rename(OLD_HISTORY, HISTORY) -# END history/config definition +# END history config CQL_ERRORS = ( cassandra.AlreadyExists, cassandra.AuthenticationFailed, cassandra.CoordinationFailure, @@@ -1896,8 -1969,11 +1916,8 @@@ class Shell(cmd.Cmd) delims += '.' readline.set_completer_delims(delims) - def save_history(self): - if readline is not None: - # configure length of history shown - self.max_history_length_shown = 50 - + def save_history(self, history_disabled=False): + if readline is not None and not history_disabled: try: readline.write_history_file(HISTORY) except IOError: @@@ -2033,11 -2111,11 +2053,11 @@@ def read_options(cmdlineargs, parser, c argvalues.tty = option_with_default(configs.getboolean, 'ui', 'tty', sys.stdin.isatty()) argvalues.protocol_version = option_with_default(configs.getint, 'protocol', 'version', None) argvalues.cqlversion = option_with_default(configs.get, 'cql', 'version', None) - argvalues.connect_timeout = option_with_default(configs.getint, 'connection', 'timeout', DEFAULT_CONNECT_TIMEOUT_SECONDS) - argvalues.request_timeout = option_with_default(configs.getint, 'connection', 'request_timeout', DEFAULT_REQUEST_TIMEOUT_SECONDS) + argvalues.connect_timeout = option_with_default(configs.getint, 'connection', 'timeout', Shell.DEFAULT_CONNECT_TIMEOUT_SECONDS) + argvalues.request_timeout = option_with_default(configs.getint, 'connection', 'request_timeout', Shell.DEFAULT_REQUEST_TIMEOUT_SECONDS) argvalues.execute = None argvalues.insecure_password_without_warning = False - + argvalues.disable_history = option_with_default(configs.getboolean, 'history', 'disabled', False) options, arguments = parser.parse_known_args(cmdlineargs, argvalues) # Credentials from cqlshrc will be expanded, @@@ -2162,80 -2271,12 +2182,94 @@@ def insert_driver_hooks() def main(cmdline, pkgpath): insert_driver_hooks() - (options, hostname, port) = read_options(cmdline) - setup_docspath(pkgpath) - setup_cqlruleset(options.cqlmodule) - setup_cqldocs(options.cqlmodule) - csv.field_size_limit(options.field_size_limit) + epilog = f"Connects to {Shell.DEFAULT_HOST}:{Shell.DEFAULT_PORT} by default. These \ + defaults can be changed by setting $CQLSH_HOST and/or $CQLSH_PORT. When a \ + host (and optional port number) are given on the command line, they take \ + precedence over any defaults." + + description = "CQL Shell for Apache Cassandra" + + parser = argparse.ArgumentParser(description=description, epilog=epilog, + usage="Usage: %(prog)s [options] [host [port]]", + prog='cqlsh') + parser.add_argument('-v', '--version', action='version', version='cqlsh ' + version) + parser.add_argument("-C", "--color", action='store_true', dest='color', + help='Always use color output') + parser.add_argument("--no-color", action='store_false', dest='color', help='Never use color output') + parser.add_argument("--browser", dest='browser', help="""The browser to use to display CQL help, where BROWSER can be: + - one of the supported browsers in https://docs.python.org/3/library/webbrowser.html. + - browser path followed by %%s, example: /usr/bin/google-chrome-stable %%s""") + + parser.add_argument('--ssl', action='store_true', help='Use SSL', default=False) + parser.add_argument("-u", "--username", help="Authenticate as user.") + parser.add_argument("-p", "--password", help="Authenticate using password.") + parser.add_argument('-k', '--keyspace', help='Authenticate to the given keyspace.') + parser.add_argument("-f", "--file", help="Execute commands from FILE, then exit") + parser.add_argument('--debug', action='store_true', + help='Show additional debugging information') + parser.add_argument('--coverage', action='store_true', + help='Collect coverage data') + parser.add_argument("--encoding", help=f"Specify a non-default encoding for output." + + " (Default: {UTF8)") + parser.add_argument("--cqlshrc", help="Specify an alternative cqlshrc file location.") + parser.add_argument("--credentials", help="Specify an alternative credentials file location.") + parser.add_argument('--cqlversion', default=None, + help='Specify a particular CQL version, ' + 'by default the highest version supported by the server will be used.' + ' Examples: "3.0.3", "3.1.0"') + parser.add_argument("--protocol-version", type=int, default=None, + help='Specify a specific protcol version otherwise the client will default and downgrade as necessary') + + parser.add_argument("-e", "--execute", help='Execute the statement and quit.') + parser.add_argument("--connect-timeout", default=Shell.DEFAULT_CONNECT_TIMEOUT_SECONDS, dest='connect_timeout', + help='Specify the connection timeout in seconds (default: %(default)s seconds).') + parser.add_argument("--request-timeout", default=Shell.DEFAULT_REQUEST_TIMEOUT_SECONDS, dest='request_timeout', + help='Specify the default request timeout in seconds (default: %(default)s seconds).') + parser.add_argument("-t", "--tty", action='store_true', dest='tty', + help='Force tty mode (command prompt).') ++ parser.add_argument('--disable-history', default=False, action='store_true', ++ help='Disable saving of history (existing history will still be loaded)') + + # This is a hidden option to suppress the warning when the -p/--password command line option is used. + # Power users may use this option if they know no other people has access to the system where cqlsh is run or don't care about security. + # Use of this option in scripting is discouraged. Please use a (temporary) credentials file where possible. + # The Cassandra distributed tests (dtests) also use this option in some tests when a well-known password is supplied via the command line. + parser.add_argument("--insecure-password-without-warning", action='store_true', + dest='insecure_password_without_warning', + help=argparse.SUPPRESS) + + # use cfarguments for config file + + cfarguments, args = parser.parse_known_args() + + default_cqlshrc = os.path.expanduser(os.path.join('~', '.cassandra', 'cqlshrc')) + + if cfarguments.cqlshrc is not None: + config_file = os.path.expanduser(cfarguments.cqlshrc) + if not os.path.exists(config_file): + print('\nWarning: Specified cqlshrc location `%s` does not exist. Using `%s` instead.\n' % + (config_file, default_cqlshrc)) + config_file = default_cqlshrc + else: + config_file = default_cqlshrc + + cql_dir = os.path.dirname(config_file) ++ ++ old_config_file = os.path.expanduser(os.path.join('~', '.cqlshrc')) ++ if os.path.exists(old_config_file): ++ if os.path.exists(config_file): ++ print('\nWarning: cqlshrc config files were found at both the old location ({0})' ++ + ' and the new location ({1}), the old config file will not be migrated to the new' ++ + ' location, and the new location will be used for now. You should manually' ++ + ' consolidate the config files at the new location and remove the old file.' ++ .format(old_config_file, config_file)) ++ else: ++ os.rename(old_config_file, config_file) ++ + (options, hostname, port) = read_options(cmdline, parser, config_file, cql_dir) + + docspath = get_docspath(pkgpath) if options.file is None: stdin = None @@@ -2329,10 -2368,11 +2363,11 @@@ connect_timeout=options.connect_timeout, encoding=options.encoding, auth_provider=authproviderhandling.load_auth_provider( - config_file=CONFIG_FILE, + config_file=config_file, cred_file=options.credentials, username=options.username, - password=options.password)) + password=options.password), + disable_history=options.disable_history) except KeyboardInterrupt: sys.exit('Connection aborted.') except CQL_ERRORS as e: --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
