Merge branch 'cassandra-4.1' into cassandra-5.0

* cassandra-4.1:
  Add option to disable cqlsh history
This commit is contained in:
mck 2026-03-17 23:06:56 +01:00
commit be0b73ecba
No known key found for this signature in database
GPG Key ID: E91335D77E3E87CB
4 changed files with 47 additions and 9 deletions

View File

@ -16,6 +16,7 @@ Merged from 4.1:
* Disk usage guardrail cannot be disabled when failure threshold is reached (CASSANDRA-21057)
* ReadCommandController should close fast to avoid deadlock when building secondary index (CASSANDRA-19564)
Merged from 4.0:
* Add option to disable cqlsh history (CASSANDRA-21180)
* Rate limit password changes (CASSANDRA-21202)
* Node does not send multiple inflight echos (CASSANDRA-18866)
* Obsolete expired SSTables before compaction starts (CASSANDRA-19776)

View File

@ -111,6 +111,16 @@ port = 9042
[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

View File

@ -57,8 +57,8 @@ Example config values and documentation can be found in the
[[cql_history]]
== cql history
All CQL commands you execute are written to a history file. By default, CQL history will be written to `~/.cassandra/cql_history`. You can change this default by setting the environment variable `CQL_HISTORY` like `~/some/other/path/to/cqlsh_history` where `cqlsh_history` is a file. All parent directories to history file will be created if they do not exist. If you do not want to persist history, you can do so by setting CQL_HISTORY to /dev/null.
This feature is supported from Cassandra 4.1.
All CQL commands you execute are written to a history file. By default, CQL history will be written to `~/.cassandra/cql_history`.
You can change this default by setting the environment variable `CQL_HISTORY` like `~/some/other/path/to/cqlsh_history` where `cqlsh_history` is a file. All parent directories to history file will be created if they do not exist. If you do not want to persist history, you can do so by setting CQL_HISTORY to `/dev/null`. This can also be controlled via the --disable-history command line option or from the cqlshrc file. Disabling history is supported since Cassandra 4.0.
== Command Line Options

View File

@ -150,6 +150,8 @@ parser.add_argument("--request-timeout", default=DEFAULT_REQUEST_TIMEOUT_SECONDS
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.
@ -191,7 +193,6 @@ 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'))
@ -206,6 +207,22 @@ else:
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)
# END history/config definition
CQL_ERRORS = (
cassandra.AlreadyExists, cassandra.AuthenticationFailed, cassandra.CoordinationFailure,
cassandra.InvalidRequest, cassandra.Timeout, cassandra.Unauthorized, cassandra.OperationTimedOut,
@ -362,7 +379,8 @@ class Shell(cmd.Cmd):
protocol_version=None,
connect_timeout=DEFAULT_CONNECT_TIMEOUT_SECONDS,
is_subshell=False,
auth_provider=None):
auth_provider=None,
disable_history=False):
cmd.Cmd.__init__(self, completekey=completekey)
self.hostname = hostname
self.port = port
@ -441,6 +459,14 @@ class Shell(cmd.Cmd):
self.check_build_versions()
if tty:
# Inform users about history logging if not disabled
if not disable_history and readline is not None:
print()
print("ATTENTION: All commands will be saved to history file: %s" % HISTORY)
print("This may include sensitive information such as passwords.")
print("To disable history, use --disable-history or set 'disabled = true' in the [history] section of cqlshrc.")
print("See https://cassandra.apache.org/doc/latest/tools/cqlsh.html for more information.")
print()
self.reset_prompt()
self.report_connection()
print('Use HELP for help.')
@ -1946,8 +1972,8 @@ class Shell(cmd.Cmd):
# configure length of history shown
self.max_history_length_shown = 50
def save_history(self):
if readline is not None:
def save_history(self, history_disabled=False):
if readline is not None and not history_disabled:
try:
readline.write_history_file(HISTORY)
except IOError:
@ -2089,7 +2115,7 @@ def read_options(cmdlineargs, environment=os.environ):
argvalues.request_timeout = option_with_default(configs.getint, 'connection', 'request_timeout', 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,
@ -2345,7 +2371,8 @@ def main(cmdline, pkgpath):
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:
@ -2366,7 +2393,7 @@ def main(cmdline, pkgpath):
shell.init_history()
shell.cmdloop()
shell.save_history()
shell.save_history(options.disable_history)
if shell.batch_mode and shell.statement_error:
sys.exit(2)