mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-19 16:20:50 +00:00
Merge df1a9eee7c
into b94a7167a8
This commit is contained in:
commit
1b41f0245c
@ -1289,12 +1289,17 @@ bool ParserStringLiteral::parseImpl(Pos & pos, ASTPtr & node, Expected & expecte
|
|||||||
template <typename Collection>
|
template <typename Collection>
|
||||||
struct CollectionOfLiteralsLayer
|
struct CollectionOfLiteralsLayer
|
||||||
{
|
{
|
||||||
explicit CollectionOfLiteralsLayer(IParser::Pos & pos) : literal_begin(pos)
|
explicit CollectionOfLiteralsLayer(IParser::Pos & pos, bool is_functional_style_ = false)
|
||||||
|
: literal_begin(pos)
|
||||||
|
, is_functional_style(is_functional_style_)
|
||||||
{
|
{
|
||||||
|
pos.increaseDepth();
|
||||||
++pos;
|
++pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
IParser::Pos literal_begin;
|
IParser::Pos literal_begin;
|
||||||
|
/// Functional-style literal definition, e.g. `tuple(1, 2, 3)`, or `array(1, 2, 3)`.
|
||||||
|
bool is_functional_style = false;
|
||||||
Collection arr;
|
Collection arr;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1306,7 +1311,6 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
|
|||||||
|
|
||||||
std::vector<CollectionOfLiteralsLayer<Collection>> layers;
|
std::vector<CollectionOfLiteralsLayer<Collection>> layers;
|
||||||
layers.emplace_back(pos);
|
layers.emplace_back(pos);
|
||||||
pos.increaseDepth();
|
|
||||||
|
|
||||||
ParserLiteral literal_p;
|
ParserLiteral literal_p;
|
||||||
|
|
||||||
@ -1314,13 +1318,26 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
|
|||||||
{
|
{
|
||||||
if (!layers.back().arr.empty())
|
if (!layers.back().arr.empty())
|
||||||
{
|
{
|
||||||
if (pos->type == closing_bracket)
|
auto expected_closing = layers.back().is_functional_style ? TokenType::ClosingRoundBracket : closing_bracket;
|
||||||
|
if (pos->type == expected_closing)
|
||||||
{
|
{
|
||||||
std::shared_ptr<ASTLiteral> literal;
|
std::shared_ptr<ASTLiteral> literal;
|
||||||
|
|
||||||
/// Parse one-element tuples (e.g. (1)) later as single values for backward compatibility.
|
if (std::is_same_v<Collection, Tuple> && layers.back().arr.size() == 1 && !layers.back().is_functional_style)
|
||||||
if (std::is_same_v<Collection, Tuple> && layers.back().arr.size() == 1)
|
{
|
||||||
|
/// Single element inside round brackets is not a tuple, but a scalar.
|
||||||
|
/// We need to merge it with the previous layer.
|
||||||
|
/// Example: (1, (2)) is parsed as tuple(1, 2) instead of tuple(1, tuple(2)).
|
||||||
|
if (layers.size() > 1)
|
||||||
|
{
|
||||||
|
layers[layers.size() - 2].arr.push_back(layers.back().arr[0]);
|
||||||
|
layers.pop_back();
|
||||||
|
++pos;
|
||||||
|
pos.decreaseDepth();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
literal = std::make_shared<ASTLiteral>(std::move(layers.back().arr));
|
literal = std::make_shared<ASTLiteral>(std::move(layers.back().arr));
|
||||||
literal->begin = layers.back().literal_begin;
|
literal->begin = layers.back().literal_begin;
|
||||||
@ -1361,7 +1378,16 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
|
|||||||
else if (pos->type == opening_bracket)
|
else if (pos->type == opening_bracket)
|
||||||
{
|
{
|
||||||
layers.emplace_back(pos);
|
layers.emplace_back(pos);
|
||||||
pos.increaseDepth();
|
}
|
||||||
|
else if (pos->type == TokenType::BareWord && std::string_view(pos->begin, pos->end) == FunctionName<Collection>::value)
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
if (pos.isValid() && pos->type == TokenType::OpeningRoundBracket)
|
||||||
|
{
|
||||||
|
layers.emplace_back(pos, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -313,6 +313,12 @@ protected:
|
|||||||
private:
|
private:
|
||||||
TokenType opening_bracket;
|
TokenType opening_bracket;
|
||||||
TokenType closing_bracket;
|
TokenType closing_bracket;
|
||||||
|
|
||||||
|
template <typename> struct FunctionName;
|
||||||
|
template <> struct FunctionName<Array> { static constexpr auto value = "array"; };
|
||||||
|
template <> struct FunctionName<Tuple> { static constexpr auto value = "tuple"; };
|
||||||
|
|
||||||
|
std::string_view function_name = FunctionName<Collection>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A tuple of literals with same type.
|
/// A tuple of literals with same type.
|
||||||
|
@ -682,3 +682,81 @@ INSTANTIATE_TEST_SUITE_P(
|
|||||||
"WITH\n table_1 AS\n (\n SELECT\n country,\n city,\n c + some_derived_value AS _expr_1\n FROM matches\n WHERE start_date > toDate('2023-05-30')\n ),\n table_0 AS\n (\n SELECT\n country,\n city,\n AVG(_expr_1) AS _expr_0,\n MAX(_expr_1) AS aggr\n FROM table_1\n WHERE _expr_1 > 0\n GROUP BY\n country,\n city\n )\nSELECT\n country,\n city,\n _expr_0,\n aggr,\n CONCAT(city, ' in ', country) AS place,\n LEFT(country, 2) AS country_code\nFROM table_0\nORDER BY\n aggr ASC,\n country DESC\nLIMIT 20",
|
"WITH\n table_1 AS\n (\n SELECT\n country,\n city,\n c + some_derived_value AS _expr_1\n FROM matches\n WHERE start_date > toDate('2023-05-30')\n ),\n table_0 AS\n (\n SELECT\n country,\n city,\n AVG(_expr_1) AS _expr_0,\n MAX(_expr_1) AS aggr\n FROM table_1\n WHERE _expr_1 > 0\n GROUP BY\n country,\n city\n )\nSELECT\n country,\n city,\n _expr_0,\n aggr,\n CONCAT(city, ' in ', country) AS place,\n LEFT(country, 2) AS country_code\nFROM table_0\nORDER BY\n aggr ASC,\n country DESC\nLIMIT 20",
|
||||||
},
|
},
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
namespace DB { std::ostream & operator<<(std::ostream & stream, const Field & field) { return stream << field.dump(); } }
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ParserType, typename LiteralType>
|
||||||
|
void testCollectionOfLiteralsParser(
|
||||||
|
const std::vector<std::string_view> & test_cases,
|
||||||
|
const std::vector<std::string_view> & negative_test_cases,
|
||||||
|
size_t top_level_size)
|
||||||
|
{
|
||||||
|
ParserType parser;
|
||||||
|
auto parse_literal = [&](std::string_view text) -> std::optional<LiteralType>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto ast = parseQuery(parser, text.begin(), text.end(), 0, 0, 0);
|
||||||
|
const auto * literal = typeid_cast<const ASTLiteral *>(ast.get());
|
||||||
|
if (!literal || literal->value.getType() != Field::TypeToEnum<LiteralType>::value)
|
||||||
|
return {};
|
||||||
|
return literal->value.template safeGet<LiteralType>();
|
||||||
|
}
|
||||||
|
catch (const DB::Exception & e)
|
||||||
|
{
|
||||||
|
std::cerr << e.displayText() << std::endl;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto & reference_literal = parse_literal(test_cases[0]);
|
||||||
|
ASSERT_TRUE(reference_literal && reference_literal->size() == top_level_size);
|
||||||
|
|
||||||
|
for (size_t i = 1; i < test_cases.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(fmt::format("Test case #{}: {}", i + 1, test_cases[i]));
|
||||||
|
EXPECT_EQ(reference_literal, parse_literal(test_cases[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < negative_test_cases.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(fmt::format("Negative test case #{}: {}", i + 1, negative_test_cases[i]));
|
||||||
|
auto negative_example = parse_literal(negative_test_cases[i]);
|
||||||
|
ASSERT_TRUE(negative_example);
|
||||||
|
EXPECT_NE(reference_literal, negative_example);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParserTest, parseTupleLiterals)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
testCollectionOfLiteralsParser<ParserTupleOfLiterals, Tuple>({
|
||||||
|
"((1, 2), (3, 4))",
|
||||||
|
"(((1), 2), (3, 4))",
|
||||||
|
"(((1, 2)), (3, 4))",
|
||||||
|
"(((1), 2), (((((((3))))), 4)))",
|
||||||
|
"(tuple(1, 2), (3, 4))",
|
||||||
|
"(tuple((1), (2)), (tuple(3, 4)))",
|
||||||
|
}, {
|
||||||
|
"((tuple(1), 2), (3, 4))",
|
||||||
|
}, 2);
|
||||||
|
|
||||||
|
// testCollectionOfLiteralsParser<ParserArrayOfLiterals, Array>({
|
||||||
|
// "[[1, 2, 3]]",
|
||||||
|
// "[[(1), (2), 3]]",
|
||||||
|
// "[[1, (((2))), (((((3)))))]]",
|
||||||
|
// "[(([1, (((2))), (((((3)))))]))]",
|
||||||
|
// "[([1, 2, 3])]",
|
||||||
|
// }, {
|
||||||
|
// "[[[1], 2, 3]]",
|
||||||
|
// "[1, 2, 3]",
|
||||||
|
// "[[1, (2, 3)]]",
|
||||||
|
// "[[[1, 2, 3]]]",
|
||||||
|
// }, 1);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
std::cerr << getCurrentExceptionMessage(true) << std::endl;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user