2020-01-01 19:22:57 +00:00
|
|
|
#include "Suggest.h"
|
|
|
|
|
2021-01-25 20:05:41 +00:00
|
|
|
#include <Core/Settings.h>
|
2020-01-01 19:22:57 +00:00
|
|
|
#include <Columns/ColumnString.h>
|
|
|
|
#include <Common/typeid_cast.h>
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
2020-02-25 18:10:48 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
|
|
|
extern const int UNKNOWN_PACKET_FROM_SERVER;
|
2020-02-29 19:53:22 +00:00
|
|
|
extern const int DEADLOCK_AVOIDED;
|
2020-02-25 18:10:48 +00:00
|
|
|
}
|
2020-01-01 19:22:57 +00:00
|
|
|
|
|
|
|
void Suggest::load(const ConnectionParameters & connection_parameters, size_t suggestion_limit)
|
|
|
|
{
|
|
|
|
loading_thread = std::thread([connection_parameters, suggestion_limit, this]
|
|
|
|
{
|
2020-02-29 19:53:22 +00:00
|
|
|
for (size_t retry = 0; retry < 10; ++retry)
|
2020-01-01 19:22:57 +00:00
|
|
|
{
|
2020-02-29 19:53:22 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
Connection connection(
|
|
|
|
connection_parameters.host,
|
|
|
|
connection_parameters.port,
|
|
|
|
connection_parameters.default_database,
|
|
|
|
connection_parameters.user,
|
|
|
|
connection_parameters.password,
|
2020-09-14 21:55:43 +00:00
|
|
|
"" /* cluster */,
|
|
|
|
"" /* cluster_secret */,
|
2020-02-29 19:53:22 +00:00
|
|
|
"client",
|
|
|
|
connection_parameters.compression,
|
|
|
|
connection_parameters.security);
|
2020-01-01 19:22:57 +00:00
|
|
|
|
2020-02-29 19:53:22 +00:00
|
|
|
loadImpl(connection, connection_parameters.timeouts, suggestion_limit);
|
|
|
|
}
|
|
|
|
catch (const Exception & e)
|
|
|
|
{
|
|
|
|
/// Retry when the server said "Client should retry".
|
|
|
|
if (e.code() == ErrorCodes::DEADLOCK_AVOIDED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
std::cerr << "Cannot load data for command line suggestions: " << getCurrentExceptionMessage(false, true) << "\n";
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
std::cerr << "Cannot load data for command line suggestions: " << getCurrentExceptionMessage(false, true) << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2020-01-01 19:22:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Note that keyword suggestions are available even if we cannot load data from server.
|
|
|
|
|
2020-03-21 05:17:12 +00:00
|
|
|
std::sort(words.begin(), words.end());
|
|
|
|
words_no_case = words;
|
|
|
|
std::sort(words_no_case.begin(), words_no_case.end(), [](const std::string & str1, const std::string & str2)
|
2020-03-18 05:27:56 +00:00
|
|
|
{
|
|
|
|
return std::lexicographical_compare(begin(str1), end(str1), begin(str2), end(str2), [](const char char1, const char char2)
|
2020-02-25 08:30:11 +00:00
|
|
|
{
|
2020-03-18 05:27:56 +00:00
|
|
|
return std::tolower(char1) < std::tolower(char2);
|
2020-02-25 08:30:11 +00:00
|
|
|
});
|
2020-03-18 05:27:56 +00:00
|
|
|
});
|
2020-02-25 08:30:11 +00:00
|
|
|
|
2020-01-01 19:22:57 +00:00
|
|
|
ready = true;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Suggest::Suggest()
|
|
|
|
{
|
|
|
|
/// Keywords may be not up to date with ClickHouse parser.
|
2020-04-08 23:53:41 +00:00
|
|
|
words = {"CREATE", "DATABASE", "IF", "NOT", "EXISTS", "TEMPORARY", "TABLE", "ON", "CLUSTER", "DEFAULT",
|
|
|
|
"MATERIALIZED", "ALIAS", "ENGINE", "AS", "VIEW", "POPULATE", "SETTINGS", "ATTACH", "DETACH", "DROP",
|
|
|
|
"RENAME", "TO", "ALTER", "ADD", "MODIFY", "CLEAR", "COLUMN", "AFTER", "COPY", "PROJECT",
|
|
|
|
"PRIMARY", "KEY", "CHECK", "PARTITION", "PART", "FREEZE", "FETCH", "FROM", "SHOW", "INTO",
|
|
|
|
"OUTFILE", "FORMAT", "TABLES", "DATABASES", "LIKE", "PROCESSLIST", "CASE", "WHEN", "THEN", "ELSE",
|
|
|
|
"END", "DESCRIBE", "DESC", "USE", "SET", "OPTIMIZE", "FINAL", "DEDUPLICATE", "INSERT", "VALUES",
|
|
|
|
"SELECT", "DISTINCT", "SAMPLE", "ARRAY", "JOIN", "GLOBAL", "LOCAL", "ANY", "ALL", "INNER",
|
|
|
|
"LEFT", "RIGHT", "FULL", "OUTER", "CROSS", "USING", "PREWHERE", "WHERE", "GROUP", "BY",
|
|
|
|
"WITH", "TOTALS", "HAVING", "ORDER", "COLLATE", "LIMIT", "UNION", "AND", "OR", "ASC",
|
|
|
|
"IN", "KILL", "QUERY", "SYNC", "ASYNC", "TEST", "BETWEEN", "TRUNCATE", "USER", "ROLE",
|
|
|
|
"PROFILE", "QUOTA", "POLICY", "ROW", "GRANT", "REVOKE", "OPTION", "ADMIN", "EXCEPT", "REPLACE",
|
2020-09-26 04:03:55 +00:00
|
|
|
"IDENTIFIED", "HOST", "NAME", "READONLY", "WRITABLE", "PERMISSIVE", "FOR", "RESTRICTIVE", "RANDOMIZED",
|
2020-07-05 15:57:59 +00:00
|
|
|
"INTERVAL", "LIMITS", "ONLY", "TRACKING", "IP", "REGEXP", "ILIKE"};
|
2020-01-01 19:22:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Suggest::loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit)
|
|
|
|
{
|
2021-01-27 19:44:22 +00:00
|
|
|
/// NOTE: Once you will update the completion list,
|
|
|
|
/// do not forget to update 01676_clickhouse_client_autocomplete.sh
|
|
|
|
|
2020-11-10 18:22:26 +00:00
|
|
|
std::stringstream query; // STYLE_CHECK_ALLOW_STD_STRING_STREAM
|
2020-01-01 19:22:57 +00:00
|
|
|
query << "SELECT DISTINCT arrayJoin(extractAll(name, '[\\\\w_]{2,}')) AS res FROM ("
|
|
|
|
"SELECT name FROM system.functions"
|
|
|
|
" UNION ALL "
|
|
|
|
"SELECT name FROM system.table_engines"
|
|
|
|
" UNION ALL "
|
|
|
|
"SELECT name FROM system.formats"
|
|
|
|
" UNION ALL "
|
|
|
|
"SELECT name FROM system.table_functions"
|
|
|
|
" UNION ALL "
|
|
|
|
"SELECT name FROM system.data_type_families"
|
|
|
|
" UNION ALL "
|
2020-01-28 19:25:43 +00:00
|
|
|
"SELECT name FROM system.merge_tree_settings"
|
|
|
|
" UNION ALL "
|
2020-01-01 19:22:57 +00:00
|
|
|
"SELECT name FROM system.settings"
|
|
|
|
" UNION ALL "
|
2020-01-18 14:00:59 +00:00
|
|
|
"SELECT cluster FROM system.clusters"
|
|
|
|
" UNION ALL "
|
2021-01-25 20:15:47 +00:00
|
|
|
"SELECT macro FROM system.macros"
|
|
|
|
" UNION ALL "
|
2021-01-25 20:15:59 +00:00
|
|
|
"SELECT policy_name FROM system.storage_policies"
|
|
|
|
" UNION ALL "
|
2020-01-01 19:22:57 +00:00
|
|
|
"SELECT concat(func.name, comb.name) FROM system.functions AS func CROSS JOIN system.aggregate_function_combinators AS comb WHERE is_aggregate";
|
|
|
|
|
|
|
|
/// The user may disable loading of databases, tables, columns by setting suggestion_limit to zero.
|
|
|
|
if (suggestion_limit > 0)
|
|
|
|
{
|
|
|
|
String limit_str = toString(suggestion_limit);
|
|
|
|
query <<
|
|
|
|
" UNION ALL "
|
|
|
|
"SELECT name FROM system.databases LIMIT " << limit_str
|
|
|
|
<< " UNION ALL "
|
|
|
|
"SELECT DISTINCT name FROM system.tables LIMIT " << limit_str
|
|
|
|
<< " UNION ALL "
|
2020-06-04 22:56:11 +00:00
|
|
|
"SELECT DISTINCT name FROM system.dictionaries LIMIT " << limit_str
|
|
|
|
<< " UNION ALL "
|
2020-01-01 19:22:57 +00:00
|
|
|
"SELECT DISTINCT name FROM system.columns LIMIT " << limit_str;
|
|
|
|
}
|
|
|
|
|
|
|
|
query << ") WHERE notEmpty(res)";
|
|
|
|
|
2021-04-13 12:39:13 +00:00
|
|
|
fetch(connection, timeouts, query.str());
|
2020-01-01 19:22:57 +00:00
|
|
|
}
|
|
|
|
|
2021-04-13 12:39:13 +00:00
|
|
|
void Suggest::fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query)
|
2020-01-01 19:22:57 +00:00
|
|
|
{
|
2021-04-13 12:39:13 +00:00
|
|
|
connection.sendQuery(timeouts, query, "" /* query_id */, QueryProcessingStage::Complete);
|
2020-01-01 19:22:57 +00:00
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
Packet packet = connection.receivePacket();
|
|
|
|
switch (packet.type)
|
|
|
|
{
|
|
|
|
case Protocol::Server::Data:
|
|
|
|
fillWordsFromBlock(packet.block);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case Protocol::Server::Progress:
|
|
|
|
continue;
|
|
|
|
case Protocol::Server::ProfileInfo:
|
|
|
|
continue;
|
|
|
|
case Protocol::Server::Totals:
|
|
|
|
continue;
|
|
|
|
case Protocol::Server::Extremes:
|
|
|
|
continue;
|
|
|
|
case Protocol::Server::Log:
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case Protocol::Server::Exception:
|
|
|
|
packet.exception->rethrow();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case Protocol::Server::EndOfStream:
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
2020-08-17 18:37:24 +00:00
|
|
|
throw Exception(ErrorCodes::UNKNOWN_PACKET_FROM_SERVER, "Unknown packet {} from server {}",
|
|
|
|
packet.type, connection.getDescription());
|
2020-01-01 19:22:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Suggest::fillWordsFromBlock(const Block & block)
|
|
|
|
{
|
|
|
|
if (!block)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (block.columns() != 1)
|
|
|
|
throw Exception("Wrong number of columns received for query to read words for suggestion", ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
const ColumnString & column = typeid_cast<const ColumnString &>(*block.getByPosition(0).column);
|
|
|
|
|
|
|
|
size_t rows = block.rows();
|
|
|
|
for (size_t i = 0; i < rows; ++i)
|
|
|
|
words.emplace_back(column.getDataAt(i).toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|