Merge pull request #36167 from kitaisreal/special-operators-broken-aliases-fix

Fix broken aliases during parsing of special operators
This commit is contained in:
Maksim Kita 2022-04-21 22:31:45 +02:00 committed by GitHub
commit bac1f12a2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 409 additions and 90 deletions

View File

@ -298,28 +298,69 @@ namespace
{
bool parseCastAs(IParser::Pos & pos, ASTPtr & node, Expected & expected)
{
/// expr AS type
/** Possible variants for cast operator cast(expr [[AS] alias_1] AS Type), cast(expr [[AS] alias_1], type_expr [[as] alias_2]).
* First try to match with cast(expr [[AS] alias_1] AS Type)
* Then try to match with cast(expr [[AS] alias_1], type_expr [[as] alias_2]).
*/
ASTPtr expr_node;
ASTPtr type_node;
ASTPtr identifier_node;
if (ParserExpression().parse(pos, expr_node, expected))
{
if (ParserKeyword("AS").ignore(pos, expected))
ParserKeyword as_keyword_parser("AS");
bool parse_as = as_keyword_parser.ignore(pos, expected);
/// CAST (a b AS UInt32) OR CAST (a b, expr)
if (!parse_as && ParserIdentifier().parse(pos, identifier_node, expected))
{
if (ParserDataType().parse(pos, type_node, expected))
expr_node->setAlias(getIdentifierName(identifier_node));
parse_as = as_keyword_parser.ignore(pos, expected);
}
if (parse_as)
{
/// CAST (a AS Type) OR CAST (a AS b AS Type) OR CAST (a AS b, expr)
auto begin = pos;
auto expected_copy = expected;
bool next_identifier = ParserIdentifier().ignore(begin, expected_copy);
bool next_identifier_with_comma = next_identifier && ParserToken(TokenType::Comma).ignore(begin, expected_copy);
bool next_identifier_with_as
= next_identifier && !next_identifier_with_comma && as_keyword_parser.ignore(begin, expected_copy);
if (next_identifier_with_as)
{
if (ParserIdentifier().parse(pos, identifier_node, expected) && as_keyword_parser.ignore(pos, expected))
expr_node->setAlias(getIdentifierName(identifier_node));
else
return false;
}
if (!next_identifier_with_comma && ParserDataType().parse(pos, type_node, expected))
{
node = createFunctionCast(expr_node, type_node);
return true;
}
}
else if (ParserToken(TokenType::Comma).ignore(pos, expected))
/// CAST(a AS b, expr)
if (parse_as)
{
if (ParserExpression().parse(pos, type_node, expected))
{
node = makeASTFunction("CAST", expr_node, type_node);
return true;
}
if (ParserIdentifier().parse(pos, identifier_node, expected))
expr_node->setAlias(getIdentifierName(identifier_node));
else
return false;
}
if (ParserToken(TokenType::Comma).ignore(pos, expected)
&& ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, type_node, expected))
{
node = makeASTFunction("CAST", expr_node, type_node);
return true;
}
}
@ -331,6 +372,9 @@ namespace
/// Either SUBSTRING(expr FROM start) or SUBSTRING(expr FROM start FOR length) or SUBSTRING(expr, start, length)
/// The latter will be parsed normally as a function later.
ParserKeyword as_keyword_parser("AS");
ParserIdentifier identifier_parser;
ASTPtr expr_node;
ASTPtr start_node;
ASTPtr length_node;
@ -338,35 +382,65 @@ namespace
if (!ParserExpression().parse(pos, expr_node, expected))
return false;
if (pos->type != TokenType::Comma)
auto from_keyword_parser = ParserKeyword("FROM");
bool from_exists = from_keyword_parser.check(pos, expected);
if (!from_exists && pos->type != TokenType::Comma)
{
if (!ParserKeyword("FROM").ignore(pos, expected))
ASTPtr identifier_node;
bool parsed_as = as_keyword_parser.ignore(pos, expected);
bool parsed_identifer = identifier_parser.parse(pos, identifier_node, expected);
if (parsed_as && !parsed_identifer)
return false;
if (parsed_identifer)
expr_node->setAlias(getIdentifierName(identifier_node));
from_exists = from_keyword_parser.check(pos, expected);
}
else
if (pos->type == TokenType::Comma)
{
if (from_exists)
return false;
++pos;
}
if (!ParserExpression().parse(pos, start_node, expected))
return false;
if (pos->type != TokenType::ClosingRoundBracket)
auto for_keyword_parser = ParserKeyword("FOR");
bool for_exists = for_keyword_parser.check(pos, expected);
if (!for_exists && pos->type != TokenType::Comma)
{
if (pos->type != TokenType::Comma)
{
if (!ParserKeyword("FOR").ignore(pos, expected))
return false;
}
else
{
++pos;
}
if (!ParserExpression().parse(pos, length_node, expected))
ASTPtr identifier_node;
bool parsed_as = as_keyword_parser.ignore(pos, expected);
bool parsed_identifer = identifier_parser.parse(pos, identifier_node, expected);
if (parsed_as && !parsed_identifer)
return false;
if (parsed_identifer)
start_node->setAlias(getIdentifierName(identifier_node));
for_exists = for_keyword_parser.check(pos, expected);
}
bool need_parse_length_expression = for_exists;
if (pos->type == TokenType::Comma)
{
if (for_exists)
return false;
++pos;
need_parse_length_expression = true;
}
if (need_parse_length_expression
&& !ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, length_node, expected))
return false;
/// Convert to canonical representation in functional form: SUBSTRING(expr, start, length)
if (length_node)
node = makeASTFunction("substring", expr_node, start_node, length_node);
@ -378,7 +452,7 @@ namespace
bool parseTrim(bool trim_left, bool trim_right, IParser::Pos & pos, ASTPtr & node, Expected & expected)
{
/// Handles all possible TRIM/LTRIM/RTRIM call variants
/// Handles all possible TRIM/LTRIM/RTRIM call variants ([[LEADING|TRAILING|BOTH] trim_character FROM] input_string)
std::string func_name;
bool char_override = false;
@ -414,7 +488,26 @@ namespace
{
if (!ParserExpression().parse(pos, to_remove, expected))
return false;
if (!ParserKeyword("FROM").ignore(pos, expected))
auto from_keyword_parser = ParserKeyword("FROM");
bool from_exists = from_keyword_parser.check(pos, expected);
if (!from_exists)
{
ASTPtr identifier_node;
bool parsed_as = ParserKeyword("AS").ignore(pos, expected);
bool parsed_identifer = ParserIdentifier().parse(pos, identifier_node, expected);
if (parsed_as && !parsed_identifer)
return false;
if (parsed_identifer)
to_remove->setAlias(getIdentifierName(identifier_node));
from_exists = from_keyword_parser.check(pos, expected);
}
if (!from_exists)
return false;
auto quote_meta_func_node = std::make_shared<ASTFunction>();
@ -429,7 +522,7 @@ namespace
}
}
if (!ParserExpression().parse(pos, expr_node, expected))
if (!ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, expr_node, expected))
return false;
/// Convert to regexp replace function call
@ -440,33 +533,24 @@ namespace
auto pattern_list_args = std::make_shared<ASTExpressionList>();
if (trim_left && trim_right)
{
pattern_list_args->children = {
std::make_shared<ASTLiteral>("^["),
to_remove,
std::make_shared<ASTLiteral>("]+|["),
to_remove,
std::make_shared<ASTLiteral>("]+$")
};
pattern_list_args->children
= {std::make_shared<ASTLiteral>("^["),
to_remove,
std::make_shared<ASTLiteral>("]+|["),
to_remove,
std::make_shared<ASTLiteral>("]+$")};
func_name = "replaceRegexpAll";
}
else
{
if (trim_left)
{
pattern_list_args->children = {
std::make_shared<ASTLiteral>("^["),
to_remove,
std::make_shared<ASTLiteral>("]+")
};
pattern_list_args->children = {std::make_shared<ASTLiteral>("^["), to_remove, std::make_shared<ASTLiteral>("]+")};
}
else
{
/// trim_right == false not possible
pattern_list_args->children = {
std::make_shared<ASTLiteral>("["),
to_remove,
std::make_shared<ASTLiteral>("]+$")
};
pattern_list_args->children = {std::make_shared<ASTLiteral>("["), to_remove, std::make_shared<ASTLiteral>("]+$")};
}
func_name = "replaceRegexpOne";
}
@ -506,6 +590,9 @@ namespace
bool parseExtract(IParser::Pos & pos, ASTPtr & node, Expected & expected)
{
/// First try to match with date extract operator EXTRACT(part FROM date)
/// Then with function extract(haystack, pattern)
IParser::Pos begin = pos;
IntervalKind interval_kind;
@ -514,7 +601,7 @@ namespace
ASTPtr expr;
ParserKeyword s_from("FROM");
ParserExpression elem_parser;
ParserExpressionWithOptionalAlias elem_parser(true /*allow_alias_without_as_keyword*/);
if (s_from.ignore(pos, expected) && elem_parser.parse(pos, expr, expected))
{
@ -526,7 +613,7 @@ namespace
pos = begin;
ASTPtr expr_list;
if (!ParserExpressionList(false, false).parse(pos, expr_list, expected))
if (!ParserExpressionList(true /*allow_alias_without_as_keyword*/).parse(pos, expr_list, expected))
return false;
auto res = std::make_shared<ASTFunction>();
@ -539,28 +626,57 @@ namespace
bool parsePosition(IParser::Pos & pos, ASTPtr & node, Expected & expected)
{
ASTPtr expr_list_node;
if (!ParserExpressionList(false, false).parse(pos, expr_list_node, expected))
return false;
/// First try to match with position(needle IN haystack)
/// Then with position(haystack, needle[, start_pos])
ASTExpressionList * expr_list = typeid_cast<ASTExpressionList *>(expr_list_node.get());
if (expr_list && expr_list->children.size() == 1)
ParserExpressionWithOptionalAlias expr_parser(true /*allow_alias_without_as_keyword*/);
ASTPtr first_arg_expr_node;
if (!expr_parser.parse(pos, first_arg_expr_node, expected))
{
ASTFunction * func_in = typeid_cast<ASTFunction *>(expr_list->children[0].get());
if (func_in && func_in->name == "in")
return false;
}
ASTFunction * func_in = typeid_cast<ASTFunction *>(first_arg_expr_node.get());
if (func_in && func_in->name == "in")
{
ASTExpressionList * in_args = typeid_cast<ASTExpressionList *>(func_in->arguments.get());
if (in_args && in_args->children.size() == 2)
{
ASTExpressionList * in_args = typeid_cast<ASTExpressionList *>(func_in->arguments.get());
if (in_args && in_args->children.size() == 2)
{
node = makeASTFunction("position", in_args->children[1], in_args->children[0]);
return true;
}
node = makeASTFunction("position", in_args->children[1], in_args->children[0]);
return true;
}
}
if (pos->type != TokenType::Comma)
return false;
++pos;
ASTPtr second_arg_expr_node;
if (!expr_parser.parse(pos, second_arg_expr_node, expected))
{
return false;
}
ASTPtr start_pos_expr_node;
if (pos->type == TokenType::Comma)
{
++pos;
if (!expr_parser.parse(pos, start_pos_expr_node, expected))
return false;
}
auto arguments = std::make_shared<ASTExpressionList>();
arguments->children.push_back(std::move(first_arg_expr_node));
arguments->children.push_back(std::move(second_arg_expr_node));
if (start_pos_expr_node)
arguments->children.push_back(std::move(start_pos_expr_node));
auto res = std::make_shared<ASTFunction>();
res->name = "position";
res->arguments = expr_list_node;
res->arguments = std::move(arguments);
res->children.push_back(res->arguments);
node = std::move(res);
return true;
@ -568,6 +684,9 @@ namespace
bool parseDateAdd(const char * function_name, IParser::Pos & pos, ASTPtr & node, Expected & expected)
{
/// First to match with function(unit, offset, timestamp)
/// Then with function(offset, timestamp)
ASTPtr timestamp_node;
ASTPtr offset_node;
@ -575,19 +694,18 @@ namespace
ASTPtr interval_func_node;
if (parseIntervalKind(pos, expected, interval_kind))
{
/// function(unit, offset, timestamp)
if (pos->type != TokenType::Comma)
return false;
++pos;
if (!ParserExpression().parse(pos, offset_node, expected))
if (!ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, offset_node, expected))
return false;
if (pos->type != TokenType::Comma)
return false;
++pos;
if (!ParserExpression().parse(pos, timestamp_node, expected))
if (!ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, timestamp_node, expected))
return false;
auto interval_expr_list_args = std::make_shared<ASTExpressionList>();
interval_expr_list_args->children = {offset_node};
@ -600,7 +718,7 @@ namespace
else
{
ASTPtr expr_list;
if (!ParserExpressionList(false, false).parse(pos, expr_list, expected))
if (!ParserExpressionList(true /*allow_alias_without_as_keyword*/).parse(pos, expr_list, expected))
return false;
auto res = std::make_shared<ASTFunction>();
@ -617,39 +735,59 @@ namespace
bool parseDateDiff(IParser::Pos & pos, ASTPtr & node, Expected & expected)
{
/// First to match with dateDiff(unit, startdate, enddate, [timezone])
/// Then with dateDiff('unit', startdate, enddate, [timezone])
ASTPtr left_node;
ASTPtr right_node;
IntervalKind interval_kind;
if (!parseIntervalKind(pos, expected, interval_kind))
if (parseIntervalKind(pos, expected, interval_kind))
{
ASTPtr expr_list;
if (!ParserExpressionList(false, false).parse(pos, expr_list, expected))
if (pos->type != TokenType::Comma)
return false;
++pos;
if (!ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, left_node, expected))
return false;
auto res = std::make_shared<ASTFunction>();
res->name = "dateDiff";
res->arguments = expr_list;
res->children.push_back(res->arguments);
node = std::move(res);
if (pos->type != TokenType::Comma)
return false;
++pos;
if (!ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, right_node, expected))
return false;
ASTPtr timezone_node;
if (pos->type == TokenType::Comma)
{
/// Optional timezone
++pos;
if (!ParserExpressionWithOptionalAlias(true /*allow_alias_without_as_keyword*/).parse(pos, timezone_node, expected))
return false;
}
auto interval_literal = std::make_shared<ASTLiteral>(interval_kind.toDateDiffUnit());
if (timezone_node)
node = makeASTFunction("dateDiff", std::move(interval_literal), std::move(left_node), std::move(right_node), std::move(timezone_node));
else
node = makeASTFunction("dateDiff", std::move(interval_literal), std::move(left_node), std::move(right_node));
return true;
}
if (pos->type != TokenType::Comma)
return false;
++pos;
if (!ParserExpression().parse(pos, left_node, expected))
ASTPtr expr_list;
if (!ParserExpressionList(true /*allow_alias_without_as_keyword*/).parse(pos, expr_list, expected))
return false;
if (pos->type != TokenType::Comma)
return false;
++pos;
auto res = std::make_shared<ASTFunction>();
res->name = "dateDiff";
res->arguments = expr_list;
res->children.push_back(res->arguments);
node = std::move(res);
if (!ParserExpression().parse(pos, right_node, expected))
return false;
node = makeASTFunction("dateDiff", std::make_shared<ASTLiteral>(interval_kind.toDateDiffUnit()), left_node, right_node);
return true;
}

View File

@ -12,14 +12,14 @@ echo 'SELECT '"$(perl -e 'print "CAST(" x 100')"'a, b'"$(perl -e 'print ")" x 10
echo 'SELECT '"$(perl -e 'print "CAST(" x 100')"'a AS b'"$(perl -e 'print ")" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo 'SELECT '"$(perl -e 'print "CAST(" x 100')"'1'"$(perl -e 'print ", '"'UInt8'"')" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000
echo 'SELECT '"$(perl -e 'print "CAST(" x 100')"'1'"$(perl -e 'print " AS UInt8)" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000
echo "SELECT fo,22222?LUTAY(SELECT(NOT CAUTAY(SELECT(NOT CAST(NOTT(NOT CAST(NOT NOT LEfT(NOT coARRAYlumnsFLuTAY(SELECT(NO0?LUTAY(SELECT(NOT CAUTAY(SELECT(NOT CAST(NOTT(NOT CAST(NOT NOT LEfT(NOT coARRAYlumnsFLuTAY(SELECT(NOTAYTAY(SELECT(NOTAYEFAULT(fo,22222?LUTAY(%SELECT(NOT CAST(NOT NOTAYTAY(SELECT(NOTAYEFAULT(fo,22222?LUTAY(SELECT(NOT CAST(NOT NOT (NOe)))))))))))))))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo "SELECT position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(a b))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo "SELECT position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(a, b))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'UNKNOWN_IDENTIFIER'
echo "SELECT position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(a, b, c))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'UNKNOWN_IDENTIFIER'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x'"$(perl -e 'print ")" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'UNKNOWN_IDENTIFIER'
echo "SELECT position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(a b))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo "SELECT position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(a, b))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo "SELECT position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(position(a, b, c))))))))))))))))))))" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x'"$(perl -e 'print ")" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x y'"$(perl -e 'print ")" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x IN y'"$(perl -e 'print ")" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'UNKNOWN_IDENTIFIER'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x IN y'"$(perl -e 'print ")" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'Syntax error'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x'"$(perl -e 'print " IN x)" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'UNKNOWN_IDENTIFIER'
echo 'SELECT '"$(perl -e 'print "position(" x 100')"'x'"$(perl -e 'print ", x)" x 100')" | ${CLICKHOUSE_LOCAL} --max_parser_depth 10000 2>&1 | grep -cF 'UNKNOWN_IDENTIFIER'

View File

@ -0,0 +1,60 @@
1234
1234 1234
1234 1234
1234 1234
1234 1234
1234 1234
1234
1234 1234
1234 1234
1234 UInt32
1234 UInt32
1234 1234 UInt32
1234 1234 UInt32
234
234 1234
234 1234
234 2
234 2
234 1234 2
234 1234 2
234 1234 2
23
23 2
23 2
234 1234 2 3
234 1234 2 3
bca a abca
bca a abca
bca abca
bca abca
bca a
bca a
bca
abc a abca
abc a abca
bc a abca
bc a abca
5 2019-05-05
5 2019-05-05
123 1234 123
123 1234 123
1 123 1234
0 123 1234
0 123 1234
2019-05-06 1 2019-05-05
2019-05-06 1 2019-05-05
2019-05-06 1 2019-05-05
2019-05-06 1 2019-05-05
2019-05-04 1 2019-05-05
2019-05-04 1 2019-05-05
2019-05-04 1 2019-05-05
2019-05-04 1 2019-05-05
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06
1 2019-05-05 2019-05-06

View File

@ -0,0 +1,121 @@
-- CAST expression
-- cast(expr [[AS] alias_1] AS Type)
SELECT cast('1234' AS UInt32);
SELECT cast('1234' AS lhs AS UInt32), lhs;
SELECT cast('1234' lhs AS UInt32), lhs;
SELECT cast(('1234' AS lhs) AS UInt32), lhs;
SELECT cast(('1234' AS lhs) rhs AS UInt32), rhs;
SELECT cast(('1234' AS lhs) AS rhs AS UInt32), rhs;
-- cast(expr [[AS] alias_1], type_expr [[as] alias_2])
SELECT cast('1234', 'UInt32');
SELECT cast('1234' AS lhs, 'UInt32'), lhs;
SELECT cast('1234' lhs, 'UInt32'), lhs;
SELECT cast('1234', 'UInt32' AS rhs), rhs;
SELECT cast('1234', 'UInt32' rhs), rhs;
SELECT cast('1234' AS lhs, 'UInt32' AS rhs), lhs, rhs;
SELECT cast('1234' lhs, 'UInt32' rhs), lhs, rhs;
-- SUBSTRING expression
-- SUBSTRING(expr FROM start)
SELECT substring('1234' FROM 2);
SELECT substring('1234' AS lhs FROM 2), lhs;
SELECT substring('1234' lhs FROM 2), lhs;
SELECT substring('1234' FROM 2 AS rhs), rhs;
SELECT substring('1234' FROM 2 rhs), rhs;
SELECT substring('1234' AS lhs FROM 2 AS rhs), lhs, rhs;
SELECT substring('1234' lhs FROM 2 rhs), lhs, rhs;
SELECT substring(('1234' AS lhs) FROM (2 AS rhs)), lhs, rhs;
-- SUBSTRING(expr FROM start FOR length)
SELECT substring('1234' FROM 2 FOR 2);
SELECT substring('1234' FROM 2 FOR 2 AS lhs), lhs;
SELECT substring('1234' FROM 2 FOR 2 lhs), lhs;
-- SUBSTRING(expr, start, length)
SELECT substring('1234' AS arg_1, 2 AS arg_2, 3 AS arg_3), arg_1, arg_2, arg_3;
SELECT substring('1234' arg_1, 2 arg_2, 3 arg_3), arg_1, arg_2, arg_3;
-- -- TRIM expression ([[LEADING|TRAILING|BOTH] trim_character FROM] input_string)
SELECT trim(LEADING 'a' AS arg_1 FROM 'abca' AS arg_2), arg_1, arg_2;
SELECT trim(LEADING 'a' arg_1 FROM 'abca' arg_2), arg_1, arg_2;
SELECT trim(LEADING 'a' FROM 'abca' AS arg_2), arg_2;
SELECT trim(LEADING 'a' FROM 'abca' arg_2), arg_2;
SELECT trim(LEADING 'a' AS arg_1 FROM 'abca'), arg_1;
SELECT trim(LEADING 'a' arg_1 FROM 'abca'), arg_1;
SELECT trim(LEADING 'a' FROM 'abca');
SELECT trim(TRAILING 'a' AS arg_1 FROM 'abca' AS arg_2), arg_1, arg_2;
SELECT trim(TRAILING 'a' arg_1 FROM 'abca' arg_2), arg_1, arg_2;
SELECT trim(BOTH 'a' AS arg_1 FROM 'abca' AS arg_2), arg_1, arg_2;
SELECT trim(BOTH 'a' arg_1 FROM 'abca' arg_2), arg_1, arg_2;
-- EXTRACT expression
-- EXTRACT(part FROM date)
SELECT EXTRACT(DAY FROM toDate('2019-05-05') as arg_1), arg_1;
SELECT EXTRACT(DAY FROM toDate('2019-05-05') arg_1), arg_1;
-- Function extract(haystack, pattern)
SELECT extract('1234' AS arg_1, '123' AS arg_2), arg_1, arg_2;
SELECT extract('1234' arg_1, '123' arg_2), arg_1, arg_2;
-- POSITION expression
-- position(needle IN haystack)
SELECT position(('123' AS arg_1) IN ('1234' AS arg_2)), arg_1, arg_2;
-- position(haystack, needle[, start_pos])
SELECT position('123' AS arg_1, '1234' AS arg_2), arg_1, arg_2;
SELECT position('123' arg_1, '1234' arg_2), arg_1, arg_2;
-- dateAdd, dateSub expressions
-- function(unit, offset, timestamp)
SELECT dateAdd(DAY, 1 AS arg_1, toDate('2019-05-05') AS arg_2), arg_1, arg_2;
SELECT dateAdd(DAY, 1 arg_1, toDate('2019-05-05') arg_2), arg_1, arg_2;
-- function(offset, timestamp)
SELECT dateAdd(DAY, 1 AS arg_1, toDate('2019-05-05') AS arg_2), arg_1, arg_2;
SELECT dateAdd(DAY, 1 arg_1, toDate('2019-05-05') arg_2), arg_1, arg_2;
-- function(unit, offset, timestamp)
SELECT dateSub(DAY, 1 AS arg_1, toDate('2019-05-05') AS arg_2), arg_1, arg_2;
SELECT dateSub(DAY, 1 arg_1, toDate('2019-05-05') arg_2), arg_1, arg_2;
-- function(offset, timestamp)
SELECT dateSub(DAY, 1 AS arg_1, toDate('2019-05-05') AS arg_2), arg_1, arg_2;
SELECT dateSub(DAY, 1 arg_1, toDate('2019-05-05') arg_2), arg_1, arg_2;
-- dateDiff expression
-- dateDiff(unit, startdate, enddate, [timezone])
SELECT dateDiff(DAY, toDate('2019-05-05') AS arg_1, toDate('2019-05-06') AS arg_2), arg_1, arg_2;
SELECT dateDiff(DAY, toDate('2019-05-05') arg_1, toDate('2019-05-06') arg_2), arg_1, arg_2;
SELECT dateDiff(DAY, toDate('2019-05-05') AS arg_1, toDate('2019-05-06') AS arg_2, 'UTC'), arg_1, arg_2;
SELECT dateDiff(DAY, toDate('2019-05-05') arg_1, toDate('2019-05-06') arg_2, 'UTC'), arg_1, arg_2;
-- dateDiff('unit', startdate, enddate, [timezone])
SELECT dateDiff('DAY', toDate('2019-05-05') AS arg_1, toDate('2019-05-06') AS arg_2), arg_1, arg_2;
SELECT dateDiff('DAY', toDate('2019-05-05') arg_1, toDate('2019-05-06') arg_2), arg_1, arg_2;
SELECT dateDiff('DAY', toDate('2019-05-05') AS arg_1, toDate('2019-05-06') AS arg_2, 'UTC'), arg_1, arg_2;
SELECT dateDiff('DAY', toDate('2019-05-05') arg_1, toDate('2019-05-06') arg_2, 'UTC'), arg_1, arg_2;