mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-23 08:02:02 +00:00
Merge branch 'ClickHouse:master' into fix_in_operator_type_conversion
This commit is contained in:
commit
f576ad5b60
@ -508,7 +508,7 @@ Now `rule` can configure `method`, `headers`, `url`, `handler`:
|
||||
|
||||
- `headers` are responsible for matching the header part of the HTTP request. It is compatible with RE2’s regular expressions. It is an optional configuration. If it is not defined in the configuration file, it does not match the header portion of the HTTP request.
|
||||
|
||||
- `handler` contains the main processing part. Now `handler` can configure `type`, `status`, `content_type`, `response_content`, `query`, `query_param_name`.
|
||||
- `handler` contains the main processing part. Now `handler` can configure `type`, `status`, `content_type`, `http_response_headers`, `response_content`, `query`, `query_param_name`.
|
||||
`type` currently supports three types: [predefined_query_handler](#predefined_query_handler), [dynamic_query_handler](#dynamic_query_handler), [static](#static).
|
||||
|
||||
- `query` — use with `predefined_query_handler` type, executes query when the handler is called.
|
||||
@ -519,6 +519,8 @@ Now `rule` can configure `method`, `headers`, `url`, `handler`:
|
||||
|
||||
- `content_type` — use with any type, response [content-type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type).
|
||||
|
||||
- `http_response_headers` — use with any type, response headers map. Could be used to set content type as well.
|
||||
|
||||
- `response_content` — use with `static` type, response content sent to client, when using the prefix ‘file://’ or ‘config://’, find the content from the file or configuration sends to client.
|
||||
|
||||
Next are the configuration methods for different `type`.
|
||||
@ -616,6 +618,33 @@ Return a message.
|
||||
<type>static</type>
|
||||
<status>402</status>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<http_response_headers>
|
||||
<Content-Language>en</Content-Language>
|
||||
<X-My-Custom-Header>43</X-My-Custom-Header>
|
||||
</http_response_headers>
|
||||
<response_content>Say Hi!</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
<defaults/>
|
||||
</http_handlers>
|
||||
```
|
||||
|
||||
`http_response_headers` could be used to set content type instead of `content_type`.
|
||||
|
||||
``` xml
|
||||
<http_handlers>
|
||||
<rule>
|
||||
<methods>GET</methods>
|
||||
<headers><XXX>xxx</XXX></headers>
|
||||
<url>/hi</url>
|
||||
<handler>
|
||||
<type>static</type>
|
||||
<status>402</status>
|
||||
<http_response_headers>
|
||||
<Content-Type>text/html; charset=UTF-8</Content-Type>
|
||||
<Content-Language>en</Content-Language>
|
||||
<X-My-Custom-Header>43</X-My-Custom-Header>
|
||||
</http_response_headers>
|
||||
<response_content>Say Hi!</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
@ -696,6 +725,9 @@ Find the content from the file send to client.
|
||||
<handler>
|
||||
<type>static</type>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<http_response_headers>
|
||||
<ETag>737060cd8c284d8af7ad3082f209582d</ETag>
|
||||
</http_response_headers>
|
||||
<response_content>file:///absolute_path_file.html</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
@ -706,6 +738,9 @@ Find the content from the file send to client.
|
||||
<handler>
|
||||
<type>static</type>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<http_response_headers>
|
||||
<ETag>737060cd8c284d8af7ad3082f209582d</ETag>
|
||||
</http_response_headers>
|
||||
<response_content>file://./relative_path_file.html</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
|
@ -639,6 +639,10 @@ An internal metric of the low-level memory allocator (jemalloc). See https://jem
|
||||
|
||||
An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html
|
||||
|
||||
### jemalloc.prof.active
|
||||
|
||||
An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html
|
||||
|
||||
**See Also**
|
||||
|
||||
- [Monitoring](../../operations/monitoring.md) — Base concepts of ClickHouse monitoring.
|
||||
|
@ -7,33 +7,43 @@ sidebar_label: Float32, Float64
|
||||
# Float32, Float64
|
||||
|
||||
:::note
|
||||
If you need accurate calculations, in particular if you work with financial or business data requiring a high precision you should consider using Decimal instead. Floats might lead to inaccurate results as illustrated below:
|
||||
If you need accurate calculations, in particular if you work with financial or business data requiring a high precision, you should consider using [Decimal](../data-types/decimal.md) instead.
|
||||
|
||||
```
|
||||
[Floating Point Numbers](https://en.wikipedia.org/wiki/IEEE_754) might lead to inaccurate results as illustrated below:
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS float_vs_decimal
|
||||
(
|
||||
my_float Float64,
|
||||
my_decimal Decimal64(3)
|
||||
)Engine=MergeTree ORDER BY tuple()
|
||||
|
||||
INSERT INTO float_vs_decimal SELECT round(randCanonical(), 3) AS res, res FROM system.numbers LIMIT 1000000; # Generate 1 000 000 random number with 2 decimal places and store them as a float and as a decimal
|
||||
)
|
||||
Engine=MergeTree
|
||||
ORDER BY tuple();
|
||||
|
||||
# Generate 1 000 000 random numbers with 2 decimal places and store them as a float and as a decimal
|
||||
INSERT INTO float_vs_decimal SELECT round(randCanonical(), 3) AS res, res FROM system.numbers LIMIT 1000000;
|
||||
```
|
||||
```
|
||||
SELECT sum(my_float), sum(my_decimal) FROM float_vs_decimal;
|
||||
> 500279.56300000014 500279.563
|
||||
|
||||
┌──────sum(my_float)─┬─sum(my_decimal)─┐
|
||||
│ 499693.60500000004 │ 499693.605 │
|
||||
└────────────────────┴─────────────────┘
|
||||
|
||||
SELECT sumKahan(my_float), sumKahan(my_decimal) FROM float_vs_decimal;
|
||||
> 500279.563 500279.563
|
||||
|
||||
┌─sumKahan(my_float)─┬─sumKahan(my_decimal)─┐
|
||||
│ 499693.605 │ 499693.605 │
|
||||
└────────────────────┴──────────────────────┘
|
||||
```
|
||||
:::
|
||||
|
||||
[Floating point numbers](https://en.wikipedia.org/wiki/IEEE_754).
|
||||
|
||||
Types are equivalent to types of C:
|
||||
The equivalent types in ClickHouse and in C are given below:
|
||||
|
||||
- `Float32` — `float`.
|
||||
- `Float64` — `double`.
|
||||
|
||||
Aliases:
|
||||
Float types in ClickHouse have the following aliases:
|
||||
|
||||
- `Float32` — `FLOAT`, `REAL`, `SINGLE`.
|
||||
- `Float64` — `DOUBLE`, `DOUBLE PRECISION`.
|
||||
|
@ -414,6 +414,8 @@ $ curl -v 'http://localhost:8123/predefined_query'
|
||||
|
||||
- `content_type` — используется со всеми типами, возвращает [content-type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type).
|
||||
|
||||
- `http_response_headers` — используется со всеми типами чтобы добавить кастомные хедеры в ответ. Может использоваться в том числе для задания хедера `Content-Type` вместо `content_type`.
|
||||
|
||||
- `response_content` — используется с типом`static`, содержимое ответа, отправленное клиенту, при использовании префикса ‘file://’ or ‘config://’, находит содержимое из файла или конфигурации, отправленного клиенту.
|
||||
|
||||
Далее приведены методы настройки для различных типов.
|
||||
@ -509,6 +511,33 @@ max_final_threads 2
|
||||
<type>static</type>
|
||||
<status>402</status>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<http_response_headers>
|
||||
<Content-Language>en</Content-Language>
|
||||
<X-My-Custom-Header>43</X-My-Custom-Header>
|
||||
</http_response_headers>
|
||||
<response_content>Say Hi!</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
<defaults/>
|
||||
</http_handlers>
|
||||
```
|
||||
|
||||
`http_response_headers` так же может использоваться для определения `Content-Type` вместо `content_type`.
|
||||
|
||||
``` xml
|
||||
<http_handlers>
|
||||
<rule>
|
||||
<methods>GET</methods>
|
||||
<headers><XXX>xxx</XXX></headers>
|
||||
<url>/hi</url>
|
||||
<handler>
|
||||
<type>static</type>
|
||||
<status>402</status>
|
||||
<http_response_headers>
|
||||
<Content-Type>text/html; charset=UTF-8</Content-Type>
|
||||
<Content-Language>en</Content-Language>
|
||||
<X-My-Custom-Header>43</X-My-Custom-Header>
|
||||
</http_response_headers>
|
||||
<response_content>Say Hi!</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
@ -589,6 +618,9 @@ $ curl -v -H 'XXX:xxx' 'http://localhost:8123/get_config_static_handler'
|
||||
<handler>
|
||||
<type>static</type>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<http_response_headers>
|
||||
<ETag>737060cd8c284d8af7ad3082f209582d</ETag>
|
||||
</http_response_headers>
|
||||
<response_content>file:///absolute_path_file.html</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
@ -599,6 +631,9 @@ $ curl -v -H 'XXX:xxx' 'http://localhost:8123/get_config_static_handler'
|
||||
<handler>
|
||||
<type>static</type>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<http_response_headers>
|
||||
<ETag>737060cd8c284d8af7ad3082f209582d</ETag>
|
||||
</http_response_headers>
|
||||
<response_content>file://./relative_path_file.html</response_content>
|
||||
</handler>
|
||||
</rule>
|
||||
|
@ -31,6 +31,7 @@ namespace DB
|
||||
{
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int AUTHENTICATION_FAILED;
|
||||
extern const int SUPPORT_IS_DISABLED;
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int LOGICAL_ERROR;
|
||||
@ -90,8 +91,10 @@ bool AuthenticationData::Util::checkPasswordBcrypt(std::string_view password [[m
|
||||
{
|
||||
#if USE_BCRYPT
|
||||
int ret = bcrypt_checkpw(password.data(), reinterpret_cast<const char *>(password_bcrypt.data()));
|
||||
/// Before 24.6 we didn't validate hashes on creation, so it could be that the stored hash is invalid
|
||||
/// and it could not be decoded by the library
|
||||
if (ret == -1)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "BCrypt library failed: bcrypt_checkpw returned {}", ret);
|
||||
throw Exception(ErrorCodes::AUTHENTICATION_FAILED, "Internal failure decoding Bcrypt hash");
|
||||
return (ret == 0);
|
||||
#else
|
||||
throw Exception(
|
||||
@ -230,6 +233,17 @@ void AuthenticationData::setPasswordHashBinary(const Digest & hash)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Password hash for the 'BCRYPT_PASSWORD' authentication type has length {} "
|
||||
"but must be 59 or 60 bytes.", hash.size());
|
||||
|
||||
auto resized = hash;
|
||||
resized.resize(64);
|
||||
|
||||
#if USE_BCRYPT
|
||||
/// Verify that it is a valid hash
|
||||
int ret = bcrypt_checkpw("", reinterpret_cast<const char *>(resized.data()));
|
||||
if (ret == -1)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Could not decode the provided hash with 'bcrypt_hash'");
|
||||
#endif
|
||||
|
||||
password_hash = hash;
|
||||
password_hash.resize(64);
|
||||
return;
|
||||
|
@ -415,6 +415,15 @@ Value saveAllArenasMetric(AsynchronousMetricValues & values,
|
||||
fmt::format("jemalloc.arenas.all.{}", metric_name));
|
||||
}
|
||||
|
||||
template<typename Value>
|
||||
Value saveJemallocProf(AsynchronousMetricValues & values,
|
||||
const std::string & metric_name)
|
||||
{
|
||||
return saveJemallocMetricImpl<Value>(values,
|
||||
fmt::format("prof.{}", metric_name),
|
||||
fmt::format("jemalloc.prof.{}", metric_name));
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -607,6 +616,7 @@ void AsynchronousMetrics::update(TimePoint update_time, bool force_update)
|
||||
saveJemallocMetric<size_t>(new_values, "background_thread.num_threads");
|
||||
saveJemallocMetric<uint64_t>(new_values, "background_thread.num_runs");
|
||||
saveJemallocMetric<uint64_t>(new_values, "background_thread.run_intervals");
|
||||
saveJemallocProf<size_t>(new_values, "active");
|
||||
saveAllArenasMetric<size_t>(new_values, "pactive");
|
||||
[[maybe_unused]] size_t je_malloc_pdirty = saveAllArenasMetric<size_t>(new_values, "pdirty");
|
||||
[[maybe_unused]] size_t je_malloc_pmuzzy = saveAllArenasMetric<size_t>(new_values, "pmuzzy");
|
||||
|
@ -85,9 +85,18 @@ StatusFile::StatusFile(std::string path_, FillFunction fill_)
|
||||
|
||||
/// Write information about current server instance to the file.
|
||||
WriteBufferFromFileDescriptor out(fd, 1024);
|
||||
fill(out);
|
||||
/// Finalize here to avoid throwing exceptions in destructor.
|
||||
out.finalize();
|
||||
try
|
||||
{
|
||||
fill(out);
|
||||
/// Finalize here to avoid throwing exceptions in destructor.
|
||||
out.finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// Finalize in case of exception to avoid throwing exceptions in destructor
|
||||
out.finalize();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
@ -146,7 +146,7 @@ void SerializationVariantElement::deserializeBinaryBulkWithMultipleStreams(
|
||||
}
|
||||
|
||||
/// If we started to read a new column, reinitialize variant column in deserialization state.
|
||||
if (!variant_element_state->variant || result_column->empty())
|
||||
if (!variant_element_state->variant || mutable_column->empty())
|
||||
{
|
||||
variant_element_state->variant = mutable_column->cloneEmpty();
|
||||
|
||||
|
@ -77,7 +77,15 @@ WriteBufferFromFile::~WriteBufferFromFile()
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
finalize();
|
||||
try
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
int err = ::close(fd);
|
||||
/// Everything except for EBADF should be ignored in dtor, since all of
|
||||
/// others (EINTR/EIO/ENOSPC/EDQUOT) could be possible during writing to
|
||||
|
@ -105,7 +105,14 @@ WriteBufferFromFileDescriptor::WriteBufferFromFileDescriptor(
|
||||
|
||||
WriteBufferFromFileDescriptor::~WriteBufferFromFileDescriptor()
|
||||
{
|
||||
finalize();
|
||||
try
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteBufferFromFileDescriptor::finalizeImpl()
|
||||
|
@ -450,8 +450,8 @@ ASTPtr InterpreterCreateQuery::formatColumns(const ColumnsDescription & columns)
|
||||
|
||||
if (!column.statistics.empty())
|
||||
{
|
||||
column_declaration->stat_type = column.statistics.getAST();
|
||||
column_declaration->children.push_back(column_declaration->stat_type);
|
||||
column_declaration->statistics_desc = column.statistics.getAST();
|
||||
column_declaration->children.push_back(column_declaration->statistics_desc);
|
||||
}
|
||||
|
||||
if (column.ttl)
|
||||
@ -676,12 +676,11 @@ ColumnsDescription InterpreterCreateQuery::getColumnsDescription(
|
||||
}
|
||||
|
||||
column.statistics.column_name = column.name; /// We assign column name here for better exception error message.
|
||||
if (col_decl.stat_type)
|
||||
if (col_decl.statistics_desc)
|
||||
{
|
||||
if (!skip_checks && !context_->getSettingsRef().allow_experimental_statistics)
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "Create table with statistics is now disabled. Turn on allow_experimental_statistics");
|
||||
column.statistics = ColumnStatisticsDescription::fromColumnDeclaration(col_decl);
|
||||
column.statistics.data_type = column.type;
|
||||
column.statistics = ColumnStatisticsDescription::fromColumnDeclaration(col_decl, column.type);
|
||||
}
|
||||
|
||||
if (col_decl.ttl)
|
||||
@ -1089,11 +1088,14 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
String current_database = getContext()->getCurrentDatabase();
|
||||
auto database_name = create.database ? create.getDatabase() : current_database;
|
||||
|
||||
bool is_secondary_query = getContext()->getZooKeeperMetadataTransaction() && !getContext()->getZooKeeperMetadataTransaction()->isInitialQuery();
|
||||
auto mode = getLoadingStrictnessLevel(create.attach, /*force_attach*/ false, /*has_force_restore_data_flag*/ false, is_secondary_query || is_restore_from_backup);
|
||||
|
||||
if (!create.sql_security && create.supportSQLSecurity() && !getContext()->getServerSettings().ignore_empty_sql_security_in_create_view_query)
|
||||
create.sql_security = std::make_shared<ASTSQLSecurity>();
|
||||
|
||||
if (create.sql_security)
|
||||
processSQLSecurityOption(getContext(), create.sql_security->as<ASTSQLSecurity &>(), create.attach, create.is_materialized_view);
|
||||
processSQLSecurityOption(getContext(), create.sql_security->as<ASTSQLSecurity &>(), create.is_materialized_view, /* skip_check_permissions= */ mode >= LoadingStrictnessLevel::SECONDARY_CREATE);
|
||||
|
||||
DDLGuardPtr ddl_guard;
|
||||
|
||||
@ -1220,9 +1222,6 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
if (!UserDefinedSQLFunctionFactory::instance().empty())
|
||||
UserDefinedSQLFunctionVisitor::visit(query_ptr);
|
||||
|
||||
bool is_secondary_query = getContext()->getZooKeeperMetadataTransaction() && !getContext()->getZooKeeperMetadataTransaction()->isInitialQuery();
|
||||
auto mode = getLoadingStrictnessLevel(create.attach, /*force_attach*/ false, /*has_force_restore_data_flag*/ false, is_secondary_query || is_restore_from_backup);
|
||||
|
||||
/// Set and retrieve list of columns, indices and constraints. Set table engine if needed. Rewrite query in canonical way.
|
||||
TableProperties properties = getTablePropertiesAndNormalizeCreateQuery(create, mode);
|
||||
|
||||
@ -1887,7 +1886,7 @@ void InterpreterCreateQuery::addColumnsDescriptionToCreateQueryIfNecessary(ASTCr
|
||||
}
|
||||
}
|
||||
|
||||
void InterpreterCreateQuery::processSQLSecurityOption(ContextPtr context_, ASTSQLSecurity & sql_security, bool is_attach, bool is_materialized_view)
|
||||
void InterpreterCreateQuery::processSQLSecurityOption(ContextPtr context_, ASTSQLSecurity & sql_security, bool is_materialized_view, bool skip_check_permissions)
|
||||
{
|
||||
/// If no SQL security is specified, apply default from default_*_view_sql_security setting.
|
||||
if (!sql_security.type)
|
||||
@ -1928,7 +1927,7 @@ void InterpreterCreateQuery::processSQLSecurityOption(ContextPtr context_, ASTSQ
|
||||
}
|
||||
|
||||
/// Checks the permissions for the specified definer user.
|
||||
if (sql_security.definer && !sql_security.is_definer_current_user && !is_attach)
|
||||
if (sql_security.definer && !sql_security.is_definer_current_user && !skip_check_permissions)
|
||||
{
|
||||
const auto definer_name = sql_security.definer->toString();
|
||||
|
||||
@ -1938,7 +1937,7 @@ void InterpreterCreateQuery::processSQLSecurityOption(ContextPtr context_, ASTSQ
|
||||
context_->checkAccess(AccessType::SET_DEFINER, definer_name);
|
||||
}
|
||||
|
||||
if (sql_security.type == SQLSecurityType::NONE && !is_attach)
|
||||
if (sql_security.type == SQLSecurityType::NONE && !skip_check_permissions)
|
||||
context_->checkAccess(AccessType::ALLOW_SQL_SECURITY_NONE);
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ public:
|
||||
void extendQueryLogElemImpl(QueryLogElement & elem, const ASTPtr & ast, ContextPtr) const override;
|
||||
|
||||
/// Check access right, validate definer statement and replace `CURRENT USER` with actual name.
|
||||
static void processSQLSecurityOption(ContextPtr context_, ASTSQLSecurity & sql_security, bool is_attach = false, bool is_materialized_view = false);
|
||||
static void processSQLSecurityOption(ContextPtr context_, ASTSQLSecurity & sql_security, bool is_materialized_view = false, bool skip_check_permissions = false);
|
||||
|
||||
private:
|
||||
struct TableProperties
|
||||
|
@ -39,10 +39,10 @@ ASTPtr ASTColumnDeclaration::clone() const
|
||||
res->children.push_back(res->codec);
|
||||
}
|
||||
|
||||
if (stat_type)
|
||||
if (statistics_desc)
|
||||
{
|
||||
res->stat_type = stat_type->clone();
|
||||
res->children.push_back(res->stat_type);
|
||||
res->statistics_desc = statistics_desc->clone();
|
||||
res->children.push_back(res->statistics_desc);
|
||||
}
|
||||
|
||||
if (ttl)
|
||||
@ -111,10 +111,10 @@ void ASTColumnDeclaration::formatImpl(const FormatSettings & format_settings, Fo
|
||||
codec->formatImpl(format_settings, state, frame);
|
||||
}
|
||||
|
||||
if (stat_type)
|
||||
if (statistics_desc)
|
||||
{
|
||||
format_settings.ostr << ' ';
|
||||
stat_type->formatImpl(format_settings, state, frame);
|
||||
statistics_desc->formatImpl(format_settings, state, frame);
|
||||
}
|
||||
|
||||
if (ttl)
|
||||
|
@ -19,7 +19,7 @@ public:
|
||||
bool ephemeral_default = false;
|
||||
ASTPtr comment;
|
||||
ASTPtr codec;
|
||||
ASTPtr stat_type;
|
||||
ASTPtr statistics_desc;
|
||||
ASTPtr ttl;
|
||||
ASTPtr collation;
|
||||
ASTPtr settings;
|
||||
|
@ -193,7 +193,7 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
ASTPtr default_expression;
|
||||
ASTPtr comment_expression;
|
||||
ASTPtr codec_expression;
|
||||
ASTPtr stat_type_expression;
|
||||
ASTPtr statistics_desc_expression;
|
||||
ASTPtr ttl_expression;
|
||||
ASTPtr collation_expression;
|
||||
ASTPtr settings;
|
||||
@ -325,7 +325,7 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
|
||||
if (s_stat.ignore(pos, expected))
|
||||
{
|
||||
if (!stat_type_parser.parse(pos, stat_type_expression, expected))
|
||||
if (!stat_type_parser.parse(pos, statistics_desc_expression, expected))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -398,10 +398,10 @@ bool IParserColumnDeclaration<NameParser>::parseImpl(Pos & pos, ASTPtr & node, E
|
||||
column_declaration->children.push_back(std::move(settings));
|
||||
}
|
||||
|
||||
if (stat_type_expression)
|
||||
if (statistics_desc_expression)
|
||||
{
|
||||
column_declaration->stat_type = stat_type_expression;
|
||||
column_declaration->children.push_back(std::move(stat_type_expression));
|
||||
column_declaration->statistics_desc = statistics_desc_expression;
|
||||
column_declaration->children.push_back(std::move(statistics_desc_expression));
|
||||
}
|
||||
|
||||
if (ttl_expression)
|
||||
|
@ -30,7 +30,6 @@
|
||||
#include <Common/scope_guard_safe.h>
|
||||
#include <Common/setThreadName.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Common/re2.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <Processors/Formats/IOutputFormat.h>
|
||||
#include <Formats/FormatFactory.h>
|
||||
@ -44,6 +43,7 @@
|
||||
#include <Poco/Base64Decoder.h>
|
||||
#include <Poco/Base64Encoder.h>
|
||||
#include <Poco/Net/HTTPBasicCredentials.h>
|
||||
#include <Poco/Net/HTTPMessage.h>
|
||||
#include <Poco/Net/HTTPStream.h>
|
||||
#include <Poco/MemoryStream.h>
|
||||
#include <Poco/StreamCopier.h>
|
||||
@ -53,7 +53,10 @@
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#if USE_SSL
|
||||
#include <Poco/Net/X509Certificate.h>
|
||||
@ -338,11 +341,11 @@ void HTTPHandler::pushDelayedResults(Output & used_output)
|
||||
}
|
||||
|
||||
|
||||
HTTPHandler::HTTPHandler(IServer & server_, const std::string & name, const std::optional<String> & content_type_override_)
|
||||
HTTPHandler::HTTPHandler(IServer & server_, const std::string & name, const HTTPResponseHeaderSetup & http_response_headers_override_)
|
||||
: server(server_)
|
||||
, log(getLogger(name))
|
||||
, default_settings(server.context()->getSettingsRef())
|
||||
, content_type_override(content_type_override_)
|
||||
, http_response_headers_override(http_response_headers_override_)
|
||||
{
|
||||
server_display_name = server.config().getString("display_name", getFQDNOrHostName());
|
||||
}
|
||||
@ -670,8 +673,7 @@ void HTTPHandler::processQuery(
|
||||
{
|
||||
auto tmp_data = std::make_shared<TemporaryDataOnDisk>(server.context()->getTempDataOnDisk());
|
||||
|
||||
auto create_tmp_disk_buffer = [tmp_data] (const WriteBufferPtr &) -> WriteBufferPtr
|
||||
{
|
||||
auto create_tmp_disk_buffer = [tmp_data] (const WriteBufferPtr &) -> WriteBufferPtr {
|
||||
return tmp_data->createRawStream();
|
||||
};
|
||||
|
||||
@ -893,13 +895,14 @@ void HTTPHandler::processQuery(
|
||||
customizeContext(request, context, *in_post_maybe_compressed);
|
||||
in = has_external_data ? std::move(in_param) : std::make_unique<ConcatReadBuffer>(*in_param, *in_post_maybe_compressed);
|
||||
|
||||
applyHTTPResponseHeaders(response, http_response_headers_override);
|
||||
|
||||
auto set_query_result = [&response, this] (const QueryResultDetails & details)
|
||||
{
|
||||
response.add("X-ClickHouse-Query-Id", details.query_id);
|
||||
|
||||
if (content_type_override)
|
||||
response.setContentType(*content_type_override);
|
||||
else if (details.content_type)
|
||||
if (!(http_response_headers_override && http_response_headers_override->contains(Poco::Net::HTTPMessage::CONTENT_TYPE))
|
||||
&& details.content_type)
|
||||
response.setContentType(*details.content_type);
|
||||
|
||||
if (details.format)
|
||||
@ -1185,8 +1188,9 @@ void HTTPHandler::handleRequest(HTTPServerRequest & request, HTTPServerResponse
|
||||
used_output.finalize();
|
||||
}
|
||||
|
||||
DynamicQueryHandler::DynamicQueryHandler(IServer & server_, const std::string & param_name_, const std::optional<String>& content_type_override_)
|
||||
: HTTPHandler(server_, "DynamicQueryHandler", content_type_override_), param_name(param_name_)
|
||||
DynamicQueryHandler::DynamicQueryHandler(
|
||||
IServer & server_, const std::string & param_name_, const HTTPResponseHeaderSetup & http_response_headers_override_)
|
||||
: HTTPHandler(server_, "DynamicQueryHandler", http_response_headers_override_), param_name(param_name_)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1247,8 +1251,8 @@ PredefinedQueryHandler::PredefinedQueryHandler(
|
||||
const std::string & predefined_query_,
|
||||
const CompiledRegexPtr & url_regex_,
|
||||
const std::unordered_map<String, CompiledRegexPtr> & header_name_with_regex_,
|
||||
const std::optional<String> & content_type_override_)
|
||||
: HTTPHandler(server_, "PredefinedQueryHandler", content_type_override_)
|
||||
const HTTPResponseHeaderSetup & http_response_headers_override_)
|
||||
: HTTPHandler(server_, "PredefinedQueryHandler", http_response_headers_override_)
|
||||
, receive_params(receive_params_)
|
||||
, predefined_query(predefined_query_)
|
||||
, url_regex(url_regex_)
|
||||
@ -1340,14 +1344,10 @@ HTTPRequestHandlerFactoryPtr createDynamicHandlerFactory(IServer & server,
|
||||
{
|
||||
auto query_param_name = config.getString(config_prefix + ".handler.query_param_name", "query");
|
||||
|
||||
std::optional<String> content_type_override;
|
||||
if (config.has(config_prefix + ".handler.content_type"))
|
||||
content_type_override = config.getString(config_prefix + ".handler.content_type");
|
||||
HTTPResponseHeaderSetup http_response_headers_override = parseHTTPResponseHeaders(config, config_prefix);
|
||||
|
||||
auto creator = [&server, query_param_name, content_type_override] () -> std::unique_ptr<DynamicQueryHandler>
|
||||
{
|
||||
return std::make_unique<DynamicQueryHandler>(server, query_param_name, content_type_override);
|
||||
};
|
||||
auto creator = [&server, query_param_name, http_response_headers_override]() -> std::unique_ptr<DynamicQueryHandler>
|
||||
{ return std::make_unique<DynamicQueryHandler>(server, query_param_name, http_response_headers_override); };
|
||||
|
||||
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(creator));
|
||||
factory->addFiltersFromConfig(config, config_prefix);
|
||||
@ -1402,9 +1402,7 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
|
||||
headers_name_with_regex.emplace(std::make_pair(header_name, regex));
|
||||
}
|
||||
|
||||
std::optional<String> content_type_override;
|
||||
if (config.has(config_prefix + ".handler.content_type"))
|
||||
content_type_override = config.getString(config_prefix + ".handler.content_type");
|
||||
HTTPResponseHeaderSetup http_response_headers_override = parseHTTPResponseHeaders(config, config_prefix);
|
||||
|
||||
std::shared_ptr<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>> factory;
|
||||
|
||||
@ -1424,12 +1422,12 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
|
||||
predefined_query,
|
||||
regex,
|
||||
headers_name_with_regex,
|
||||
content_type_override]
|
||||
http_response_headers_override]
|
||||
-> std::unique_ptr<PredefinedQueryHandler>
|
||||
{
|
||||
return std::make_unique<PredefinedQueryHandler>(
|
||||
server, analyze_receive_params, predefined_query, regex,
|
||||
headers_name_with_regex, content_type_override);
|
||||
headers_name_with_regex, http_response_headers_override);
|
||||
};
|
||||
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
|
||||
factory->addFiltersFromConfig(config, config_prefix);
|
||||
@ -1442,12 +1440,12 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
|
||||
analyze_receive_params,
|
||||
predefined_query,
|
||||
headers_name_with_regex,
|
||||
content_type_override]
|
||||
http_response_headers_override]
|
||||
-> std::unique_ptr<PredefinedQueryHandler>
|
||||
{
|
||||
return std::make_unique<PredefinedQueryHandler>(
|
||||
server, analyze_receive_params, predefined_query, CompiledRegexPtr{},
|
||||
headers_name_with_regex, content_type_override);
|
||||
headers_name_with_regex, http_response_headers_override);
|
||||
};
|
||||
|
||||
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
|
||||
|
@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <Core/Names.h>
|
||||
#include <Server/HTTP/HTMLForm.h>
|
||||
#include <Server/HTTP/HTTPRequestHandler.h>
|
||||
@ -10,6 +13,8 @@
|
||||
#include <Compression/CompressedWriteBuffer.h>
|
||||
#include <Common/re2.h>
|
||||
|
||||
#include "HTTPResponseHeaderWriter.h"
|
||||
|
||||
namespace CurrentMetrics
|
||||
{
|
||||
extern const Metric HTTPConnection;
|
||||
@ -31,7 +36,7 @@ using CompiledRegexPtr = std::shared_ptr<const re2::RE2>;
|
||||
class HTTPHandler : public HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
HTTPHandler(IServer & server_, const std::string & name, const std::optional<String> & content_type_override_);
|
||||
HTTPHandler(IServer & server_, const std::string & name, const HTTPResponseHeaderSetup & http_response_headers_override_);
|
||||
~HTTPHandler() override;
|
||||
|
||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||
@ -113,8 +118,8 @@ private:
|
||||
/// See settings http_max_fields, http_max_field_name_size, http_max_field_value_size in HTMLForm.
|
||||
const Settings & default_settings;
|
||||
|
||||
/// Overrides Content-Type provided by the format of the response.
|
||||
std::optional<String> content_type_override;
|
||||
/// Overrides for response headers.
|
||||
HTTPResponseHeaderSetup http_response_headers_override;
|
||||
|
||||
// session is reset at the end of each request/response.
|
||||
std::unique_ptr<Session> session;
|
||||
@ -162,8 +167,12 @@ class DynamicQueryHandler : public HTTPHandler
|
||||
{
|
||||
private:
|
||||
std::string param_name;
|
||||
|
||||
public:
|
||||
explicit DynamicQueryHandler(IServer & server_, const std::string & param_name_ = "query", const std::optional<String>& content_type_override_ = std::nullopt);
|
||||
explicit DynamicQueryHandler(
|
||||
IServer & server_,
|
||||
const std::string & param_name_ = "query",
|
||||
const HTTPResponseHeaderSetup & http_response_headers_override_ = std::nullopt);
|
||||
|
||||
std::string getQuery(HTTPServerRequest & request, HTMLForm & params, ContextMutablePtr context) override;
|
||||
|
||||
@ -177,11 +186,15 @@ private:
|
||||
std::string predefined_query;
|
||||
CompiledRegexPtr url_regex;
|
||||
std::unordered_map<String, CompiledRegexPtr> header_name_with_capture_regex;
|
||||
|
||||
public:
|
||||
PredefinedQueryHandler(
|
||||
IServer & server_, const NameSet & receive_params_, const std::string & predefined_query_
|
||||
, const CompiledRegexPtr & url_regex_, const std::unordered_map<String, CompiledRegexPtr> & header_name_with_regex_
|
||||
, const std::optional<std::string> & content_type_override_);
|
||||
IServer & server_,
|
||||
const NameSet & receive_params_,
|
||||
const std::string & predefined_query_,
|
||||
const CompiledRegexPtr & url_regex_,
|
||||
const std::unordered_map<String, CompiledRegexPtr> & header_name_with_regex_,
|
||||
const HTTPResponseHeaderSetup & http_response_headers_override_ = std::nullopt);
|
||||
|
||||
void customizeContext(HTTPServerRequest & request, ContextMutablePtr context, ReadBuffer & body) override;
|
||||
|
||||
|
@ -74,7 +74,8 @@ static auto createPingHandlerFactory(IServer & server)
|
||||
auto creator = [&server]() -> std::unique_ptr<StaticRequestHandler>
|
||||
{
|
||||
constexpr auto ping_response_expression = "Ok.\n";
|
||||
return std::make_unique<StaticRequestHandler>(server, ping_response_expression);
|
||||
return std::make_unique<StaticRequestHandler>(
|
||||
server, ping_response_expression, parseHTTPResponseHeaders("text/html; charset=UTF-8"));
|
||||
};
|
||||
return std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(creator));
|
||||
}
|
||||
@ -214,7 +215,8 @@ void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IS
|
||||
auto root_creator = [&server]() -> std::unique_ptr<StaticRequestHandler>
|
||||
{
|
||||
constexpr auto root_response_expression = "config://http_server_default_response";
|
||||
return std::make_unique<StaticRequestHandler>(server, root_response_expression);
|
||||
return std::make_unique<StaticRequestHandler>(
|
||||
server, root_response_expression, parseHTTPResponseHeaders("text/html; charset=UTF-8"));
|
||||
};
|
||||
auto root_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(root_creator));
|
||||
root_handler->attachStrictPath("/");
|
||||
|
69
src/Server/HTTPResponseHeaderWriter.cpp
Normal file
69
src/Server/HTTPResponseHeaderWriter.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "HTTPResponseHeaderWriter.h"
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <Poco/Net/HTTPMessage.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
std::unordered_map<String, String>
|
||||
baseParseHTTPResponseHeaders(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix)
|
||||
{
|
||||
std::unordered_map<String, String> http_response_headers_override;
|
||||
String http_response_headers_key = config_prefix + ".handler.http_response_headers";
|
||||
String http_response_headers_key_prefix = http_response_headers_key + ".";
|
||||
if (config.has(http_response_headers_key))
|
||||
{
|
||||
Poco::Util::AbstractConfiguration::Keys keys;
|
||||
config.keys(http_response_headers_key, keys);
|
||||
for (const auto & key : keys)
|
||||
{
|
||||
http_response_headers_override[key] = config.getString(http_response_headers_key_prefix + key);
|
||||
}
|
||||
}
|
||||
if (config.has(config_prefix + ".handler.content_type"))
|
||||
http_response_headers_override[Poco::Net::HTTPMessage::CONTENT_TYPE] = config.getString(config_prefix + ".handler.content_type");
|
||||
|
||||
return http_response_headers_override;
|
||||
}
|
||||
|
||||
HTTPResponseHeaderSetup parseHTTPResponseHeaders(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix)
|
||||
{
|
||||
std::unordered_map<String, String> http_response_headers_override = baseParseHTTPResponseHeaders(config, config_prefix);
|
||||
|
||||
if (http_response_headers_override.empty())
|
||||
return {};
|
||||
|
||||
return std::move(http_response_headers_override);
|
||||
}
|
||||
|
||||
std::unordered_map<String, String> parseHTTPResponseHeaders(
|
||||
const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, const std::string & default_content_type)
|
||||
{
|
||||
std::unordered_map<String, String> http_response_headers_override = baseParseHTTPResponseHeaders(config, config_prefix);
|
||||
|
||||
if (!http_response_headers_override.contains(Poco::Net::HTTPMessage::CONTENT_TYPE))
|
||||
http_response_headers_override[Poco::Net::HTTPMessage::CONTENT_TYPE] = default_content_type;
|
||||
|
||||
return http_response_headers_override;
|
||||
}
|
||||
|
||||
std::unordered_map<String, String> parseHTTPResponseHeaders(const std::string & default_content_type)
|
||||
{
|
||||
return {{{Poco::Net::HTTPMessage::CONTENT_TYPE, default_content_type}}};
|
||||
}
|
||||
|
||||
void applyHTTPResponseHeaders(Poco::Net::HTTPResponse & response, const HTTPResponseHeaderSetup & setup)
|
||||
{
|
||||
if (setup)
|
||||
for (const auto & [header_name, header_value] : *setup)
|
||||
response.set(header_name, header_value);
|
||||
}
|
||||
|
||||
void applyHTTPResponseHeaders(Poco::Net::HTTPResponse & response, const std::unordered_map<String, String> & setup)
|
||||
{
|
||||
for (const auto & [header_name, header_value] : setup)
|
||||
response.set(header_name, header_value);
|
||||
}
|
||||
|
||||
}
|
25
src/Server/HTTPResponseHeaderWriter.h
Normal file
25
src/Server/HTTPResponseHeaderWriter.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <base/types.h>
|
||||
#include <Poco/Net/HTTPResponse.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
using HTTPResponseHeaderSetup = std::optional<std::unordered_map<String, String>>;
|
||||
|
||||
HTTPResponseHeaderSetup parseHTTPResponseHeaders(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix);
|
||||
|
||||
std::unordered_map<String, String> parseHTTPResponseHeaders(
|
||||
const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix, const std::string & default_content_type);
|
||||
|
||||
std::unordered_map<String, String> parseHTTPResponseHeaders(const std::string & default_content_type);
|
||||
|
||||
void applyHTTPResponseHeaders(Poco::Net::HTTPResponse & response, const HTTPResponseHeaderSetup & setup);
|
||||
|
||||
void applyHTTPResponseHeaders(Poco::Net::HTTPResponse & response, const std::unordered_map<String, String> & setup);
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#include "IServer.h"
|
||||
|
||||
#include "HTTPHandlerFactory.h"
|
||||
#include "HTTPHandlerRequestFilter.h"
|
||||
#include "HTTPResponseHeaderWriter.h"
|
||||
|
||||
#include <IO/HTTPCommon.h>
|
||||
#include <IO/ReadBufferFromFile.h>
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#include <Common/Exception.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
@ -94,7 +95,7 @@ void StaticRequestHandler::handleRequest(HTTPServerRequest & request, HTTPServer
|
||||
|
||||
try
|
||||
{
|
||||
response.setContentType(content_type);
|
||||
applyHTTPResponseHeaders(response, http_response_headers_override);
|
||||
|
||||
if (request.getVersion() == Poco::Net::HTTPServerRequest::HTTP_1_1)
|
||||
response.setChunkedTransferEncoding(true);
|
||||
@ -155,8 +156,9 @@ void StaticRequestHandler::writeResponse(WriteBuffer & out)
|
||||
writeString(response_expression, out);
|
||||
}
|
||||
|
||||
StaticRequestHandler::StaticRequestHandler(IServer & server_, const String & expression, int status_, const String & content_type_)
|
||||
: server(server_), status(status_), content_type(content_type_), response_expression(expression)
|
||||
StaticRequestHandler::StaticRequestHandler(
|
||||
IServer & server_, const String & expression, const std::unordered_map<String, String> & http_response_headers_override_, int status_)
|
||||
: server(server_), status(status_), http_response_headers_override(http_response_headers_override_), response_expression(expression)
|
||||
{
|
||||
}
|
||||
|
||||
@ -166,12 +168,12 @@ HTTPRequestHandlerFactoryPtr createStaticHandlerFactory(IServer & server,
|
||||
{
|
||||
int status = config.getInt(config_prefix + ".handler.status", 200);
|
||||
std::string response_content = config.getRawString(config_prefix + ".handler.response_content", "Ok.\n");
|
||||
std::string response_content_type = config.getString(config_prefix + ".handler.content_type", "text/plain; charset=UTF-8");
|
||||
|
||||
auto creator = [&server, response_content, status, response_content_type]() -> std::unique_ptr<StaticRequestHandler>
|
||||
{
|
||||
return std::make_unique<StaticRequestHandler>(server, response_content, status, response_content_type);
|
||||
};
|
||||
std::unordered_map<String, String> http_response_headers_override
|
||||
= parseHTTPResponseHeaders(config, config_prefix, "text/plain; charset=UTF-8");
|
||||
|
||||
auto creator = [&server, http_response_headers_override, response_content, status]() -> std::unique_ptr<StaticRequestHandler>
|
||||
{ return std::make_unique<StaticRequestHandler>(server, response_content, http_response_headers_override, status); };
|
||||
|
||||
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(creator));
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <Server/HTTP/HTTPRequestHandler.h>
|
||||
#include <base/types.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -17,15 +17,16 @@ private:
|
||||
IServer & server;
|
||||
|
||||
int status;
|
||||
String content_type;
|
||||
/// Overrides for response headers.
|
||||
std::unordered_map<String, String> http_response_headers_override;
|
||||
String response_expression;
|
||||
|
||||
public:
|
||||
StaticRequestHandler(
|
||||
IServer & server,
|
||||
const String & expression,
|
||||
int status_ = 200,
|
||||
const String & content_type_ = "text/html; charset=UTF-8");
|
||||
const std::unordered_map<String, String> & http_response_headers_override_,
|
||||
int status_ = 200);
|
||||
|
||||
void writeResponse(WriteBuffer & out);
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||
#include <Parsers/ASTSetQuery.h>
|
||||
#include <IO/WriteBuffer.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <IO/ReadBuffer.h>
|
||||
@ -24,7 +25,6 @@
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include "Parsers/ASTSetQuery.h"
|
||||
#include <Core/Defines.h>
|
||||
#include <Compression/CompressionFactory.h>
|
||||
#include <Interpreters/ExpressionAnalyzer.h>
|
||||
@ -207,6 +207,13 @@ void ColumnDescription::readText(ReadBuffer & buf)
|
||||
|
||||
if (col_ast->settings)
|
||||
settings = col_ast->settings->as<ASTSetQuery &>().changes;
|
||||
|
||||
if (col_ast->statistics_desc)
|
||||
{
|
||||
statistics = ColumnStatisticsDescription::fromColumnDeclaration(*col_ast, type);
|
||||
/// every column has name `x` here, so we have to set the name manually.
|
||||
statistics.column_name = name;
|
||||
}
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::CANNOT_PARSE_TEXT, "Cannot parse column description");
|
||||
|
@ -132,11 +132,11 @@ void WriteBufferFromHDFS::sync()
|
||||
}
|
||||
|
||||
|
||||
void WriteBufferFromHDFS::finalizeImpl()
|
||||
WriteBufferFromHDFS::~WriteBufferFromHDFS()
|
||||
{
|
||||
try
|
||||
{
|
||||
next();
|
||||
finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -144,11 +144,5 @@ void WriteBufferFromHDFS::finalizeImpl()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WriteBufferFromHDFS::~WriteBufferFromHDFS()
|
||||
{
|
||||
finalize();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -38,8 +38,6 @@ public:
|
||||
std::string getFileName() const override { return filename; }
|
||||
|
||||
private:
|
||||
void finalizeImpl() override;
|
||||
|
||||
struct WriteBufferFromHDFSImpl;
|
||||
std::unique_ptr<WriteBufferFromHDFSImpl> impl;
|
||||
const std::string filename;
|
||||
|
@ -83,7 +83,6 @@ void StorageObjectStorageSink::finalize()
|
||||
{
|
||||
writer->finalize();
|
||||
writer->flush();
|
||||
write_buf->finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -91,6 +90,8 @@ void StorageObjectStorageSink::finalize()
|
||||
release();
|
||||
throw;
|
||||
}
|
||||
|
||||
write_buf->finalize();
|
||||
}
|
||||
|
||||
void StorageObjectStorageSink::release()
|
||||
|
@ -169,9 +169,9 @@ std::vector<ColumnStatisticsDescription> ColumnStatisticsDescription::fromAST(co
|
||||
return result;
|
||||
}
|
||||
|
||||
ColumnStatisticsDescription ColumnStatisticsDescription::fromColumnDeclaration(const ASTColumnDeclaration & column)
|
||||
ColumnStatisticsDescription ColumnStatisticsDescription::fromColumnDeclaration(const ASTColumnDeclaration & column, DataTypePtr data_type)
|
||||
{
|
||||
const auto & stat_type_list_ast = column.stat_type->as<ASTFunction &>().arguments;
|
||||
const auto & stat_type_list_ast = column.statistics_desc->as<ASTFunction &>().arguments;
|
||||
if (stat_type_list_ast->children.empty())
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "We expect at least one statistics type for column {}", queryToString(column));
|
||||
ColumnStatisticsDescription stats;
|
||||
@ -185,7 +185,7 @@ ColumnStatisticsDescription ColumnStatisticsDescription::fromColumnDeclaration(c
|
||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "Column {} already contains statistics type {}", stats.column_name, stat_type);
|
||||
stats.types_to_desc.emplace(stat.type, std::move(stat));
|
||||
}
|
||||
|
||||
stats.data_type = data_type;
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ struct ColumnStatisticsDescription
|
||||
ASTPtr getAST() const;
|
||||
|
||||
static std::vector<ColumnStatisticsDescription> fromAST(const ASTPtr & definition_ast, const ColumnsDescription & columns);
|
||||
static ColumnStatisticsDescription fromColumnDeclaration(const ASTColumnDeclaration & column);
|
||||
static ColumnStatisticsDescription fromColumnDeclaration(const ASTColumnDeclaration & column, DataTypePtr data_type);
|
||||
|
||||
using StatisticsTypeDescMap = std::map<StatisticsType, SingleStatisticsDescription>;
|
||||
StatisticsTypeDescMap types_to_desc;
|
||||
|
@ -1823,7 +1823,6 @@ private:
|
||||
{
|
||||
writer->finalize();
|
||||
writer->flush();
|
||||
write_buf->finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -1831,12 +1830,14 @@ private:
|
||||
release();
|
||||
throw;
|
||||
}
|
||||
|
||||
write_buf->finalize();
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
writer.reset();
|
||||
write_buf->finalize();
|
||||
write_buf.reset();
|
||||
}
|
||||
|
||||
StorageMetadataPtr metadata_snapshot;
|
||||
|
@ -609,7 +609,6 @@ void StorageURLSink::finalize()
|
||||
{
|
||||
writer->finalize();
|
||||
writer->flush();
|
||||
write_buf->finalize();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -617,12 +616,14 @@ void StorageURLSink::finalize()
|
||||
release();
|
||||
throw;
|
||||
}
|
||||
|
||||
write_buf->finalize();
|
||||
}
|
||||
|
||||
void StorageURLSink::release()
|
||||
{
|
||||
writer.reset();
|
||||
write_buf->finalize();
|
||||
write_buf.reset();
|
||||
}
|
||||
|
||||
class PartitionedStorageURLSink : public PartitionedSink
|
||||
|
@ -0,0 +1,3 @@
|
||||
<clickhouse>
|
||||
<asynchronous_metrics_update_period_s>1</asynchronous_metrics_update_period_s>
|
||||
</clickhouse>
|
@ -0,0 +1,73 @@
|
||||
import time
|
||||
|
||||
import pytest
|
||||
from helpers.cluster import ClickHouseCluster
|
||||
|
||||
cluster = ClickHouseCluster(__file__)
|
||||
node1 = cluster.add_instance(
|
||||
"node1",
|
||||
main_configs=["configs/asynchronous_metrics_update_period_s.xml"],
|
||||
env_variables={"MALLOC_CONF": "background_thread:true,prof:true"},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def started_cluster():
|
||||
try:
|
||||
cluster.start()
|
||||
yield cluster
|
||||
|
||||
finally:
|
||||
cluster.shutdown()
|
||||
|
||||
|
||||
# asynchronous metrics are updated once every 60s by default. To make the test run faster, the setting
|
||||
# asynchronous_metric_update_period_s is being set to 1s so that the metrics are populated faster and
|
||||
# are available for querying during the test.
|
||||
def test_asynchronous_metric_jemalloc_profile_active(started_cluster):
|
||||
# default open
|
||||
if node1.is_built_with_sanitizer():
|
||||
pytest.skip("Disabled for sanitizers")
|
||||
|
||||
res_o = node1.query(
|
||||
"SELECT * FROM system.asynchronous_metrics WHERE metric ILIKE '%jemalloc.prof.active%' FORMAT Vertical;"
|
||||
)
|
||||
assert (
|
||||
res_o
|
||||
== """Row 1:
|
||||
──────
|
||||
metric: jemalloc.prof.active
|
||||
value: 1
|
||||
description: An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html
|
||||
"""
|
||||
)
|
||||
# disable
|
||||
node1.query("SYSTEM JEMALLOC DISABLE PROFILE")
|
||||
time.sleep(5)
|
||||
res_t = node1.query(
|
||||
"SELECT * FROM system.asynchronous_metrics WHERE metric ILIKE '%jemalloc.prof.active%' FORMAT Vertical;"
|
||||
)
|
||||
assert (
|
||||
res_t
|
||||
== """Row 1:
|
||||
──────
|
||||
metric: jemalloc.prof.active
|
||||
value: 0
|
||||
description: An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html
|
||||
"""
|
||||
)
|
||||
# enable
|
||||
node1.query("SYSTEM JEMALLOC ENABLE PROFILE")
|
||||
time.sleep(5)
|
||||
res_f = node1.query(
|
||||
"SELECT * FROM system.asynchronous_metrics WHERE metric ILIKE '%jemalloc.prof.active%' FORMAT Vertical;"
|
||||
)
|
||||
assert (
|
||||
res_f
|
||||
== """Row 1:
|
||||
──────
|
||||
metric: jemalloc.prof.active
|
||||
value: 1
|
||||
description: An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html
|
||||
"""
|
||||
)
|
@ -168,6 +168,32 @@ def test_restore_table(engine):
|
||||
assert instance.query("SELECT count(), sum(x) FROM test.table") == "100\t4950\n"
|
||||
|
||||
|
||||
def test_restore_materialized_view_with_definer():
|
||||
instance.query("CREATE DATABASE test")
|
||||
instance.query(
|
||||
"CREATE TABLE test.test_table (s String) ENGINE = MergeTree ORDER BY s"
|
||||
)
|
||||
instance.query("CREATE USER u1")
|
||||
instance.query("GRANT SELECT ON *.* TO u1")
|
||||
instance.query("GRANT INSERT ON *.* TO u1")
|
||||
|
||||
instance.query(
|
||||
"""
|
||||
CREATE MATERIALIZED VIEW test.test_mv_1 (s String)
|
||||
ENGINE = MergeTree ORDER BY s
|
||||
DEFINER = u1 SQL SECURITY DEFINER
|
||||
AS SELECT * FROM test.test_table
|
||||
"""
|
||||
)
|
||||
|
||||
backup_name = new_backup_name()
|
||||
instance.query(f"BACKUP DATABASE test TO {backup_name}")
|
||||
instance.query("DROP DATABASE test")
|
||||
instance.query("DROP USER u1")
|
||||
|
||||
instance.query(f"RESTORE DATABASE test FROM {backup_name}")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"engine", ["MergeTree", "Log", "TinyLog", "StripeLog", "Memory"]
|
||||
)
|
||||
|
@ -88,6 +88,11 @@ def test_dynamic_query_handler():
|
||||
"application/whatever; charset=cp1337"
|
||||
== res_custom_ct.headers["content-type"]
|
||||
)
|
||||
assert "it works" == res_custom_ct.headers["X-Test-Http-Response-Headers-Works"]
|
||||
assert (
|
||||
"also works"
|
||||
== res_custom_ct.headers["X-Test-Http-Response-Headers-Even-Multiple"]
|
||||
)
|
||||
|
||||
|
||||
def test_predefined_query_handler():
|
||||
@ -146,6 +151,10 @@ def test_predefined_query_handler():
|
||||
)
|
||||
assert b"max_final_threads\t1\nmax_threads\t1\n" == res2.content
|
||||
assert "application/generic+one" == res2.headers["content-type"]
|
||||
assert "it works" == res2.headers["X-Test-Http-Response-Headers-Works"]
|
||||
assert (
|
||||
"also works" == res2.headers["X-Test-Http-Response-Headers-Even-Multiple"]
|
||||
)
|
||||
|
||||
cluster.instance.query(
|
||||
"CREATE TABLE test_table (id UInt32, data String) Engine=TinyLog"
|
||||
@ -212,6 +221,18 @@ def test_fixed_static_handler():
|
||||
"test_get_fixed_static_handler", method="GET", headers={"XXX": "xxx"}
|
||||
).content
|
||||
)
|
||||
assert (
|
||||
"it works"
|
||||
== cluster.instance.http_request(
|
||||
"test_get_fixed_static_handler", method="GET", headers={"XXX": "xxx"}
|
||||
).headers["X-Test-Http-Response-Headers-Works"]
|
||||
)
|
||||
assert (
|
||||
"also works"
|
||||
== cluster.instance.http_request(
|
||||
"test_get_fixed_static_handler", method="GET", headers={"XXX": "xxx"}
|
||||
).headers["X-Test-Http-Response-Headers-Even-Multiple"]
|
||||
)
|
||||
|
||||
|
||||
def test_config_static_handler():
|
||||
|
@ -18,6 +18,10 @@
|
||||
<type>dynamic_query_handler</type>
|
||||
<query_param_name>get_dynamic_handler_query</query_param_name>
|
||||
<content_type>application/whatever; charset=cp1337</content_type>
|
||||
<http_response_headers>
|
||||
<X-Test-Http-Response-Headers-Works>it works</X-Test-Http-Response-Headers-Works>
|
||||
<X-Test-Http-Response-Headers-Even-Multiple>also works</X-Test-Http-Response-Headers-Even-Multiple>
|
||||
</http_response_headers>
|
||||
</handler>
|
||||
</rule>
|
||||
</http_handlers>
|
||||
|
@ -19,6 +19,10 @@
|
||||
<type>predefined_query_handler</type>
|
||||
<query>SELECT name, value FROM system.settings WHERE name = {setting_name_1:String} OR name = {setting_name_2:String}</query>
|
||||
<content_type>application/generic+one</content_type>
|
||||
<http_response_headers>
|
||||
<X-Test-Http-Response-Headers-Works>it works</X-Test-Http-Response-Headers-Works>
|
||||
<X-Test-Http-Response-Headers-Even-Multiple>also works</X-Test-Http-Response-Headers-Even-Multiple>
|
||||
</http_response_headers>
|
||||
</handler>
|
||||
</rule>
|
||||
<rule>
|
||||
|
@ -12,6 +12,10 @@
|
||||
<status>402</status>
|
||||
<content_type>text/html; charset=UTF-8</content_type>
|
||||
<response_content>Test get static handler and fix content</response_content>
|
||||
<http_response_headers>
|
||||
<X-Test-Http-Response-Headers-Works>it works</X-Test-Http-Response-Headers-Works>
|
||||
<X-Test-Http-Response-Headers-Even-Multiple>also works</X-Test-Http-Response-Headers-Even-Multiple>
|
||||
</http_response_headers>
|
||||
</handler>
|
||||
</rule>
|
||||
|
||||
|
@ -6,7 +6,11 @@ from helpers.cluster import ClickHouseCluster
|
||||
cluster = ClickHouseCluster(__file__)
|
||||
|
||||
node1 = cluster.add_instance(
|
||||
"node1", user_configs=["config/config.xml"], with_zookeeper=False
|
||||
"node1", user_configs=["config/config.xml"], with_zookeeper=True
|
||||
)
|
||||
|
||||
node2 = cluster.add_instance(
|
||||
"node2", user_configs=["config/config.xml"], with_zookeeper=True
|
||||
)
|
||||
|
||||
|
||||
@ -122,3 +126,58 @@ def test_single_node_normal(started_cluster):
|
||||
"""
|
||||
)
|
||||
run_test_single_node(started_cluster)
|
||||
|
||||
|
||||
def test_replicated_table_ddl(started_cluster):
|
||||
node1.query("DROP TABLE IF EXISTS test_stat")
|
||||
node2.query("DROP TABLE IF EXISTS test_stat")
|
||||
|
||||
node1.query(
|
||||
"""
|
||||
CREATE TABLE test_stat(a Int64 STATISTICS(tdigest, uniq), b Int64 STATISTICS(tdigest, uniq), c Int64 STATISTICS(tdigest))
|
||||
ENGINE = ReplicatedMergeTree('/clickhouse/test/statistics', '1') ORDER BY a;
|
||||
"""
|
||||
)
|
||||
node2.query(
|
||||
"""
|
||||
CREATE TABLE test_stat(a Int64 STATISTICS(tdigest, uniq), b Int64 STATISTICS(tdigest, uniq), c Int64 STATISTICS(tdigest))
|
||||
ENGINE = ReplicatedMergeTree('/clickhouse/test/statistics', '2') ORDER BY a;
|
||||
"""
|
||||
)
|
||||
|
||||
node1.query(
|
||||
"ALTER TABLE test_stat MODIFY STATISTICS c TYPE tdigest, uniq",
|
||||
settings={"alter_sync": "2"},
|
||||
)
|
||||
node1.query("ALTER TABLE test_stat DROP STATISTICS b", settings={"alter_sync": "2"})
|
||||
|
||||
assert (
|
||||
node2.query("SHOW CREATE TABLE test_stat")
|
||||
== "CREATE TABLE default.test_stat\\n(\\n `a` Int64 STATISTICS(tdigest, uniq),\\n `b` Int64,\\n `c` Int64 STATISTICS(tdigest, uniq)\\n)\\nENGINE = ReplicatedMergeTree(\\'/clickhouse/test/statistics\\', \\'2\\')\\nORDER BY a\\nSETTINGS index_granularity = 8192\n"
|
||||
)
|
||||
|
||||
node2.query("insert into test_stat values(1,2,3), (2,3,4)")
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0", "a", True)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0", "c", True)
|
||||
node1.query(
|
||||
"ALTER TABLE test_stat RENAME COLUMN c TO d", settings={"alter_sync": "2"}
|
||||
)
|
||||
assert node2.query("select sum(a), sum(d) from test_stat") == "3\t7\n"
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_1", "a", True)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_1", "c", False)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_1", "d", True)
|
||||
node1.query(
|
||||
"ALTER TABLE test_stat CLEAR STATISTICS d", settings={"alter_sync": "2"}
|
||||
)
|
||||
node1.query(
|
||||
"ALTER TABLE test_stat ADD STATISTICS b type tdigest",
|
||||
settings={"alter_sync": "2"},
|
||||
)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_2", "a", True)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_2", "b", False)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_2", "d", False)
|
||||
node1.query(
|
||||
"ALTER TABLE test_stat MATERIALIZE STATISTICS b", settings={"alter_sync": "2"}
|
||||
)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_3", "a", True)
|
||||
check_stat_file_on_disk(node2, "test_stat", "all_0_0_0_3", "b", True)
|
||||
|
@ -9,14 +9,14 @@ create table data_01256 as system.numbers Engine=Memory();
|
||||
|
||||
select 'min';
|
||||
create table buffer_01256 as system.numbers Engine=Buffer(currentDatabase(), data_01256, 1,
|
||||
2, 100, /* time */
|
||||
5, 100, /* time */
|
||||
4, 100, /* rows */
|
||||
1, 1e6 /* bytes */
|
||||
);
|
||||
insert into buffer_01256 select * from system.numbers limit 5;
|
||||
select count() from data_01256;
|
||||
-- sleep 2 (min time) + 1 (round up) + bias (1) = 4
|
||||
select sleepEachRow(2) from numbers(2) FORMAT Null;
|
||||
-- It is enough to ensure that the buffer will be flushed earlier then 2*min_time (10 sec)
|
||||
select sleepEachRow(9) FORMAT Null SETTINGS function_sleep_max_microseconds_per_block=10e6;
|
||||
select count() from data_01256;
|
||||
drop table buffer_01256;
|
||||
|
||||
|
3
tests/queries/0_stateless/03172_bcrypt_validation.sql
Normal file
3
tests/queries/0_stateless/03172_bcrypt_validation.sql
Normal file
@ -0,0 +1,3 @@
|
||||
-- Tags: no-fasttest
|
||||
DROP USER IF EXISTS 03172_user_invalid_bcrypt_hash;
|
||||
CREATE USER 03172_user_invalid_bcrypt_hash IDENTIFIED WITH bcrypt_hash BY '012345678901234567890123456789012345678901234567890123456789'; -- { serverError BAD_ARGUMENTS }
|
Loading…
Reference in New Issue
Block a user