mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
Merge branch 'master' of https://github.com/yandex/ClickHouse into yandex_open_code_competition
This commit is contained in:
commit
9d2a0c69fc
@ -1,5 +1,11 @@
|
||||
option(USE_INTERNAL_PROTOBUF_LIBRARY "Set to FALSE to use system protobuf instead of bundled" ${NOT_UNBUNDLED})
|
||||
|
||||
if(OS_FREEBSD AND SANITIZE STREQUAL "address")
|
||||
# ../contrib/protobuf/src/google/protobuf/arena_impl.h:45:10: fatal error: 'sanitizer/asan_interface.h' file not found
|
||||
set(MISSING_INTERNAL_PROTOBUF_LIBRARY 1)
|
||||
set(USE_INTERNAL_PROTOBUF_LIBRARY 0)
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/protobuf/cmake/CMakeLists.txt")
|
||||
if(USE_INTERNAL_PROTOBUF_LIBRARY)
|
||||
message(WARNING "submodule contrib/protobuf is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
|
@ -282,6 +282,7 @@ target_link_libraries (dbms PRIVATE ${Poco_Foundation_LIBRARY})
|
||||
|
||||
if (USE_ICU)
|
||||
target_link_libraries (dbms PRIVATE ${ICU_LIBRARIES})
|
||||
target_include_directories (dbms SYSTEM PRIVATE ${ICU_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
if (USE_CAPNP)
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include <Common/Exception.h>
|
||||
#include <IO/ConnectionTimeouts.h>
|
||||
|
||||
#include <common/SetTerminalEcho.h>
|
||||
#include <common/setTerminalEcho.h>
|
||||
#include <ext/scope_guard.h>
|
||||
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
@ -56,10 +56,10 @@ struct ConnectionParameters
|
||||
throw Exception("Specified both --password and --ask-password. Remove one of them", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
std::cout << "Password for user " << user << ": ";
|
||||
SetTerminalEcho(false);
|
||||
setTerminalEcho(false);
|
||||
|
||||
SCOPE_EXIT({
|
||||
SetTerminalEcho(true);
|
||||
setTerminalEcho(true);
|
||||
});
|
||||
std::getline(std::cin, password);
|
||||
std::cout << std::endl;
|
||||
|
@ -128,6 +128,32 @@ UInt64 PerformanceTest::calculateMaxExecTime() const
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void PerformanceTest::prepare() const
|
||||
{
|
||||
for (const auto & query : test_info.create_queries)
|
||||
{
|
||||
LOG_INFO(log, "Executing create query '" << query << "'");
|
||||
connection.sendQuery(query);
|
||||
}
|
||||
|
||||
for (const auto & query : test_info.fill_queries)
|
||||
{
|
||||
LOG_INFO(log, "Executing fill query '" << query << "'");
|
||||
connection.sendQuery(query);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PerformanceTest::finish() const
|
||||
{
|
||||
for (const auto & query : test_info.drop_queries)
|
||||
{
|
||||
LOG_INFO(log, "Executing drop query '" << query << "'");
|
||||
connection.sendQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TestStats> PerformanceTest::execute()
|
||||
{
|
||||
std::vector<TestStats> statistics_by_run;
|
||||
|
@ -25,12 +25,15 @@ public:
|
||||
Context & context_);
|
||||
|
||||
bool checkPreconditions() const;
|
||||
void prepare() const;
|
||||
std::vector<TestStats> execute();
|
||||
void finish() const;
|
||||
|
||||
const PerformanceTestInfo & getTestInfo() const
|
||||
{
|
||||
return test_info;
|
||||
}
|
||||
|
||||
bool checkSIGINT() const
|
||||
{
|
||||
return got_SIGINT;
|
||||
|
@ -90,6 +90,7 @@ PerformanceTestInfo::PerformanceTestInfo(
|
||||
getExecutionType(config);
|
||||
getStopConditions(config);
|
||||
getMetrics(config);
|
||||
extractAuxiliaryQueries(config);
|
||||
}
|
||||
|
||||
void PerformanceTestInfo::applySettings(XMLConfigurationPtr config)
|
||||
@ -269,4 +270,16 @@ void PerformanceTestInfo::getMetrics(XMLConfigurationPtr config)
|
||||
checkMetricsInput(metrics, exec_type);
|
||||
}
|
||||
|
||||
void PerformanceTestInfo::extractAuxiliaryQueries(XMLConfigurationPtr config)
|
||||
{
|
||||
if (config->has("create_query"))
|
||||
create_queries = getMultipleValuesFromConfig(*config, "", "create_query");
|
||||
|
||||
if (config->has("fill_query"))
|
||||
fill_queries = getMultipleValuesFromConfig(*config, "", "fill_query");
|
||||
|
||||
if (config->has("drop_query"))
|
||||
drop_queries = getMultipleValuesFromConfig(*config, "", "drop_query");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,6 +43,10 @@ public:
|
||||
std::string profiles_file;
|
||||
std::vector<TestStopConditions> stop_conditions_by_run;
|
||||
|
||||
Strings create_queries;
|
||||
Strings fill_queries;
|
||||
Strings drop_queries;
|
||||
|
||||
private:
|
||||
void applySettings(XMLConfigurationPtr config);
|
||||
void extractQueries(XMLConfigurationPtr config);
|
||||
@ -50,6 +54,7 @@ private:
|
||||
void getExecutionType(XMLConfigurationPtr config);
|
||||
void getStopConditions(XMLConfigurationPtr config);
|
||||
void getMetrics(XMLConfigurationPtr config);
|
||||
void extractAuxiliaryQueries(XMLConfigurationPtr config);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -202,11 +202,18 @@ private:
|
||||
|
||||
current.checkPreconditions();
|
||||
LOG_INFO(log, "Preconditions for test '" << info.test_name << "' are fullfilled");
|
||||
|
||||
LOG_INFO(log, "Preparing for run, have " << info.create_queries.size()
|
||||
<< " create queries and " << info.fill_queries.size() << " fill queries");
|
||||
current.prepare();
|
||||
LOG_INFO(log, "Prepared");
|
||||
LOG_INFO(log, "Running test '" << info.test_name << "'");
|
||||
auto result = current.execute();
|
||||
LOG_INFO(log, "Test '" << info.test_name << "' finished");
|
||||
|
||||
LOG_INFO(log, "Running post run queries");
|
||||
current.finish();
|
||||
LOG_INFO(log, "Postqueries finished");
|
||||
|
||||
if (lite_output)
|
||||
return {report_builder->buildCompactReport(info, result), current.checkSIGINT()};
|
||||
else
|
||||
|
@ -120,17 +120,7 @@ void CreatingSetsBlockInputStream::createOne(SubqueryForSet & subquery)
|
||||
|
||||
if (!done_with_join)
|
||||
{
|
||||
for (const auto & name_with_alias : subquery.joined_block_aliases)
|
||||
{
|
||||
if (block.has(name_with_alias.first))
|
||||
{
|
||||
auto pos = block.getPositionByName(name_with_alias.first);
|
||||
auto column = block.getByPosition(pos);
|
||||
block.erase(pos);
|
||||
column.name = name_with_alias.second;
|
||||
block.insert(std::move(column));
|
||||
}
|
||||
}
|
||||
subquery.renameColumns(block);
|
||||
|
||||
if (subquery.joined_block_actions)
|
||||
subquery.joined_block_actions->execute(block);
|
||||
|
@ -36,6 +36,7 @@ endif ()
|
||||
|
||||
if (USE_ICU)
|
||||
target_link_libraries (clickhouse_functions PRIVATE ${ICU_LIBRARIES})
|
||||
target_include_directories(clickhouse_functions SYSTEM PRIVATE ${ICU_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
if (USE_VECTORCLASS)
|
||||
|
@ -1,111 +0,0 @@
|
||||
#include <IO/InterserverWriteBuffer.h>
|
||||
#include <IO/WriteBufferFromOStream.h>
|
||||
|
||||
#include <Poco/Version.h>
|
||||
#include <Poco/URI.h>
|
||||
#include <Poco/Net/HTTPRequest.h>
|
||||
#include <Poco/Net/HTTPResponse.h>
|
||||
|
||||
#include <common/logger_useful.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int CANNOT_WRITE_TO_OSTREAM;
|
||||
extern const int RECEIVED_ERROR_FROM_REMOTE_IO_SERVER;
|
||||
}
|
||||
|
||||
InterserverWriteBuffer::InterserverWriteBuffer(const std::string & host_, int port_,
|
||||
const std::string & endpoint_,
|
||||
const std::string & path_,
|
||||
bool compress_,
|
||||
size_t buffer_size_,
|
||||
const Poco::Timespan & connection_timeout,
|
||||
const Poco::Timespan & send_timeout,
|
||||
const Poco::Timespan & receive_timeout)
|
||||
: WriteBuffer(nullptr, 0), host(host_), port(port_), path(path_)
|
||||
{
|
||||
std::string encoded_path;
|
||||
Poco::URI::encode(path, "&#", encoded_path);
|
||||
|
||||
std::string encoded_endpoint;
|
||||
Poco::URI::encode(endpoint_, "&#", encoded_endpoint);
|
||||
|
||||
std::string compress_str = compress_ ? "true" : "false";
|
||||
std::string encoded_compress;
|
||||
Poco::URI::encode(compress_str, "&#", encoded_compress);
|
||||
|
||||
std::stringstream uri;
|
||||
uri << "http://" << host << ":" << port
|
||||
<< "/?endpoint=" << encoded_endpoint
|
||||
<< "&compress=" << encoded_compress
|
||||
<< "&path=" << encoded_path;
|
||||
|
||||
std::string uri_str = Poco::URI(uri.str()).getPathAndQuery();
|
||||
|
||||
session.setHost(host);
|
||||
session.setPort(port);
|
||||
session.setKeepAlive(true);
|
||||
|
||||
/// set the timeout
|
||||
#if POCO_CLICKHOUSE_PATCH || POCO_VERSION >= 0x02000000
|
||||
session.setTimeout(connection_timeout, send_timeout, receive_timeout);
|
||||
#else
|
||||
session.setTimeout(connection_timeout);
|
||||
static_cast <void> (send_timeout);
|
||||
static_cast <void> (receive_timeout);
|
||||
#endif
|
||||
|
||||
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, uri_str, Poco::Net::HTTPRequest::HTTP_1_1);
|
||||
|
||||
request.setChunkedTransferEncoding(true);
|
||||
|
||||
ostr = &session.sendRequest(request);
|
||||
impl = std::make_unique<WriteBufferFromOStream>(*ostr, buffer_size_);
|
||||
set(impl->buffer().begin(), impl->buffer().size());
|
||||
}
|
||||
|
||||
InterserverWriteBuffer::~InterserverWriteBuffer()
|
||||
{
|
||||
try
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
void InterserverWriteBuffer::nextImpl()
|
||||
{
|
||||
if (!offset() || finalized)
|
||||
return;
|
||||
|
||||
/// For correct work with AsynchronousWriteBuffer, which replaces buffers.
|
||||
impl->set(buffer().begin(), buffer().size());
|
||||
|
||||
impl->position() = pos;
|
||||
|
||||
impl->next();
|
||||
}
|
||||
|
||||
void InterserverWriteBuffer::finalize()
|
||||
{
|
||||
if (finalized)
|
||||
return;
|
||||
|
||||
next();
|
||||
|
||||
finalized = true;
|
||||
}
|
||||
|
||||
void InterserverWriteBuffer::cancel()
|
||||
{
|
||||
finalized = true;
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <IO/WriteBuffer.h>
|
||||
#include <IO/HashingWriteBuffer.h>
|
||||
|
||||
#include <Poco/Net/HTTPClientSession.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr auto DEFAULT_REMOTE_WRITE_BUFFER_CONNECTION_TIMEOUT = 1;
|
||||
constexpr auto DEFAULT_REMOTE_WRITE_BUFFER_RECEIVE_TIMEOUT = 1800;
|
||||
constexpr auto DEFAULT_REMOTE_WRITE_BUFFER_SEND_TIMEOUT = 1800;
|
||||
|
||||
}
|
||||
|
||||
/** Allows you to write a file to a remote server.
|
||||
*/
|
||||
class InterserverWriteBuffer final : public WriteBuffer
|
||||
{
|
||||
public:
|
||||
InterserverWriteBuffer(const std::string & host_, int port_,
|
||||
const std::string & endpoint_,
|
||||
const std::string & path_,
|
||||
bool compress_ = false,
|
||||
size_t buffer_size_ = DBMS_DEFAULT_BUFFER_SIZE,
|
||||
const Poco::Timespan & connection_timeout = Poco::Timespan(DEFAULT_REMOTE_WRITE_BUFFER_CONNECTION_TIMEOUT, 0),
|
||||
const Poco::Timespan & send_timeout = Poco::Timespan(DEFAULT_REMOTE_WRITE_BUFFER_SEND_TIMEOUT, 0),
|
||||
const Poco::Timespan & receive_timeout = Poco::Timespan(DEFAULT_REMOTE_WRITE_BUFFER_RECEIVE_TIMEOUT, 0));
|
||||
|
||||
~InterserverWriteBuffer() override;
|
||||
void finalize();
|
||||
void cancel();
|
||||
|
||||
private:
|
||||
void nextImpl() override;
|
||||
|
||||
private:
|
||||
std::string host;
|
||||
int port;
|
||||
std::string path;
|
||||
|
||||
Poco::Net::HTTPClientSession session;
|
||||
std::ostream * ostr; /// this is owned by session
|
||||
std::unique_ptr<WriteBuffer> impl;
|
||||
|
||||
/// Sent all the data and renamed the file
|
||||
bool finalized = false;
|
||||
};
|
||||
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Interpreters/PreparedSets.h>
|
||||
#include <Interpreters/ExpressionActions.h>
|
||||
#include <Interpreters/SubqueryForSet.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -11,32 +12,6 @@ namespace DB
|
||||
class Context;
|
||||
class ASTFunction;
|
||||
|
||||
class Join;
|
||||
using JoinPtr = std::shared_ptr<Join>;
|
||||
|
||||
/// Information on what to do when executing a subquery in the [GLOBAL] IN/JOIN section.
|
||||
struct SubqueryForSet
|
||||
{
|
||||
/// The source is obtained using the InterpreterSelectQuery subquery.
|
||||
BlockInputStreamPtr source;
|
||||
|
||||
/// If set, build it from result.
|
||||
SetPtr set;
|
||||
JoinPtr join;
|
||||
/// Apply this actions to joined block.
|
||||
ExpressionActionsPtr joined_block_actions;
|
||||
/// Rename column from joined block from this list.
|
||||
NamesWithAliases joined_block_aliases;
|
||||
|
||||
/// If set, put the result into the table.
|
||||
/// This is a temporary table for transferring to remote servers for distributed query processing.
|
||||
StoragePtr table;
|
||||
};
|
||||
|
||||
/// ID of subquery -> what to do with it.
|
||||
using SubqueriesForSets = std::unordered_map<String, SubqueryForSet>;
|
||||
|
||||
|
||||
/// The case of an explicit enumeration of values.
|
||||
SetPtr makeExplicitSet(
|
||||
const ASTFunction * node, const Block & sample_block, bool create_ordered_set,
|
||||
|
129
dbms/src/Interpreters/CrossToInnerJoinVisitor.cpp
Normal file
129
dbms/src/Interpreters/CrossToInnerJoinVisitor.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Interpreters/CrossToInnerJoinVisitor.h>
|
||||
#include <Interpreters/DatabaseAndTableWithAlias.h>
|
||||
#include <Interpreters/IdentifierSemantic.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ParserTablesInSelectQuery.h>
|
||||
#include <Parsers/ExpressionListParsers.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
/// TODO: array join aliases?
|
||||
struct CheckColumnsVisitorData
|
||||
{
|
||||
using TypeToVisit = ASTIdentifier;
|
||||
|
||||
const std::vector<DatabaseAndTableWithAlias> & tables;
|
||||
size_t visited;
|
||||
size_t found;
|
||||
|
||||
size_t allMatch() const { return visited == found; }
|
||||
|
||||
void visit(ASTIdentifier & node, ASTPtr &)
|
||||
{
|
||||
++visited;
|
||||
for (const auto & t : tables)
|
||||
if (IdentifierSemantic::canReferColumnToTable(node, t))
|
||||
++found;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static bool extractTableName(const ASTTableExpression & expr, std::vector<DatabaseAndTableWithAlias> & names)
|
||||
{
|
||||
/// Subselects are not supported.
|
||||
if (!expr.database_and_table_name)
|
||||
return false;
|
||||
|
||||
names.emplace_back(DatabaseAndTableWithAlias(expr));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static ASTPtr getCrossJoin(ASTSelectQuery & select, std::vector<DatabaseAndTableWithAlias> & table_names)
|
||||
{
|
||||
if (!select.tables)
|
||||
return {};
|
||||
|
||||
auto tables = typeid_cast<const ASTTablesInSelectQuery *>(select.tables.get());
|
||||
if (!tables)
|
||||
return {};
|
||||
|
||||
size_t num_tables = tables->children.size();
|
||||
if (num_tables != 2)
|
||||
return {};
|
||||
|
||||
auto left = typeid_cast<const ASTTablesInSelectQueryElement *>(tables->children[0].get());
|
||||
auto right = typeid_cast<const ASTTablesInSelectQueryElement *>(tables->children[1].get());
|
||||
if (!left || !right || !right->table_join)
|
||||
return {};
|
||||
|
||||
if (auto join = typeid_cast<const ASTTableJoin *>(right->table_join.get()))
|
||||
{
|
||||
if (join->kind == ASTTableJoin::Kind::Cross ||
|
||||
join->kind == ASTTableJoin::Kind::Comma)
|
||||
{
|
||||
if (!join->children.empty())
|
||||
throw Exception("Logical error: CROSS JOIN has expressions", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
auto & left_expr = typeid_cast<const ASTTableExpression &>(*left->table_expression);
|
||||
auto & right_expr = typeid_cast<const ASTTableExpression &>(*right->table_expression);
|
||||
|
||||
table_names.reserve(2);
|
||||
if (extractTableName(left_expr, table_names) &&
|
||||
extractTableName(right_expr, table_names))
|
||||
return right->table_join;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
std::vector<ASTPtr *> CrossToInnerJoinMatcher::visit(ASTPtr & ast, Data & data)
|
||||
{
|
||||
if (auto * t = typeid_cast<ASTSelectQuery *>(ast.get()))
|
||||
visit(*t, ast, data);
|
||||
return {};
|
||||
}
|
||||
|
||||
void CrossToInnerJoinMatcher::visit(ASTSelectQuery & select, ASTPtr & ast, Data & data)
|
||||
{
|
||||
using CheckColumnsMatcher = OneTypeMatcher<CheckColumnsVisitorData>;
|
||||
using CheckColumnsVisitor = InDepthNodeVisitor<CheckColumnsMatcher, true>;
|
||||
|
||||
std::vector<DatabaseAndTableWithAlias> table_names;
|
||||
ASTPtr ast_join = getCrossJoin(select, table_names);
|
||||
if (!ast_join)
|
||||
return;
|
||||
|
||||
/// check Identifier names from where expression
|
||||
CheckColumnsVisitor::Data columns_data{table_names, 0, 0};
|
||||
CheckColumnsVisitor(columns_data).visit(select.where_expression);
|
||||
|
||||
if (!columns_data.allMatch())
|
||||
return;
|
||||
|
||||
auto & join = typeid_cast<ASTTableJoin &>(*ast_join);
|
||||
join.kind = ASTTableJoin::Kind::Inner;
|
||||
join.strictness = ASTTableJoin::Strictness::All; /// TODO: do we need it?
|
||||
|
||||
join.on_expression.swap(select.where_expression);
|
||||
join.children.push_back(join.on_expression);
|
||||
|
||||
ast = ast->clone(); /// rewrite AST in right manner
|
||||
data.done = true;
|
||||
}
|
||||
|
||||
}
|
30
dbms/src/Interpreters/CrossToInnerJoinVisitor.h
Normal file
30
dbms/src/Interpreters/CrossToInnerJoinVisitor.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <Interpreters/InDepthNodeVisitor.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class ASTSelectQuery;
|
||||
|
||||
/// AST transformer. It replaces cross joins with equivalented inner join if possible.
|
||||
class CrossToInnerJoinMatcher
|
||||
{
|
||||
public:
|
||||
struct Data
|
||||
{
|
||||
bool done = false;
|
||||
};
|
||||
|
||||
static constexpr const char * label = "JoinToSubqueryTransform";
|
||||
|
||||
static bool needChildVisit(ASTPtr &, const ASTPtr &) { return true; }
|
||||
static std::vector<ASTPtr *> visit(ASTPtr & ast, Data & data);
|
||||
|
||||
private:
|
||||
static void visit(ASTSelectQuery & select, ASTPtr & ast, Data & data);
|
||||
};
|
||||
|
||||
using CrossToInnerJoinVisitor = InDepthNodeVisitor<CrossToInnerJoinMatcher, true>;
|
||||
|
||||
}
|
@ -27,7 +27,7 @@ struct DatabaseAndTableWithAlias
|
||||
DatabaseAndTableWithAlias() = default;
|
||||
DatabaseAndTableWithAlias(const ASTPtr & identifier_node, const String & current_database = "");
|
||||
DatabaseAndTableWithAlias(const ASTIdentifier & identifier, const String & current_database = "");
|
||||
DatabaseAndTableWithAlias(const ASTTableExpression & table_expression, const String & current_database);
|
||||
DatabaseAndTableWithAlias(const ASTTableExpression & table_expression, const String & current_database = "");
|
||||
|
||||
/// "alias." or "table." if alias is empty
|
||||
String getQualifiedNamePrefix() const;
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
#include <Columns/IColumn.h>
|
||||
|
||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||
#include <Interpreters/ExpressionAnalyzer.h>
|
||||
#include <Interpreters/ExpressionActions.h>
|
||||
#include <Interpreters/InJoinSubqueriesPreprocessor.h>
|
||||
@ -39,7 +38,6 @@
|
||||
#include <Storages/StorageMemory.h>
|
||||
#include <Storages/StorageJoin.h>
|
||||
|
||||
#include <DataStreams/LazyBlockInputStream.h>
|
||||
#include <DataStreams/copyData.h>
|
||||
|
||||
#include <Dictionaries/IDictionary.h>
|
||||
@ -568,9 +566,6 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
|
||||
|
||||
if (!subquery_for_set.join)
|
||||
{
|
||||
JoinPtr join = std::make_shared<Join>(analyzedJoin().key_names_right, settings.join_use_nulls,
|
||||
settings.size_limits_for_join, join_params.kind, join_params.strictness);
|
||||
|
||||
/** For GLOBAL JOINs (in the case, for example, of the push method for executing GLOBAL subqueries), the following occurs
|
||||
* - in the addExternalStorage function, the JOIN (SELECT ...) subquery is replaced with JOIN _data1,
|
||||
* in the subquery_for_set object this subquery is exposed as source and the temporary table _data1 as the `table`.
|
||||
@ -587,39 +582,23 @@ bool ExpressionAnalyzer::appendJoin(ExpressionActionsChain & chain, bool only_ty
|
||||
else if (table_to_join.database_and_table_name)
|
||||
table = table_to_join.database_and_table_name;
|
||||
|
||||
const JoinedColumnsList & columns_from_joined_table = analyzedJoin().columns_from_joined_table;
|
||||
|
||||
Names original_columns;
|
||||
for (const auto & column : analyzedJoin().columns_from_joined_table)
|
||||
for (const auto & column : columns_from_joined_table)
|
||||
if (required_columns_from_joined_table.count(column.name_and_type.name))
|
||||
original_columns.emplace_back(column.original_name);
|
||||
|
||||
auto interpreter = interpretSubquery(table, context, subquery_depth, original_columns);
|
||||
subquery_for_set.source = std::make_shared<LazyBlockInputStream>(
|
||||
interpreter->getSampleBlock(),
|
||||
[interpreter]() mutable { return interpreter->execute().in; });
|
||||
}
|
||||
|
||||
/// Alias duplicating columns as qualified.
|
||||
for (const auto & column : analyzedJoin().columns_from_joined_table)
|
||||
if (required_columns_from_joined_table.count(column.name_and_type.name))
|
||||
subquery_for_set.joined_block_aliases.emplace_back(column.original_name, column.name_and_type.name);
|
||||
|
||||
auto sample_block = subquery_for_set.source->getHeader();
|
||||
for (const auto & name_with_alias : subquery_for_set.joined_block_aliases)
|
||||
{
|
||||
if (sample_block.has(name_with_alias.first))
|
||||
{
|
||||
auto pos = sample_block.getPositionByName(name_with_alias.first);
|
||||
auto column = sample_block.getByPosition(pos);
|
||||
sample_block.erase(pos);
|
||||
column.name = name_with_alias.second;
|
||||
sample_block.insert(std::move(column));
|
||||
}
|
||||
subquery_for_set.makeSource(interpreter, columns_from_joined_table, required_columns_from_joined_table);
|
||||
}
|
||||
|
||||
Block sample_block = subquery_for_set.renamedSampleBlock();
|
||||
joined_block_actions->execute(sample_block);
|
||||
|
||||
/// TODO You do not need to set this up when JOIN is only needed on remote servers.
|
||||
subquery_for_set.join = join;
|
||||
subquery_for_set.join = std::make_shared<Join>(analyzedJoin().key_names_right, settings.join_use_nulls,
|
||||
settings.size_limits_for_join, join_params.kind, join_params.strictness);
|
||||
subquery_for_set.join->setSampleBlock(sample_block);
|
||||
subquery_for_set.joined_block_actions = joined_block_actions;
|
||||
}
|
||||
|
@ -380,8 +380,9 @@ InterpreterSelectQuery::AnalysisResult InterpreterSelectQuery::analyzeExpression
|
||||
|
||||
if (query_analyzer->appendJoin(chain, dry_run || !res.first_stage))
|
||||
{
|
||||
res.has_join = true;
|
||||
res.before_join = chain.getLastActions();
|
||||
if (!res.hasJoin())
|
||||
throw Exception("No expected JOIN", ErrorCodes::LOGICAL_ERROR);
|
||||
chain.addStep();
|
||||
}
|
||||
|
||||
@ -548,7 +549,7 @@ void InterpreterSelectQuery::executeImpl(Pipeline & pipeline, const BlockInputSt
|
||||
|
||||
if (expressions.first_stage)
|
||||
{
|
||||
if (expressions.has_join)
|
||||
if (expressions.hasJoin())
|
||||
{
|
||||
const ASTTableJoin & join = static_cast<const ASTTableJoin &>(*query.join()->table_join);
|
||||
if (join.kind == ASTTableJoin::Kind::Full || join.kind == ASTTableJoin::Kind::Right)
|
||||
|
@ -132,7 +132,7 @@ private:
|
||||
|
||||
struct AnalysisResult
|
||||
{
|
||||
bool has_join = false;
|
||||
bool hasJoin() const { return before_join.get(); }
|
||||
bool has_where = false;
|
||||
bool need_aggregate = false;
|
||||
bool has_having = false;
|
||||
|
@ -185,8 +185,8 @@ BlockIO InterpreterSystemQuery::execute()
|
||||
case Type::STOP_REPLICATED_SENDS:
|
||||
startStopAction(context, query, ActionLocks::PartsSend, false);
|
||||
break;
|
||||
case Type::START_REPLICATEDS_SENDS:
|
||||
startStopAction(context, query, ActionLocks::PartsSend, false);
|
||||
case Type::START_REPLICATED_SENDS:
|
||||
startStopAction(context, query, ActionLocks::PartsSend, true);
|
||||
break;
|
||||
case Type::STOP_REPLICATION_QUEUES:
|
||||
startStopAction(context, query, ActionLocks::ReplicationQueue, false);
|
||||
|
@ -298,6 +298,7 @@ struct Settings
|
||||
M(SettingBool, enable_unaligned_array_join, false, "Allow ARRAY JOIN with multiple arrays that have different sizes. When this settings is enabled, arrays will be resized to the longest one.") \
|
||||
M(SettingBool, low_cardinality_allow_in_native_format, true, "Use LowCardinality type in Native format. Otherwise, convert LowCardinality columns to ordinary for select query, and convert ordinary columns to required LowCardinality for insert query.") \
|
||||
M(SettingBool, allow_experimental_multiple_joins_emulation, false, "Emulate multiple joins using subselects") \
|
||||
M(SettingBool, allow_experimental_cross_to_join_conversion, false, "Convert CROSS JOIN to INNER JOIN if possible") \
|
||||
|
||||
#define DECLARE(TYPE, NAME, DEFAULT, DESCRIPTION) \
|
||||
TYPE NAME {DEFAULT};
|
||||
|
49
dbms/src/Interpreters/SubqueryForSet.cpp
Normal file
49
dbms/src/Interpreters/SubqueryForSet.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <Interpreters/SubqueryForSet.h>
|
||||
#include <Interpreters/AnalyzedJoin.h>
|
||||
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
|
||||
#include <DataStreams/LazyBlockInputStream.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void SubqueryForSet::makeSource(std::shared_ptr<InterpreterSelectWithUnionQuery> & interpreter,
|
||||
const std::list<JoinedColumn> & columns_from_joined_table,
|
||||
const NameSet & required_columns_from_joined_table)
|
||||
{
|
||||
source = std::make_shared<LazyBlockInputStream>(interpreter->getSampleBlock(),
|
||||
[interpreter]() mutable { return interpreter->execute().in; });
|
||||
|
||||
for (const auto & column : columns_from_joined_table)
|
||||
if (required_columns_from_joined_table.count(column.name_and_type.name))
|
||||
joined_block_aliases.emplace_back(column.original_name, column.name_and_type.name);
|
||||
|
||||
sample_block = source->getHeader();
|
||||
for (const auto & name_with_alias : joined_block_aliases)
|
||||
{
|
||||
if (sample_block.has(name_with_alias.first))
|
||||
{
|
||||
auto pos = sample_block.getPositionByName(name_with_alias.first);
|
||||
auto column = sample_block.getByPosition(pos);
|
||||
sample_block.erase(pos);
|
||||
column.name = name_with_alias.second;
|
||||
sample_block.insert(std::move(column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SubqueryForSet::renameColumns(Block & block)
|
||||
{
|
||||
for (const auto & name_with_alias : joined_block_aliases)
|
||||
{
|
||||
if (block.has(name_with_alias.first))
|
||||
{
|
||||
auto pos = block.getPositionByName(name_with_alias.first);
|
||||
auto column = block.getByPosition(pos);
|
||||
block.erase(pos);
|
||||
column.name = name_with_alias.second;
|
||||
block.insert(std::move(column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
49
dbms/src/Interpreters/SubqueryForSet.h
Normal file
49
dbms/src/Interpreters/SubqueryForSet.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Interpreters/PreparedSets.h>
|
||||
#include <Interpreters/ExpressionActions.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class Join;
|
||||
using JoinPtr = std::shared_ptr<Join>;
|
||||
|
||||
class InterpreterSelectWithUnionQuery;
|
||||
struct JoinedColumn;
|
||||
|
||||
|
||||
/// Information on what to do when executing a subquery in the [GLOBAL] IN/JOIN section.
|
||||
struct SubqueryForSet
|
||||
{
|
||||
/// The source is obtained using the InterpreterSelectQuery subquery.
|
||||
BlockInputStreamPtr source;
|
||||
|
||||
/// If set, build it from result.
|
||||
SetPtr set;
|
||||
JoinPtr join;
|
||||
/// Apply this actions to joined block.
|
||||
ExpressionActionsPtr joined_block_actions;
|
||||
|
||||
/// If set, put the result into the table.
|
||||
/// This is a temporary table for transferring to remote servers for distributed query processing.
|
||||
StoragePtr table;
|
||||
|
||||
void makeSource(std::shared_ptr<InterpreterSelectWithUnionQuery> & interpreter,
|
||||
const std::list<JoinedColumn> & columns_from_joined_table,
|
||||
const NameSet & required_columns_from_joined_table);
|
||||
|
||||
Block renamedSampleBlock() const { return sample_block; }
|
||||
void renameColumns(Block & block);
|
||||
|
||||
private:
|
||||
NamesWithAliases joined_block_aliases; /// Rename column from joined block from this list.
|
||||
Block sample_block; /// source->getHeader() + column renames
|
||||
};
|
||||
|
||||
/// ID of subquery -> what to do with it.
|
||||
using SubqueriesForSets = std::unordered_map<String, SubqueryForSet>;
|
||||
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
#include <Parsers/queryToString.h>
|
||||
|
||||
#include <Interpreters/JoinToSubqueryTransformVisitor.h>
|
||||
#include <Interpreters/CrossToInnerJoinVisitor.h>
|
||||
#include <Interpreters/Quota.h>
|
||||
#include <Interpreters/InterpreterFactory.h>
|
||||
#include <Interpreters/ProcessList.h>
|
||||
@ -199,6 +200,14 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
|
||||
logQuery(queryToString(*ast), context);
|
||||
}
|
||||
|
||||
if (settings.allow_experimental_cross_to_join_conversion)
|
||||
{
|
||||
CrossToInnerJoinVisitor::Data cross_to_inner;
|
||||
CrossToInnerJoinVisitor(cross_to_inner).visit(ast);
|
||||
if (cross_to_inner.done)
|
||||
logQuery(queryToString(*ast), context);
|
||||
}
|
||||
|
||||
/// Check the limits.
|
||||
checkASTSizeLimits(*ast, settings);
|
||||
|
||||
|
@ -59,7 +59,7 @@ const char * ASTSystemQuery::typeToString(Type type)
|
||||
return "START FETCHES";
|
||||
case Type::STOP_REPLICATED_SENDS:
|
||||
return "STOP REPLICATED SENDS";
|
||||
case Type::START_REPLICATEDS_SENDS:
|
||||
case Type::START_REPLICATED_SENDS:
|
||||
return "START REPLICATED SENDS";
|
||||
case Type::STOP_REPLICATION_QUEUES:
|
||||
return "STOP REPLICATION QUEUES";
|
||||
@ -97,7 +97,7 @@ void ASTSystemQuery::formatImpl(const FormatSettings & settings, FormatState &,
|
||||
|| type == Type::STOP_FETCHES
|
||||
|| type == Type::START_FETCHES
|
||||
|| type == Type::STOP_REPLICATED_SENDS
|
||||
|| type == Type::START_REPLICATEDS_SENDS
|
||||
|| type == Type::START_REPLICATED_SENDS
|
||||
|| type == Type::STOP_REPLICATION_QUEUES
|
||||
|| type == Type::START_REPLICATION_QUEUES)
|
||||
{
|
||||
|
@ -36,7 +36,7 @@ public:
|
||||
STOP_FETCHES,
|
||||
START_FETCHES,
|
||||
STOP_REPLICATED_SENDS,
|
||||
START_REPLICATEDS_SENDS,
|
||||
START_REPLICATED_SENDS,
|
||||
STOP_REPLICATION_QUEUES,
|
||||
START_REPLICATION_QUEUES,
|
||||
FLUSH_LOGS,
|
||||
|
@ -58,7 +58,7 @@ bool ParserSystemQuery::parseImpl(IParser::Pos & pos, ASTPtr & node, Expected &
|
||||
case Type::STOP_FETCHES:
|
||||
case Type::START_FETCHES:
|
||||
case Type::STOP_REPLICATED_SENDS:
|
||||
case Type::START_REPLICATEDS_SENDS:
|
||||
case Type::START_REPLICATED_SENDS:
|
||||
case Type::STOP_REPLICATION_QUEUES:
|
||||
case Type::START_REPLICATION_QUEUES:
|
||||
parseDatabaseAndTableName(pos, expected, res->target_database, res->target_table);
|
||||
|
@ -128,7 +128,7 @@ else
|
||||
TEST_DICT=${TEST_DICT=1}
|
||||
CLICKHOUSE_CLIENT_QUERY="${CLICKHOUSE_CLIENT} --config ${CLICKHOUSE_CONFIG_CLIENT} --port $CLICKHOUSE_PORT_TCP -m -n -q"
|
||||
$CLICKHOUSE_CLIENT_QUERY 'SELECT * from system.build_options; SELECT * FROM system.clusters;'
|
||||
CLICKHOUSE_TEST="env PATH=$PATH:$BIN_DIR ${TEST_DIR}clickhouse-test --binary ${BIN_DIR}${CLICKHOUSE_BINARY} --configclient $CLICKHOUSE_CONFIG_CLIENT --configserver $CLICKHOUSE_CONFIG --tmp $DATA_DIR/tmp --queries $QUERIES_DIR $TEST_OPT0 $TEST_OPT"
|
||||
CLICKHOUSE_TEST="env ${TEST_DIR}clickhouse-test --binary ${BIN_DIR}${CLICKHOUSE_BINARY} --configclient $CLICKHOUSE_CONFIG_CLIENT --configserver $CLICKHOUSE_CONFIG --tmp $DATA_DIR/tmp --queries $QUERIES_DIR $TEST_OPT0 $TEST_OPT"
|
||||
CLICKHOUSE_PERFORMANCE_TEST="${BIN_DIR}clickhouse-performance-test --port $CLICKHOUSE_PORT_TCP --recursive $CUR_DIR/performance --skip-tags=long"
|
||||
if [ "${TEST_RUN_STRESS}" ]; then
|
||||
# Running test in parallel will fail some results (tests can create/fill/drop same tables)
|
||||
|
@ -228,6 +228,8 @@ sudo -u clickhouse ClickHouse/build/dbms/programs/clickhouse server --config-fil
|
||||
|
||||
Разработка тестов: https://clickhouse.yandex/docs/ru/development/tests/
|
||||
|
||||
Список задач: https://github.com/yandex/ClickHouse/blob/master/dbms/tests/instructions/easy_tasks_sorted_ru.md
|
||||
|
||||
|
||||
# Тестовые данные
|
||||
|
||||
|
342
dbms/tests/instructions/easy_tasks_sorted_ru.md
Normal file
342
dbms/tests/instructions/easy_tasks_sorted_ru.md
Normal file
@ -0,0 +1,342 @@
|
||||
# Простые задачи
|
||||
|
||||
## Пустой параметр --password в клиенте должен быть эквивалентен --ask-password.
|
||||
|
||||
То есть означать предложение ввести пароль в интерактивном режиме.
|
||||
|
||||
`dbms/programs/client/ConnectionParameters.h`
|
||||
|
||||
\* кстати, сейчас функциональность реализована плохо: ввод пароля не поддерживает корректную обработку backspace.
|
||||
|
||||
## Недостатки юзабилити: у clickhouse-client отсутствует сокращённая опция -C, как вариант --config-file; Недостатки юзабилити, если пользователь не может прочитать конфиг клиента.
|
||||
|
||||
`dbms/programs/client/Client.cpp`
|
||||
|
||||
Также делаем `chmod 000 /etc/clickhouse-client/config.xml` и смотрим, что получится.
|
||||
|
||||
## Оператор NOT BETWEEN.
|
||||
|
||||
`SELECT * FROM system.numbers WHERE number NOT BETWEEN 5 AND 10 LIMIT 10`
|
||||
|
||||
`ExpressionListParsers.cpp`: `ParserBetweenExpression::parseImpl`
|
||||
|
||||
## HTTP заголовок query_id.
|
||||
|
||||
`programs/server/HTTPHandler.cpp` - смотрим метод `executeQuery`
|
||||
|
||||
`src/Interpreters/executeQuery.h`
|
||||
|
||||
`src/Interpreters/executeQuery.cpp` - смотрим колбэк на выставление Content-Type
|
||||
|
||||
## Уменьшать max_memory_usage и размеры кэшей при старте, если на сервере мало оперативки.
|
||||
|
||||
Смотрим, сколько на сервере оперативки. Если `max_memory_usage`, `max_memory_usage_for_all_queries` ограничены, но больше 90% (настройка) от имеющейся оперативки, то уменьшать их и выводить предупреждение в лог. Аналогично для кэшей: `mark_cache`, `uncompressed_cache`.
|
||||
|
||||
`programs/server/Server.cpp` - инициализация сервера, установка размера кэшей
|
||||
|
||||
`getMemoryAmount.h` - информация о доступной оперативке
|
||||
|
||||
`context.setSetting` - для выставления `max_memory_usage` и других.
|
||||
|
||||
## Битовые операции для FixedString.
|
||||
|
||||
bitAnd, bitOr, bitNot, bitXor для значения типа FixedString, интерпретируемого как набор бит.
|
||||
|
||||
Сделайте сначала в C++ побитовые функции для работы с куском памяти:
|
||||
```
|
||||
void memoryBitAnd(const char * a, const char * b, char * result, size_t size);
|
||||
```
|
||||
Потом используйте их в вашей функции.
|
||||
|
||||
## Функция arrayWithConstant.
|
||||
|
||||
`arrayWithConstant(3, 'hello') = ['hello', 'hello', 'hello']`
|
||||
|
||||
Смотрите метод `IColumn::replicate` для размножения значений столбца.
|
||||
|
||||
## Функция flatten для превращения массивов массивов в массив элементов.
|
||||
|
||||
`flatten([[1, 2, 3], [4, 5]]) = [1, 2, 3, 4, 5]`
|
||||
`ColumnArray` - внимательно изучаем, как устроены массивы в ClickHouse.
|
||||
|
||||
## Добавить generic вариант функций least, greatest.
|
||||
|
||||
`SELECT least(123, 456)` - работает.
|
||||
|
||||
`SELECT least('123', '456')` - не работает. Надо сделать.
|
||||
|
||||
Делаем с помощью `IColumn::compareAt` для одинаковых типов и с помощью `castColumn`, `getLeastSuperType` для разных.
|
||||
|
||||
## При ATTACH кусков, проверять владельца файлов.
|
||||
|
||||
Смотрим, что все файлы в прикрепляемых кусках от правильного пользователя.
|
||||
|
||||
## COLLATE должно работать для Nullable(String).
|
||||
|
||||
В ClickHouse есть возможность указать collation для сортировки строк. Это не работает для `Nullable(String)`.
|
||||
|
||||
## Проверить возможность использования pdqsort вместо std::sort для полной comparison-based сортировки.
|
||||
|
||||
В случае, когда есть ORDER BY без LIMIT, это может позволить слегка увеличить производительность.
|
||||
|
||||
## Запретить чтение значений типа AggregateFunction по-умолчанию и добавить настройку.
|
||||
|
||||
Состояния агрегатных функций могут быть записаны в дамп и считаны из него. Но десериализация состояний агрегатных функций небезопасна. Аккуратно выбранные пользовательские данные могут привести к segfault или порче памяти. Поэтому нужно просто сделать настройку, которая запрещает читать AggregateFunction из пользовательских данных.
|
||||
|
||||
## Опции progress и time для clickhouse-local (по аналогии с clickhouse-client).
|
||||
|
||||
Возможность выводить время выполнения запроса, а также красивый прогресс-бар для каждого запроса.
|
||||
|
||||
## Usability: clickhouse-server должен поддерживать --help.
|
||||
|
||||
## В статистику jemalloc добавить информацию по arenas.
|
||||
|
||||
В `system.asynchronous_metrics` - суммарный размер арен.
|
||||
|
||||
## Добавить агрегатную функцию topKWeighted.
|
||||
|
||||
`SELECT topKWeighted(value, weight)` - учитывать каждое значение с весом.
|
||||
|
||||
## Функция isValidUTF8, toValidUTF8.
|
||||
|
||||
`isValidUTF8` возвращает 1, если строка содержит набор байт в кодировке UTF-8.
|
||||
|
||||
`toValidUTF8` - заменяет последовательности байт, не соответствующие кодировке UTF-8, на replacement character.
|
||||
|
||||
|
||||
# Более сложные задачи
|
||||
|
||||
## CREATE TABLE AS table_function()
|
||||
|
||||
Возможность создать таблицу с таким же типом и структурой, как табличная функция.
|
||||
|
||||
`ParserCreateQuery.cpp`, `InterpreterCreateQuery`, `Context::executeTableFunction`
|
||||
|
||||
## Layout внешних словарей "direct".
|
||||
|
||||
Как cache, но без кэша — всегда прямой запрос в источник.
|
||||
|
||||
## Подсказки в фабриках на основе edit distance.
|
||||
|
||||
Всевозможные объекты: функции, агрегатные функции, типы данных, движки таблиц, и т. п. достаются по имени из фабрик. Часто пользователь допускает опечатку. Например, вместо `SELECT count(*)` может быть написано `SELECT cunt(*)`. В случае опечатки, необходимо в текст сообщения добавлять указание на ближайшие варианты. Для реализации можно использовать расстояние Левенштейна и полный перебор, или (лучше) - триграмный индекс. Подсказки выдаём, если указанное имя отличается от существующего на 1..2 буквы. Сортируем возможные варианты в порядке похожести. Для того, чтобы это работало во всех фабриках, может быть, потребуется обобщить их.
|
||||
|
||||
## Учитывать порядок столбцов в заголовке в форматах CSV и TSV.
|
||||
|
||||
В заголовке CSV, TSV могут быть указаны имена столбцов. Сейчас они полностью игнорируются. Надо учитывать, под настройкой.
|
||||
|
||||
## Функции randomFixedString, randomBinaryString, fuzzBits, fuzzBytes.
|
||||
|
||||
## Функции для geoHash.
|
||||
|
||||
Geohash - способ преобразования географических координат в строку, так что отображение обладает свойством локальности. https://en.wikipedia.org/wiki/Geohash В качестве библиотеки следует использовать эту: https://github.com/yinqiwen/geohash-int Необходимо добавить функции для перевода в обе стороны, а также для числового и текстового вариантов.
|
||||
|
||||
## Агрегатные функции для статистических тестов (e.g. тест нормальности распределения) и статистик (e.g. энтропия).
|
||||
|
||||
Энтропию следует считать по гистограмме. Пример расчёта гистограммы смотрите в реализации функции `quantileExact`.
|
||||
|
||||
https://github.com/yandex/ClickHouse/issues/3266
|
||||
|
||||
## Функции создания и обновления состояния агрегатной функции по одному кортежу аргументов.
|
||||
|
||||
В ClickHouse есть понятие - состояние вычисления агрегатной функции. Состояния агрегатных функций можно записывать в таблицы, складывать, финализировать и т. п. https://clickhouse.yandex/docs/ru/data_types/nested_data_structures/aggregatefunction/
|
||||
|
||||
Получить состояние агрегатной функции можно с помощью комбинатора State: https://clickhouse.yandex/docs/ru/query_language/agg_functions/combinators/#-state Но хотелось бы добавить ещё более простой способ получения состояния агрегатной функции.
|
||||
|
||||
Например:
|
||||
|
||||
`createAggregationState('groupArray')` - создать пустое (начальное) состояние агрегатной функции.
|
||||
|
||||
`createAggregationState('groupArray', 1)` - создать состояние агрегатной функции, в котором агрегировано одно значение 1.
|
||||
|
||||
`createAggregationState('argMax', ('hello', 123))` - то же самое для агрегатных функций, принимающих несколько аргументов.
|
||||
|
||||
## Корректное сравнение Date и DateTime.
|
||||
|
||||
https://github.com/yandex/ClickHouse/issues/2011
|
||||
|
||||
Нужно сравнивать Date и DateTime так, как будто Date расширено до DateTime на начало суток в том же часовом поясе.
|
||||
|
||||
## LEFT ONLY JOIN
|
||||
|
||||
## Функции makeDate, makeDateTime.
|
||||
|
||||
`makeDate(year, month, day)`
|
||||
`makeDateTime(year, month, day, hour, minute, second, [timezone])`
|
||||
|
||||
## Функции changeYear, changeMonth, ...
|
||||
|
||||
`changeYear(datetime, 2019)`
|
||||
|
||||
## Исправить мерцание прогресс-бара в clickhouse-client.
|
||||
|
||||
Это заметно при работе с серверами с большим пингом.
|
||||
Прогресс бар не должен мерцать.
|
||||
Наверное, надо просто вместо очистки строки, перемещать курсор в начало, не очищая её.
|
||||
|
||||
## Функция format для вставки значений в строку-шаблон.
|
||||
|
||||
`format('Hello {2} World {1}', x, y)`
|
||||
|
||||
## Добавить поддержку hyperscan.
|
||||
|
||||
https://github.com/intel/hyperscan
|
||||
|
||||
Реализовать на основе этой библиотеки функцию для матчинга сразу большого количества регулярных выражений.
|
||||
|
||||
## Функция rowNumberForKey.
|
||||
|
||||
Возвращает инкрементальное число для повторно встречающихся значений key.
|
||||
|
||||
## Агрегатная функция groupConcat.
|
||||
|
||||
`groupConcat(x, ',')` - собрать из переданных значений x строку, разделённую запятыми.
|
||||
|
||||
## Функции DATE_ADD, DATE_SUB как синонимы для совместимости с SQL.
|
||||
|
||||
https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_date-add
|
||||
|
||||
## Функции positionReverse, positionUTF8Reverse, positionCaseInsensitiveReverse, positionCaseInsensitiveUTF8Reverse.
|
||||
|
||||
position с конца строки.
|
||||
|
||||
## Функция indexOf должна поддерживать Enum-ы без cast-а.
|
||||
|
||||
`indexOf(arr, 'hello')`, `indexOf(arr, 1)` должны работать, если arr имеет тип `Array(Enum8('hello' = 1, 'world' = 2))`
|
||||
|
||||
## Комбинатор агрегатных функций Distinct.
|
||||
|
||||
Пример: `avgDistinct(x)` - вычислить среднее по всем различным переданным значениям.
|
||||
|
||||
## Проверка набора инструкций при старте сервера.
|
||||
|
||||
Если сервер собран с поддержкой SSE 4.2, 4.1, 4, SSSE 3, SSE 3, то как можно ближе к началу работы, запускаем функцию, которая выполняет нужную инструкцию в качестве теста (asm volatile вставка), а до этого ставим обработчик сигнала SIGILL, который в случае невозможности выполнить инструкцию, сделает siglongjmp, позволит нам вывести понятное сообщение в лог и завершить работу. Замечание: /proc/cpuinfo зачастую не содержит актуальную информацию.
|
||||
|
||||
## Добавить сжатие Brotli для HTTP интерфейса.
|
||||
|
||||
`Content-Encoding: br`
|
||||
|
||||
## Метрики количества ошибок.
|
||||
|
||||
Добавляем счётчики всех ошибок (ErrorCodes) по аналогии с ProfileEvents. Кроме количества запоминаем также время последней ошибки, стек трейс, сообщение. Добавляем системную таблицу system.errors. Отправка в Graphite.
|
||||
|
||||
## Добавить Lizard, LZSSE и density в качестве вариантов алгоритмов сжатия.
|
||||
|
||||
Экспериментальные алгоритмы сжатия. Сейчас ClickHouse поддерживает только lz4 и zstd.
|
||||
|
||||
## Запрос CREATE OR REPLACE TABLE/VIEW.
|
||||
|
||||
Атомарно (под блокировкой) удаляет таблицу перед созданием новой, если такая была.
|
||||
|
||||
## Приведение типов для IN (subquery).
|
||||
|
||||
`SELECT 1 IN (SELECT -1 UNION ALL SELECT 1)`
|
||||
|
||||
- сейчас не работает.
|
||||
|
||||
## Возможность задать смещение для LIMIT BY.
|
||||
|
||||
https://clickhouse.yandex/docs/ru/query_language/select/#limit-n-by
|
||||
|
||||
`LIMIT 100, 10 BY RegionID` - выдать не более 10 строк для каждого RegionID, но пропустив первые 100 строк.
|
||||
|
||||
## Возможность вставки значений типа AggregateFunction в виде кортежа значений аргументов, а не бинарного дампа состояния, под настройкой.
|
||||
|
||||
Во входных данных в запросе INSERT должна быть возможность передать значение типа AggregateFunction не в виде сериализованного состояния, а в виде аргументов, которые будут агрегированы, для формирования этого состояния.
|
||||
|
||||
## Возможность использовать ALIAS столбцы при INSERT.
|
||||
|
||||
https://clickhouse.yandex/docs/en/query_language/create/#create-table
|
||||
|
||||
`INSERT INTO table (column1, column2, ...)`
|
||||
|
||||
- если column - это ALIAS столбец, и если выражение для ALIAS тривиально (просто ссылается на другой столбец), то разрешить использовать его вместо другого столбца в запросе INSERT.
|
||||
|
||||
## Запрос ALTER TABLE LOCK/UNLOCK PARTITION.
|
||||
|
||||
Запретить модификацию данных в партиции. На партицию ставится флаг, что она заблокирована. В неё нельзя делать INSERT и ALTER. С файлов снимается доступ на запись.
|
||||
|
||||
## Поддержка произвольных константных выражений в LIMIT.
|
||||
|
||||
Возможность писать `LIMIT 1 + 2`. То же самое для `LIMIT BY`.
|
||||
|
||||
## Добавить информацию об exp-smoothed количестве ошибок соединений с репликами в таблицу system.clusters.
|
||||
|
||||
У нас есть счётчик ошибок соединения с серверами для failover. Надо сделать его видимым для пользователя.
|
||||
|
||||
## Настройка join_use_nulls: поддержка для LEFT ARRAY JOIN.
|
||||
|
||||
## Внешние словари из Redis/Aerospike/Couchbase/Cassandra (на выбор).
|
||||
|
||||
Подключить одну из key-value БД как источник.
|
||||
|
||||
## Движок таблиц Mongo, табличная функция mongo.
|
||||
|
||||
Возможность легко импортировать данные из MongoDB.
|
||||
|
||||
## Возможность использования нескольких потоков для INSERT при INSERT SELECT.
|
||||
|
||||
При INSERT SELECT, запрос SELECT может выполняться параллельно, но все данные будут передаваться на вставку в INSERT в один поток. Хотя некоторые таблицы (семейства MergeTree) поддерживают параллельную вставку. Необходимо сделать настройку для максимального количества потоков для INSERT.
|
||||
|
||||
## Корректная обработка multiline значений в Pretty форматах.
|
||||
SELECT 'hello\nworld' AS x, 123 AS y
|
||||
```
|
||||
┌─x──────────┬───y─┐
|
||||
│ hello
|
||||
world │ 123 │
|
||||
└────────────┴─────┘
|
||||
```
|
||||
А надо так:
|
||||
```
|
||||
┌─x─────┬───y─┐
|
||||
│ hello…│ 123 │
|
||||
│…world │ │
|
||||
└───────┴─────┘
|
||||
```
|
||||
|
||||
## Писать логи ClickHouse в ClickHouse.
|
||||
|
||||
Пишем текстовые логи ClickHouse в системную таблицу в структурированном виде.
|
||||
|
||||
См. SystemLog.h, cpp.
|
||||
|
||||
## Работоспособность внешних данных на время сессии.
|
||||
|
||||
https://clickhouse.yandex/docs/en/operations/table_engines/external_data/
|
||||
|
||||
Не работает, если открыть clickhouse-client в интерактивном режиме и делать несколько запросов.
|
||||
|
||||
## Настройка для возможности получить частичный результат при cancel-е.
|
||||
|
||||
Хотим по Ctrl+C получить те данные, которые успели обработаться.
|
||||
|
||||
## Раскрытие кортежей в функциях высшего порядка.
|
||||
|
||||
## Табличная функция loop.
|
||||
|
||||
`SELECT * FROM loop(database, table)`
|
||||
|
||||
Читает данные из таблицы в бесконечном цикле.
|
||||
|
||||
## Настройка, позволяющая обратиться ко всем репликам кластера, как к разным шардам.
|
||||
|
||||
## Возможность ATTACH партиции с меньшим или большим количеством столбцов.
|
||||
|
||||
## Поддержка неконстантного аргумента с тайм-зоной у некоторых функций для работы с датой и временем.
|
||||
|
||||
## Возможность задавать параметры соединений для табличных функций, движков таблиц и для реплик из отдельных разделов конфигурации.
|
||||
|
||||
## Настройка rollup_use_nulls.
|
||||
|
||||
## Настройка cast_keep_nullable.
|
||||
|
||||
## Функция bitEquals для сравнения произвольных типов данных побитово.
|
||||
|
||||
## Функция serialize для implementation specific non portable non backwards compatible сериализации любого типа данных в набор байт.
|
||||
|
||||
## Функция arrayEnumerateUniqDeep
|
||||
|
||||
Как arrayEnumerateUniq, но смотрит на самые глубокие элементы вложенных массивов.
|
||||
|
||||
## Функция bitEquals и оператор <=>.
|
||||
|
||||
## Параллельный ALTER MODIFY COLUMN.
|
@ -2,9 +2,10 @@
|
||||
<name>trim_whitespaces</name>
|
||||
<type>loop</type>
|
||||
|
||||
<preconditions>
|
||||
<table_exists>whitespaces</table_exists>
|
||||
</preconditions>
|
||||
<create_query>CREATE TABLE IF NOT EXISTS whitespaces(value String) ENGINE = MergeTree() PARTITION BY tuple() ORDER BY tuple()</create_query>
|
||||
<fill_query> INSERT INTO whitespaces SELECT value FROM (SELECT arrayStringConcat(groupArray(' ')) AS spaces, concat(spaces, toString(any(number)), spaces) AS value FROM numbers(100000000) GROUP BY pow(number, intHash32(number) % 4) % 12345678)</fill_query>
|
||||
<fill_query> INSERT INTO whitespaces SELECT value FROM (SELECT arrayStringConcat(groupArray(' ')) AS spaces, concat(spaces, toString(any(number)), spaces) AS value FROM numbers(100000000) GROUP BY pow(number, intHash32(number) % 4) % 12345678)</fill_query>
|
||||
<fill_query> INSERT INTO whitespaces SELECT value FROM (SELECT arrayStringConcat(groupArray(' ')) AS spaces, concat(spaces, toString(any(number)), spaces) AS value FROM numbers(100000000) GROUP BY pow(number, intHash32(number) % 4) % 12345678)</fill_query>
|
||||
|
||||
<stop_conditions>
|
||||
<all_of>
|
||||
@ -32,4 +33,6 @@
|
||||
</substitutions>
|
||||
|
||||
<query>SELECT count() FROM whitespaces WHERE NOT ignore({func})</query>
|
||||
|
||||
<drop_query>DROP TABLE IF EXISTS whitespaces</drop_query>
|
||||
</test>
|
||||
|
@ -1,17 +0,0 @@
|
||||
CREATE TABLE whitespaces
|
||||
(
|
||||
value String
|
||||
)
|
||||
ENGINE = MergeTree()
|
||||
PARTITION BY tuple()
|
||||
ORDER BY tuple()
|
||||
|
||||
INSERT INTO whitespaces SELECT value
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
arrayStringConcat(groupArray(' ')) AS spaces,
|
||||
concat(spaces, toString(any(number)), spaces) AS value
|
||||
FROM numbers(100000000)
|
||||
GROUP BY pow(number, intHash32(number) % 4) % 12345678
|
||||
) -- repeat something like this multiple times and/or just copy whitespaces table into itself
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,23 @@
|
||||
cross
|
||||
1 1 1 1
|
||||
2 2 2 \N
|
||||
cross nullable
|
||||
1 1 1 1
|
||||
cross nullable vs not nullable
|
||||
1 1 1 1
|
||||
Explain ParsedAST (children 1)\n SelectWithUnionQuery (children 1)\n ExpressionList (children 1)\n SelectQuery (children 3)\n ExpressionList (children 1)\n Asterisk\n TablesInSelectQuery (children 2)\n TablesInSelectQueryElement (children 1)\n TableExpression (children 1)\n Identifier t1\n TablesInSelectQueryElement (children 2)\n TableExpression (children 1)\n Identifier t2\n TableJoin\n Function equals (children 1)\n ExpressionList (children 2)\n Identifier t1.a\n Identifier t2.a\n
|
||||
Explain ParsedAST (children 1)\n SelectWithUnionQuery (children 1)\n ExpressionList (children 1)\n SelectQuery (children 3)\n ExpressionList (children 1)\n Asterisk\n TablesInSelectQuery (children 2)\n TablesInSelectQueryElement (children 1)\n TableExpression (children 1)\n Identifier t1\n TablesInSelectQueryElement (children 2)\n TableExpression (children 1)\n Identifier t2\n TableJoin\n Function equals (children 1)\n ExpressionList (children 2)\n Identifier t1.a\n Identifier t2.a\n
|
||||
Explain ParsedAST (children 1)\n SelectWithUnionQuery (children 1)\n ExpressionList (children 1)\n SelectQuery (children 2)\n ExpressionList (children 1)\n Asterisk\n TablesInSelectQuery (children 2)\n TablesInSelectQueryElement (children 1)\n TableExpression (children 1)\n Identifier t1\n TablesInSelectQueryElement (children 2)\n TableJoin (children 1)\n Function equals (children 1)\n ExpressionList (children 2)\n Identifier t1.a\n Identifier t2.a\n TableExpression (children 1)\n Identifier t2\n
|
||||
Explain ParsedAST (children 1)\n SelectWithUnionQuery (children 1)\n ExpressionList (children 1)\n SelectQuery (children 2)\n ExpressionList (children 1)\n Asterisk\n TablesInSelectQuery (children 2)\n TablesInSelectQueryElement (children 1)\n TableExpression (children 1)\n Identifier t1\n TablesInSelectQueryElement (children 2)\n TableJoin (children 1)\n Function equals (children 1)\n ExpressionList (children 2)\n Identifier t1.a\n Identifier t2.a\n TableExpression (children 1)\n Identifier t2\n
|
||||
cross
|
||||
1 1 1 1
|
||||
2 2 2 \N
|
||||
cross nullable
|
||||
1 1 1 1
|
||||
cross nullable vs not nullable
|
||||
1 1 1 1
|
||||
comma
|
||||
1 1 1 1
|
||||
2 2 2 \N
|
||||
comma nullable
|
||||
1 1 1 1
|
42
dbms/tests/queries/0_stateless/00826_cross_to_inner_join.sql
Normal file
42
dbms/tests/queries/0_stateless/00826_cross_to_inner_join.sql
Normal file
@ -0,0 +1,42 @@
|
||||
USE test;
|
||||
|
||||
DROP TABLE IF EXISTS t1;
|
||||
DROP TABLE IF EXISTS t2;
|
||||
|
||||
CREATE TABLE t1 (a Int8, b Nullable(Int8)) ENGINE = Memory;
|
||||
CREATE TABLE t2 (a Int8, b Nullable(Int8)) ENGINE = Memory;
|
||||
|
||||
INSERT INTO t1 values (1,1), (2,2);
|
||||
INSERT INTO t2 values (1,1);
|
||||
INSERT INTO t2 (a) values (2), (3);
|
||||
|
||||
SELECT 'cross';
|
||||
SELECT * FROM t1 cross join t2 where t1.a = t2.a;
|
||||
SELECT 'cross nullable';
|
||||
SELECT * FROM t1 cross join t2 where t1.b = t2.b;
|
||||
SELECT 'cross nullable vs not nullable';
|
||||
SELECT * FROM t1 cross join t2 where t1.a = t2.b;
|
||||
|
||||
SET enable_debug_queries = 1;
|
||||
AST SELECT * FROM t1 cross join t2 where t1.a = t2.a;
|
||||
AST SELECT * FROM t1, t2 where t1.a = t2.a;
|
||||
|
||||
SET allow_experimental_cross_to_join_conversion = 1;
|
||||
|
||||
AST SELECT * FROM t1 cross join t2 where t1.a = t2.a;
|
||||
AST SELECT * FROM t1, t2 where t1.a = t2.a;
|
||||
|
||||
SELECT 'cross';
|
||||
SELECT * FROM t1 cross join t2 where t1.a = t2.a;
|
||||
SELECT 'cross nullable';
|
||||
SELECT * FROM t1 cross join t2 where t1.b = t2.b;
|
||||
SELECT 'cross nullable vs not nullable';
|
||||
SELECT * FROM t1 cross join t2 where t1.a = t2.b;
|
||||
|
||||
SELECT 'comma';
|
||||
SELECT * FROM t1, t2 where t1.a = t2.a;
|
||||
SELECT 'comma nullable';
|
||||
SELECT * FROM t1, t2 where t1.b = t2.b;
|
||||
|
||||
DROP TABLE t1;
|
||||
DROP TABLE t2;
|
@ -32,6 +32,7 @@
|
||||
- [Prometheus](https://prometheus.io/)
|
||||
- [clickhouse_exporter](https://github.com/f1yegor/clickhouse_exporter)
|
||||
- [PromHouse](https://github.com/Percona-Lab/PromHouse)
|
||||
- [clickhouse_exporter](https://github.com/hot-wifi/clickhouse_exporter) (uses [Go client](https://github.com/kshvakov/clickhouse/))
|
||||
- [Nagios](https://www.nagios.org/)
|
||||
- [check_clickhouse](https://github.com/exogroup/check_clickhouse/)
|
||||
- Logging
|
||||
|
@ -31,6 +31,7 @@
|
||||
- [Prometheus](https://prometheus.io/)
|
||||
- [clickhouse_exporter](https://github.com/f1yegor/clickhouse_exporter)
|
||||
- [PromHouse](https://github.com/Percona-Lab/PromHouse)
|
||||
- [clickhouse_exporter](https://github.com/hot-wifi/clickhouse_exporter) (использует [Go client](https://github.com/kshvakov/clickhouse/))
|
||||
- [Nagios](https://www.nagios.org/)
|
||||
- [check_clickhouse](https://github.com/exogroup/check_clickhouse/)
|
||||
- Логирование
|
||||
|
@ -19,7 +19,7 @@ add_library (common ${LINK_MODE}
|
||||
src/JSON.cpp
|
||||
src/getMemoryAmount.cpp
|
||||
src/demangle.cpp
|
||||
src/SetTerminalEcho.cpp
|
||||
src/setTerminalEcho.cpp
|
||||
|
||||
include/common/Types.h
|
||||
include/common/DayNum.h
|
||||
@ -37,7 +37,7 @@ add_library (common ${LINK_MODE}
|
||||
include/common/JSON.h
|
||||
include/common/getMemoryAmount.h
|
||||
include/common/demangle.h
|
||||
include/common/SetTerminalEcho.h
|
||||
include/common/setTerminalEcho.h
|
||||
include/common/find_symbols.h
|
||||
include/common/constexpr_helpers.h
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
/// Enable or disable echoing of typed characters. Throws std::runtime_error on error.
|
||||
void SetTerminalEcho(bool enable);
|
||||
void setTerminalEcho(bool enable);
|
@ -1,6 +1,6 @@
|
||||
// https://stackoverflow.com/questions/1413445/reading-a-password-from-stdcin
|
||||
|
||||
#include <common/SetTerminalEcho.h>
|
||||
#include <common/setTerminalEcho.h>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@ -13,13 +13,13 @@
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
void SetTerminalEcho(bool enable)
|
||||
void setTerminalEcho(bool enable)
|
||||
{
|
||||
#ifdef WIN32
|
||||
auto handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD mode;
|
||||
if (!GetConsoleMode(handle, &mode))
|
||||
throw std::runtime_error(std::string("SetTerminalEcho failed get: ") + std::to_string(GetLastError()));
|
||||
throw std::runtime_error(std::string("setTerminalEcho failed get: ") + std::to_string(GetLastError()));
|
||||
|
||||
if (!enable)
|
||||
mode &= ~ENABLE_ECHO_INPUT;
|
||||
@ -27,11 +27,11 @@ void SetTerminalEcho(bool enable)
|
||||
mode |= ENABLE_ECHO_INPUT;
|
||||
|
||||
if (!SetConsoleMode(handle, mode))
|
||||
throw std::runtime_error(std::string("SetTerminalEcho failed set: ") + std::to_string(GetLastError()));
|
||||
throw std::runtime_error(std::string("setTerminalEcho failed set: ") + std::to_string(GetLastError()));
|
||||
#else
|
||||
struct termios tty;
|
||||
if (tcgetattr(STDIN_FILENO, &tty))
|
||||
throw std::runtime_error(std::string("SetTerminalEcho failed get: ") + strerror(errno));
|
||||
throw std::runtime_error(std::string("setTerminalEcho failed get: ") + strerror(errno));
|
||||
if (!enable)
|
||||
tty.c_lflag &= ~ECHO;
|
||||
else
|
||||
@ -39,6 +39,6 @@ void SetTerminalEcho(bool enable)
|
||||
|
||||
auto ret = tcsetattr(STDIN_FILENO, TCSANOW, &tty);
|
||||
if (ret)
|
||||
throw std::runtime_error(std::string("SetTerminalEcho failed set: ") + strerror(errno));
|
||||
throw std::runtime_error(std::string("setTerminalEcho failed set: ") + strerror(errno));
|
||||
#endif
|
||||
}
|
Loading…
Reference in New Issue
Block a user