diff --git a/dbms/src/AggregateFunctions/AggregateFunctionArray.cpp b/dbms/src/AggregateFunctions/AggregateFunctionArray.cpp index f42c5b6d142..9cb7d03bf69 100644 --- a/dbms/src/AggregateFunctions/AggregateFunctionArray.cpp +++ b/dbms/src/AggregateFunctions/AggregateFunctionArray.cpp @@ -18,6 +18,9 @@ public: DataTypes transformArguments(const DataTypes & arguments) const override { + if (0 == arguments.size()) + throw Exception("-Array aggregate functions require at least one argument", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + DataTypes nested_arguments; for (const auto & type : arguments) { diff --git a/dbms/src/Common/ErrorCodes.cpp b/dbms/src/Common/ErrorCodes.cpp index a1662563a1f..a789a602751 100644 --- a/dbms/src/Common/ErrorCodes.cpp +++ b/dbms/src/Common/ErrorCodes.cpp @@ -377,6 +377,7 @@ namespace ErrorCodes extern const int CANNOT_STAT = 400; extern const int FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME = 401; extern const int CANNOT_IOSETUP = 402; + extern const int INVALID_JOIN_ON_EXPRESSION = 403; extern const int KEEPER_EXCEPTION = 999; diff --git a/dbms/src/Functions/FunctionsArray.h b/dbms/src/Functions/FunctionsArray.h index 3cd1a8968f7..3e6ad6c588b 100644 --- a/dbms/src/Functions/FunctionsArray.h +++ b/dbms/src/Functions/FunctionsArray.h @@ -1011,10 +1011,11 @@ public: DataTypePtr observed_type0 = removeNullable(array_type->getNestedType()); DataTypePtr observed_type1 = removeNullable(arguments[1]); - if (!(observed_type0->isNumber() && observed_type1->isNumber()) + /// We also support arrays of Enum type (that are represented by number) to search numeric values. + if (!(observed_type0->isValueRepresentedByNumber() && observed_type1->isNumber()) && !observed_type0->equals(*observed_type1)) throw Exception("Types of array and 2nd argument of function " - + getName() + " must be identical up to nullability. Passed: " + + getName() + " must be identical up to nullability or numeric types or Enum and numeric type. Passed: " + arguments[0]->getName() + " and " + arguments[1]->getName() + ".", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); } diff --git a/dbms/src/Interpreters/ExpressionAnalyzer.cpp b/dbms/src/Interpreters/ExpressionAnalyzer.cpp index 6497b865cea..a065d01f88c 100644 --- a/dbms/src/Interpreters/ExpressionAnalyzer.cpp +++ b/dbms/src/Interpreters/ExpressionAnalyzer.cpp @@ -61,6 +61,7 @@ #include #include #include +#include namespace DB @@ -89,6 +90,7 @@ namespace ErrorCodes extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int CONDITIONAL_TREE_PARENT_NOT_FOUND; extern const int TYPE_MISMATCH; + extern const int INVALID_JOIN_ON_EXPRESSION; } @@ -2856,22 +2858,53 @@ void ExpressionAnalyzer::collectJoinedColumns(NameSet & joined_columns, NamesAnd nested_result_sample = InterpreterSelectWithUnionQuery::getSampleBlock(subquery, context); } + auto add_name_to_join_keys = [](Names & join_keys, const String & name, const char * where) + { + if (join_keys.end() == std::find(join_keys.begin(), join_keys.end(), name)) + join_keys.push_back(name); + else + throw Exception("Duplicate column " + name + " " + where, ErrorCodes::DUPLICATE_COLUMN); + }; + if (table_join.using_expression_list) { auto & keys = typeid_cast(*table_join.using_expression_list); for (const auto & key : keys.children) { - if (join_key_names_left.end() == std::find(join_key_names_left.begin(), join_key_names_left.end(), key->getColumnName())) - join_key_names_left.push_back(key->getColumnName()); - else - throw Exception("Duplicate column " + key->getColumnName() + " in USING list", ErrorCodes::DUPLICATE_COLUMN); - - if (join_key_names_right.end() == std::find(join_key_names_right.begin(), join_key_names_right.end(), key->getAliasOrColumnName())) - join_key_names_right.push_back(key->getAliasOrColumnName()); - else - throw Exception("Duplicate column " + key->getAliasOrColumnName() + " in USING list", ErrorCodes::DUPLICATE_COLUMN); + add_name_to_join_keys(join_key_names_left, key->getColumnName(), "in USING list"); + add_name_to_join_keys(join_key_names_right, key->getAliasOrColumnName(), "in USING list"); } } + else if (table_join.on_expression) + { + const auto supported_syntax = + "\nSupported syntax: JOIN ON [table.]column = [table.]column [AND [table.]column = [table.]column ...]"; + auto throwSyntaxException = [&](const String & msg) + { + throw Exception("Invalid expression for JOIN ON. " + msg + supported_syntax, ErrorCodes::INVALID_JOIN_ON_EXPRESSION); + }; + + auto add_columns_from_equals_expr = [&](const ASTPtr & expr) + { + auto * func_equals = typeid_cast(expr.get()); + if (!func_equals || func_equals->name != "equals") + throwSyntaxException("Expected equals expression, got " + queryToString(expr)); + + String left_name = func_equals->arguments->children.at(0)->getAliasOrColumnName(); + String right_name = func_equals->arguments->children.at(1)->getAliasOrColumnName(); + add_name_to_join_keys(join_key_names_left, left_name, "in JOIN ON expression for left table"); + add_name_to_join_keys(join_key_names_right, right_name, "in JOIN ON expression for right table"); + }; + + auto * func = typeid_cast(table_join.on_expression.get()); + if (func && func->name == "and") + { + for (auto expr : func->children) + add_columns_from_equals_expr(expr); + } + else + add_columns_from_equals_expr(table_join.on_expression); + } for (const auto i : ext::range(0, nested_result_sample.columns())) { diff --git a/dbms/tests/queries/0_stateless/00534_filimonov.data b/dbms/tests/queries/0_stateless/00534_filimonov.data index 2dd470403c0..b4c15b01ef4 100644 --- a/dbms/tests/queries/0_stateless/00534_filimonov.data +++ b/dbms/tests/queries/0_stateless/00534_filimonov.data @@ -428,5 +428,7 @@ SELECT COVAR_SAMPArray([CAST( 0 AS Int8)],arrayPopBack([CAST( 0 AS Int8)])); SELECT medianTimingWeightedArray([CAST( 0 AS Int8)],arrayPopBack([CAST( 0 AS Int8)])); SELECT quantilesDeterministicArray([CAST( 0 AS Int8)],arrayPopBack([CAST( 0 AS Int32)])); -SELECT maxIntersections([], []) -SELECT sumMap([], []) +SELECT maxIntersections([], []); +SELECT sumMap([], []); + +SELECT countArray();