Merge branch 'cassandra-5.0' into trunk

* cassandra-5.0:
  Add option to disable cqlsh history
This commit is contained in:
mck 2026-03-17 23:08:39 +01:00
commit ee273af1d6
No known key found for this signature in database
GPG Key ID: E91335D77E3E87CB
4 changed files with 53 additions and 8 deletions

View File

@ -322,6 +322,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

@ -104,6 +104,15 @@ port = 9042
; 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

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

@ -25,6 +25,7 @@ import subprocess
import sys
import time
import traceback
from typing import Any
import warnings
import webbrowser
from contextlib import contextmanager
@ -120,6 +121,16 @@ 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)
OLD_HISTORY = os.path.expanduser(os.path.join('~', '.cqlsh_history'))
if os.path.exists(OLD_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
@ -272,7 +283,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
@ -354,6 +366,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.')
@ -1896,8 +1916,8 @@ class Shell(cmd.Cmd):
delims += '.'
readline.set_completer_delims(delims)
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:
@ -2037,7 +2057,7 @@ def read_options(cmdlineargs, parser, config_file, cql_dir, environment=os.envir
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,
@ -2208,6 +2228,8 @@ def main(cmdline, pkgpath):
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.
@ -2233,6 +2255,18 @@ def main(cmdline, pkgpath):
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)
@ -2332,7 +2366,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:
@ -2353,7 +2388,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)