diff --git a/docs/en/operations/system-tables/functions.md b/docs/en/operations/system-tables/functions.md index 60bfa08975b..d52bf24f289 100644 --- a/docs/en/operations/system-tables/functions.md +++ b/docs/en/operations/system-tables/functions.md @@ -7,28 +7,34 @@ Contains information about normal and aggregate functions. Columns: -- `name`(`String`) – The name of the function. -- `is_aggregate`(`UInt8`) — Whether the function is aggregate. +- `name` ([String](../../sql-reference/data-types/string.md)) – The name of the function. +- `is_aggregate` ([UInt8](../../sql-reference/data-types/int-uint.md)) — Whether the function is an aggregate function. +- `is_deterministic` ([Nullable](../../sql-reference/data-types/nullable.md)([UInt8](../../sql-reference/data-types/int-uint.md))) - Whether the function is deterministic. +- `case_insensitive`, ([UInt8](../../sql-reference/data-types/int-uint.md)) - Whether the function name can be used case-insensitively. +- `alias_to`, ([String](../../sql-reference/data-types/string.md)) - The original function name, if the function name is an alias. +- `create_query`, ([String](../../sql-reference/data-types/enum.md)) - Unused. +- `origin`, ([Enum8](../../sql-reference/data-types/string.md)) - Unused. +- `description`, ([String](../../sql-reference/data-types/string.md)) - A high-level description what the function does. +- `syntax`, ([String](../../sql-reference/data-types/string.md)) - Signature of the function. +- `arguments`, ([String](../../sql-reference/data-types/string.md)) - What arguments does the function take. +- `returned_value`, ([String](../../sql-reference/data-types/string.md)) - What does the function return. +- `examples`, ([String](../../sql-reference/data-types/string.md)) - Example usage of the function. +- `categories`, ([String](../../sql-reference/data-types/string.md)) - The category of the function. **Example** ```sql - SELECT * FROM system.functions LIMIT 10; + SELECT name, is_aggregate, is_deterministic, case_insensitive, alias_to FROM system.functions LIMIT 5; ``` ```text -┌─name──────────────────┬─is_aggregate─┬─case_insensitive─┬─alias_to─┬─create_query─┬─origin─┐ -│ logTrace │ 0 │ 0 │ │ │ System │ -│ aes_decrypt_mysql │ 0 │ 0 │ │ │ System │ -│ aes_encrypt_mysql │ 0 │ 0 │ │ │ System │ -│ decrypt │ 0 │ 0 │ │ │ System │ -│ encrypt │ 0 │ 0 │ │ │ System │ -│ toBool │ 0 │ 0 │ │ │ System │ -│ windowID │ 0 │ 0 │ │ │ System │ -│ hopStart │ 0 │ 0 │ │ │ System │ -│ hop │ 0 │ 0 │ │ │ System │ -│ snowflakeToDateTime64 │ 0 │ 0 │ │ │ System │ -└───────────────────────┴──────────────┴──────────────────┴──────────┴──────────────┴────────┘ +┌─name─────────────────────┬─is_aggregate─┬─is_deterministic─┬─case_insensitive─┬─alias_to─┐ +│ BLAKE3 │ 0 │ 1 │ 0 │ │ +│ sipHash128Reference │ 0 │ 1 │ 0 │ │ +│ mapExtractKeyLike │ 0 │ 1 │ 0 │ │ +│ sipHash128ReferenceKeyed │ 0 │ 1 │ 0 │ │ +│ mapPartialSort │ 0 │ 1 │ 0 │ │ +└──────────────────────────┴──────────────┴──────────────────┴──────────────────┴──────────┘ -10 rows in set. Elapsed: 0.002 sec. +5 rows in set. Elapsed: 0.002 sec. ``` diff --git a/src/Storages/System/StorageSystemFunctions.cpp b/src/Storages/System/StorageSystemFunctions.cpp index 0c609d84da3..776371a3835 100644 --- a/src/Storages/System/StorageSystemFunctions.cpp +++ b/src/Storages/System/StorageSystemFunctions.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -15,6 +16,15 @@ namespace DB { +namespace ErrorCodes +{ + extern const int DICTIONARIES_WAS_NOT_LOADED; + extern const int FUNCTION_NOT_ALLOWED; + extern const int LOGICAL_ERROR; + extern const int NOT_IMPLEMENTED; + extern const int SUPPORT_IS_DISABLED; +}; + enum class FunctionOrigin : Int8 { SYSTEM = 0, @@ -29,6 +39,7 @@ namespace MutableColumns & res_columns, const String & name, UInt64 is_aggregate, + std::optional is_deterministic, const String & create_query, FunctionOrigin function_origin, const Factory & factory) @@ -36,53 +47,58 @@ namespace res_columns[0]->insert(name); res_columns[1]->insert(is_aggregate); + if (!is_deterministic.has_value()) + res_columns[2]->insertDefault(); + else + res_columns[2]->insert(*is_deterministic); + if constexpr (std::is_same_v || std::is_same_v) { - res_columns[2]->insert(false); - res_columns[3]->insertDefault(); + res_columns[3]->insert(false); + res_columns[4]->insertDefault(); } else { - res_columns[2]->insert(factory.isCaseInsensitive(name)); + res_columns[3]->insert(factory.isCaseInsensitive(name)); if (factory.isAlias(name)) - res_columns[3]->insert(factory.aliasTo(name)); + res_columns[4]->insert(factory.aliasTo(name)); else - res_columns[3]->insertDefault(); + res_columns[4]->insertDefault(); } - res_columns[4]->insert(create_query); - res_columns[5]->insert(static_cast(function_origin)); + res_columns[5]->insert(create_query); + res_columns[6]->insert(static_cast(function_origin)); if constexpr (std::is_same_v) { if (factory.isAlias(name)) { - res_columns[6]->insertDefault(); res_columns[7]->insertDefault(); res_columns[8]->insertDefault(); res_columns[9]->insertDefault(); res_columns[10]->insertDefault(); res_columns[11]->insertDefault(); + res_columns[12]->insertDefault(); } else { auto documentation = factory.getDocumentation(name); - res_columns[6]->insert(documentation.description); - res_columns[7]->insert(documentation.syntax); - res_columns[8]->insert(documentation.argumentsAsString()); - res_columns[9]->insert(documentation.returned_value); - res_columns[10]->insert(documentation.examplesAsString()); - res_columns[11]->insert(documentation.categoriesAsString()); + res_columns[7]->insert(documentation.description); + res_columns[8]->insert(documentation.syntax); + res_columns[9]->insert(documentation.argumentsAsString()); + res_columns[10]->insert(documentation.returned_value); + res_columns[11]->insert(documentation.examplesAsString()); + res_columns[12]->insert(documentation.categoriesAsString()); } } else { - res_columns[6]->insertDefault(); res_columns[7]->insertDefault(); res_columns[8]->insertDefault(); res_columns[9]->insertDefault(); res_columns[10]->insertDefault(); res_columns[11]->insertDefault(); + res_columns[12]->insertDefault(); } } } @@ -102,6 +118,7 @@ NamesAndTypesList StorageSystemFunctions::getNamesAndTypes() return { {"name", std::make_shared()}, {"is_aggregate", std::make_shared()}, + {"is_deterministic", std::make_shared(std::make_shared())}, {"case_insensitive", std::make_shared()}, {"alias_to", std::make_shared()}, {"create_query", std::make_shared()}, @@ -121,14 +138,34 @@ void StorageSystemFunctions::fillData(MutableColumns & res_columns, ContextPtr c const auto & function_names = functions_factory.getAllRegisteredNames(); for (const auto & function_name : function_names) { - fillRow(res_columns, function_name, UInt64(0), "", FunctionOrigin::SYSTEM, functions_factory); + std::optional is_deterministic; + try + { + is_deterministic = functions_factory.tryGet(function_name, context)->isDeterministic(); + } + catch (const Exception & e) + { + /// Some functions throw because they need special configuration or setup before use. + if (e.code() == ErrorCodes::DICTIONARIES_WAS_NOT_LOADED + || e.code() == ErrorCodes::FUNCTION_NOT_ALLOWED + || e.code() == ErrorCodes::LOGICAL_ERROR + || e.code() == ErrorCodes::NOT_IMPLEMENTED + || e.code() == ErrorCodes::SUPPORT_IS_DISABLED) + { + /// Ignore exception, show is_deterministic = NULL. + } + else + throw; + } + + fillRow(res_columns, function_name, 0, is_deterministic, "", FunctionOrigin::SYSTEM, functions_factory); } const auto & aggregate_functions_factory = AggregateFunctionFactory::instance(); const auto & aggregate_function_names = aggregate_functions_factory.getAllRegisteredNames(); for (const auto & function_name : aggregate_function_names) { - fillRow(res_columns, function_name, UInt64(1), "", FunctionOrigin::SYSTEM, aggregate_functions_factory); + fillRow(res_columns, function_name, 1, {1}, "", FunctionOrigin::SYSTEM, aggregate_functions_factory); } const auto & user_defined_sql_functions_factory = UserDefinedSQLFunctionFactory::instance(); @@ -136,14 +173,14 @@ void StorageSystemFunctions::fillData(MutableColumns & res_columns, ContextPtr c for (const auto & function_name : user_defined_sql_functions_names) { auto create_query = queryToString(user_defined_sql_functions_factory.get(function_name)); - fillRow(res_columns, function_name, UInt64(0), create_query, FunctionOrigin::SQL_USER_DEFINED, user_defined_sql_functions_factory); + fillRow(res_columns, function_name, 0, {0}, create_query, FunctionOrigin::SQL_USER_DEFINED, user_defined_sql_functions_factory); } const auto & user_defined_executable_functions_factory = UserDefinedExecutableFunctionFactory::instance(); const auto & user_defined_executable_functions_names = user_defined_executable_functions_factory.getRegisteredNames(context); for (const auto & function_name : user_defined_executable_functions_names) { - fillRow(res_columns, function_name, UInt64(0), "", FunctionOrigin::EXECUTABLE_USER_DEFINED, user_defined_executable_functions_factory); + fillRow(res_columns, function_name, 0, {0}, "", FunctionOrigin::EXECUTABLE_USER_DEFINED, user_defined_executable_functions_factory); } } diff --git a/tests/queries/0_stateless/02117_show_create_table_system.reference b/tests/queries/0_stateless/02117_show_create_table_system.reference index c7523ede607..92a38eaf35d 100644 --- a/tests/queries/0_stateless/02117_show_create_table_system.reference +++ b/tests/queries/0_stateless/02117_show_create_table_system.reference @@ -283,6 +283,7 @@ CREATE TABLE system.functions ( `name` String, `is_aggregate` UInt8, + `is_deterministic` Nullable(UInt8), `case_insensitive` UInt8, `alias_to` String, `create_query` String, diff --git a/tests/queries/0_stateless/02815_no_throw_in_simple_queries.sh b/tests/queries/0_stateless/02815_no_throw_in_simple_queries.sh index a5c6de3ce58..e2e4f3f8433 100755 --- a/tests/queries/0_stateless/02815_no_throw_in_simple_queries.sh +++ b/tests/queries/0_stateless/02815_no_throw_in_simple_queries.sh @@ -53,4 +53,9 @@ expect eof EOF } -run "$CLICKHOUSE_LOCAL" +run "$CLICKHOUSE_LOCAL --disable_suggestion" +# Suggestions are off because the suggestion feature initializes itself by reading all available function +# names from "system.functions". Getting the value for field "is_obsolete" occasionally throws (e.g. for +# certain dictionary functions when dictionaries are not set up yet). Exceptions are properly handled, but +# they exist for a short time. This, in combination with CLICKHOUSE_TERMINATE_ON_ANY_EXCEPTION, terminates +# clickhouse-local and clickhouse-client when run in interactive mode *with* suggestions.