Skip to content
Open
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
12 changes: 12 additions & 0 deletions programs/client/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,18 @@ void Client::connect()
boost::replace_all(prompt, "{" + key + "}", value);

prompt = appendSmileyIfNeeded(prompt);

/// Sync the current database from the server after the handshake, but only when all
/// three conditions hold:
/// - interactive: in batch mode the prompt is never displayed, so the extra
/// round-trip to the server would add latency with no visible benefit;
/// - no explicit --database flag: if the user passed --database, default_database
/// is already set to the correct value and must not be overridden by whatever
/// the server considers the current database;
/// - {database} appears in the prompt string: querying the server is pointless
/// when the macro is absent and the result would never be shown.
if (is_interactive && default_database.empty() && prompt.find("{database}") != String::npos)
syncDefaultDatabase();
}

// Prints changed settings to stderr. Useful for debugging fuzzing failures.
Expand Down
1 change: 1 addition & 0 deletions programs/client/clickhouse-client.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
{port}
{user}
{display_name}
{database}

You can also use colored prompt, like in [1].
[1]: https://misc.flogisoft.com/bash/tip_colors_and_formatting
Expand Down
20 changes: 19 additions & 1 deletion src/Client/ClientBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2916,7 +2916,9 @@ bool ClientBase::processQueryText(const String & text)

String ClientBase::getPrompt() const
{
return prompt;
String result = prompt;
boost::replace_all(result, "{database}", default_database.empty() ? "default" : default_database);
return result;
}


Expand Down Expand Up @@ -3130,6 +3132,22 @@ std::string ClientBase::executeQueryForSingleString(const std::string & query)
}
}

void ClientBase::syncDefaultDatabase()
{
try
{
const auto db = executeQueryForSingleString("SELECT currentDatabase()");
if (!db.empty())
default_database = db;
else
LOG_WARNING(getLogger("ClientBase"), "syncDefaultDatabase: server returned empty result for SELECT currentDatabase()");
}
catch (...)
{
LOG_WARNING(getLogger("ClientBase"), "syncDefaultDatabase: failed to query current database from server: {}", getCurrentExceptionMessage(false));
}
}

bool ClientBase::checkAIProviderAcknowledgment()
{
// If API key came from environment and user hasn't acknowledged yet, ask for confirmation
Expand Down
5 changes: 5 additions & 0 deletions src/Client/ClientBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ class ClientBase

String appendSmileyIfNeeded(const String & prompt);

/// Query the server for the current database and update default_database.
/// Called after the connection handshake so that the prompt reflects the
/// server-side default when no --database flag was passed.
void syncDefaultDatabase();

/// Should be one of the first, to be destroyed the last,
/// since other members can use them.
/// This holder may not be initialized in case if we run the client in the embedded mode (SSH).
Expand Down
48 changes: 48 additions & 0 deletions tests/queries/0_stateless/04056_client_prompt_database.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/expect -f

# Test that {database} macro in the clickhouse-client prompt reflects the current database
# and updates dynamically after USE statements.
# https://github.com/ClickHouse/ClickHouse/issues/49118

set basedir [file dirname $argv0]
set basename [file tail $argv0]
if {[info exists env(CLICKHOUSE_TMP)]} {
set CLICKHOUSE_TMP $env(CLICKHOUSE_TMP)
} else {
set CLICKHOUSE_TMP "."
}
exp_internal -f $CLICKHOUSE_TMP/$basename.debuglog 0
set history_file $CLICKHOUSE_TMP/$basename.history

log_user 0
set timeout 60
match_max 100000

expect_after {
# Do not ignore eof from expect
-i $any_spawn_id eof { exp_continue }
# A default timeout action is to do nothing, change it to fail
-i $any_spawn_id timeout { exit 1 }
}

# Test 1: {database} in prompt shows the database set via --database flag.
# CLICKHOUSE_CLIENT_EXPECT_OPT includes --database=test (CLICKHOUSE_DATABASE).
spawn bash -c "source $basedir/../shell_config.sh ; \$CLICKHOUSE_CLIENT_BINARY \$CLICKHOUSE_CLIENT_EXPECT_OPT --prompt '{database}' --history_file=$history_file"
expect -re "\ntest :\\) $"

# Test 2: {database} updates in the prompt after a USE statement.
send -- "USE system;\r"
expect "\nOk."
expect -re "\nsystem :\\) $"

send -- "\4"
expect eof

# Test 3: {database} shows the server's current database when --database flag is not passed.
# syncDefaultDatabase() fires SELECT currentDatabase() after the handshake and writes
# the result into default_database, so the prompt reflects the server-side default.
spawn bash -c "source $basedir/../shell_config.sh ; opts=\$(echo \"\$CLICKHOUSE_CLIENT_EXPECT_OPT\" | sed 's/--database=\[^ \]*//g') ; \$CLICKHOUSE_CLIENT_BINARY \$opts --prompt '{database}' --history_file=$history_file"
expect -re "\ndefault :\\) $"

send -- "\4"
expect eof
Empty file.
40 changes: 40 additions & 0 deletions tests/queries/0_stateless/04057_local_prompt_database.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/expect -f

# Test that {database} macro in the clickhouse-local prompt reflects the current database
# and updates dynamically after USE statements.
# https://github.com/ClickHouse/ClickHouse/issues/49118

set basedir [file dirname $argv0]
set basename [file tail $argv0]
if {[info exists env(CLICKHOUSE_TMP)]} {
set CLICKHOUSE_TMP $env(CLICKHOUSE_TMP)
} else {
set CLICKHOUSE_TMP "."
}
exp_internal -f $CLICKHOUSE_TMP/$basename.debuglog 0
set history_file $CLICKHOUSE_TMP/$basename.history

log_user 0
set timeout 60
match_max 100000

expect_after {
# Do not ignore eof from expect
-i $any_spawn_id eof { exp_continue }
# A default timeout action is to do nothing, change it to fail
-i $any_spawn_id timeout { exit 1 }
}

# Test 1: {database} in prompt shows "default" when --database flag is not passed.
# default_database is empty in that case; getPrompt() falls back to the literal
# string "default" so no server round-trip is needed.
spawn bash -c "source $basedir/../shell_config.sh ; \$CLICKHOUSE_LOCAL --disable_suggestion --enable-progress-table-toggle=0 --prompt '{database}' --history_file=$history_file"
expect -re "\ndefault :\\) $"

# Test 2: {database} updates in the prompt after a USE statement.
send -- "USE system;\r"
expect "\nOk."
expect -re "\nsystem :\\) $"

send -- "\4"
expect eof
Loading