Fixing tuple/array literal parsing

This commit is contained in:
vdimir 2024-08-28 16:36:10 +00:00
parent 674cddc969
commit e37546dd41
No known key found for this signature in database
GPG Key ID: 6EE4CE2BEDC51862
3 changed files with 80 additions and 7 deletions

View File

@ -1289,12 +1289,16 @@ bool ParserStringLiteral::parseImpl(Pos & pos, ASTPtr & node, Expected & expecte
template <typename Collection>
struct CollectionOfLiteralsLayer
{
explicit CollectionOfLiteralsLayer(IParser::Pos & pos) : literal_begin(pos)
explicit CollectionOfLiteralsLayer(IParser::Pos & pos, TokenType closing_bracket_)
: literal_begin(pos)
, closing_bracket(closing_bracket_)
{
pos.increaseDepth();
++pos;
}
IParser::Pos literal_begin;
TokenType closing_bracket;
Collection arr;
};
@ -1305,8 +1309,7 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
return false;
std::vector<CollectionOfLiteralsLayer<Collection>> layers;
layers.emplace_back(pos);
pos.increaseDepth();
layers.emplace_back(pos, closing_bracket);
ParserLiteral literal_p;
@ -1314,13 +1317,22 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
{
if (!layers.back().arr.empty())
{
if (pos->type == closing_bracket)
if (pos->type == layers.back().closing_bracket)
{
std::shared_ptr<ASTLiteral> literal;
/// Parse one-element tuples (e.g. (1)) later as single values for backward compatibility.
// /// 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)
{
if (layers.size() > 1)
{
layers[layers.size() - 2].arr.push_back(layers.back().arr[0]);
layers.pop_back();
pos.decreaseDepth();
continue;
}
return false;
}
literal = std::make_shared<ASTLiteral>(std::move(layers.back().arr));
literal->begin = layers.back().literal_begin;
@ -1360,8 +1372,17 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
}
else if (pos->type == opening_bracket)
{
layers.emplace_back(pos);
pos.increaseDepth();
layers.emplace_back(pos, closing_bracket);
}
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, TokenType::ClosingRoundBracket);
}
else
return false;
}
else
return false;

View File

@ -313,6 +313,12 @@ protected:
private:
TokenType opening_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.

View File

@ -662,3 +662,49 @@ 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",
},
})));
namespace DB { std::ostream & operator<<(std::ostream & stream, const Field & field) { return stream << field.dump(); } }
TEST(ParserTest, parseTupleLiterals)
try
{
ParserTupleOfLiterals parser;
auto parse_tuple = [&](std::string_view text) -> std::optional<Tuple>
{
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::Types::Tuple)
return {};
return literal->value.safeGet<Tuple>();
}
catch (const DB::Exception & e)
{
std::cerr << e.displayText() << std::endl;
return {};
}
};
const auto & tuple = parse_tuple("((1, 2), (3, 4))");
ASSERT_TRUE(tuple && tuple->size() == 2);
std::vector<std::string_view> test_cases = {
// "((1, 2,), (3, 4,),)",
// " ( (\t\t 1 \r\n , 2 ,\n ) \t\t, ( 3 , 4 \t, ) , ) ",
"(((1, 2)), (3, 4))",
"(((1), 2), (3, 4))",
"(tuple(1, 2), (3, 4))",
};
for (size_t i = 0; i < test_cases.size(); ++i)
{
SCOPED_TRACE(fmt::format("Test case #{}: {}", i + 1, test_cases[i]));
EXPECT_EQ(tuple, parse_tuple(test_cases[i]));
}
}
catch (...)
{
std::cerr << getCurrentExceptionMessage(true) << std::endl;
throw;
}