mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 07:01:59 +00:00
Query parameter type : Identifier
This commit is contained in:
parent
30325689c4
commit
4cfae808fa
@ -5,8 +5,10 @@
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
#include <Formats/FormatSettings.h>
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTQueryParameter.h>
|
||||
#include <Interpreters/IdentifierSemantic.h>
|
||||
#include <Interpreters/ReplaceQueryParameterVisitor.h>
|
||||
#include <Interpreters/addTypeConversionToAST.h>
|
||||
|
||||
@ -25,6 +27,8 @@ void ReplaceQueryParameterVisitor::visit(ASTPtr & ast)
|
||||
{
|
||||
if (ast->as<ASTQueryParameter>())
|
||||
visitQueryParameter(ast);
|
||||
else if (ast->as<ASTIdentifier>())
|
||||
visitIdentifier(ast);
|
||||
else
|
||||
visitChildren(ast);
|
||||
}
|
||||
@ -71,4 +75,27 @@ void ReplaceQueryParameterVisitor::visitQueryParameter(ASTPtr & ast)
|
||||
ast->setAlias(alias);
|
||||
}
|
||||
|
||||
void ReplaceQueryParameterVisitor::visitIdentifier(ASTPtr & ast)
|
||||
{
|
||||
auto & ast_identifier = ast->as<ASTIdentifier &>();
|
||||
if (ast_identifier.children.empty())
|
||||
return;
|
||||
|
||||
auto & name_parts = ast_identifier.name_parts;
|
||||
for (size_t i = 0, j = 0, size = name_parts.size(); i < size; ++i)
|
||||
{
|
||||
if (name_parts[i].empty())
|
||||
{
|
||||
const auto & ast_param = ast_identifier.children[j++]->as<ASTQueryParameter &>();
|
||||
name_parts[i] = getParamValue(ast_param.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_identifier.semantic->special && name_parts.size() >= 2)
|
||||
ast_identifier.semantic->table = ast_identifier.name_parts.end()[-2];
|
||||
|
||||
ast_identifier.resetFullName();
|
||||
ast_identifier.children.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ namespace DB
|
||||
class ASTQueryParameter;
|
||||
|
||||
/// Visit substitutions in a query, replace ASTQueryParameter with ASTLiteral.
|
||||
/// Rebuild ASTIdentifiers if some parts are ASTQueryParameter.
|
||||
class ReplaceQueryParameterVisitor
|
||||
{
|
||||
public:
|
||||
@ -21,6 +22,7 @@ public:
|
||||
private:
|
||||
const NameToNameMap & query_parameters;
|
||||
const String & getParamValue(const String & name);
|
||||
void visitIdentifier(ASTPtr & ast);
|
||||
void visitQueryParameter(ASTPtr & ast);
|
||||
void visitChildren(ASTPtr & ast);
|
||||
};
|
||||
|
@ -16,26 +16,48 @@ namespace ErrorCodes
|
||||
extern const int SYNTAX_ERROR;
|
||||
}
|
||||
|
||||
ASTIdentifier::ASTIdentifier(const String & short_name)
|
||||
ASTIdentifier::ASTIdentifier(const String & short_name, ASTPtr && name_param)
|
||||
: full_name(short_name), name_parts{short_name}, semantic(std::make_shared<IdentifierSemanticImpl>())
|
||||
{
|
||||
assert(!full_name.empty());
|
||||
if (name_param == nullptr)
|
||||
assert(!full_name.empty());
|
||||
else
|
||||
children.push_back(std::move(name_param));
|
||||
}
|
||||
|
||||
ASTIdentifier::ASTIdentifier(std::vector<String> && name_parts_, bool special)
|
||||
ASTIdentifier::ASTIdentifier(std::vector<String> && name_parts_, bool special, std::vector<ASTPtr> && name_params)
|
||||
: name_parts(name_parts_), semantic(std::make_shared<IdentifierSemanticImpl>())
|
||||
{
|
||||
assert(!name_parts.empty());
|
||||
for (const auto & part [[maybe_unused]] : name_parts)
|
||||
assert(!part.empty());
|
||||
|
||||
semantic->special = special;
|
||||
semantic->legacy_compound = true;
|
||||
if (!name_params.empty())
|
||||
{
|
||||
size_t params = 0;
|
||||
for (const auto & part [[maybe_unused]] : name_parts)
|
||||
{
|
||||
if (part.empty())
|
||||
++params;
|
||||
}
|
||||
assert(params == name_params.size());
|
||||
children = std::move(name_params);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto & part [[maybe_unused]] : name_parts)
|
||||
assert(!part.empty());
|
||||
|
||||
if (!special && name_parts.size() >= 2)
|
||||
semantic->table = name_parts.end()[-2];
|
||||
if (!special && name_parts.size() >= 2)
|
||||
semantic->table = name_parts.end()[-2];
|
||||
|
||||
resetFullName();
|
||||
resetFullName();
|
||||
}
|
||||
}
|
||||
|
||||
ASTPtr ASTIdentifier::getParam() const
|
||||
{
|
||||
assert(full_name.empty() && children.size() == 1);
|
||||
return children.front()->clone();
|
||||
}
|
||||
|
||||
ASTPtr ASTIdentifier::clone() const
|
||||
@ -64,13 +86,16 @@ void ASTIdentifier::setShortName(const String & new_name)
|
||||
|
||||
const String & ASTIdentifier::name() const
|
||||
{
|
||||
assert(!name_parts.empty());
|
||||
assert(!full_name.empty());
|
||||
if (children.empty())
|
||||
{
|
||||
assert(!name_parts.empty());
|
||||
assert(!full_name.empty());
|
||||
}
|
||||
|
||||
return full_name;
|
||||
}
|
||||
|
||||
void ASTIdentifier::formatImplWithoutAlias(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||
void ASTIdentifier::formatImplWithoutAlias(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
|
||||
{
|
||||
auto format_element = [&](const String & elem_name)
|
||||
{
|
||||
@ -82,17 +107,24 @@ void ASTIdentifier::formatImplWithoutAlias(const FormatSettings & settings, Form
|
||||
/// It could be compound but short
|
||||
if (!isShort())
|
||||
{
|
||||
for (size_t i = 0, size = name_parts.size(); i < size; ++i)
|
||||
for (size_t i = 0, j = 0, size = name_parts.size(); i < size; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
settings.ostr << '.';
|
||||
|
||||
format_element(name_parts[i]);
|
||||
if (name_parts[i].empty())
|
||||
children[j++]->formatImpl(settings, state, frame);
|
||||
else
|
||||
format_element(name_parts[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
format_element(shortName());
|
||||
const auto & name = shortName();
|
||||
if (name.empty())
|
||||
children.front()->formatImpl(settings, state, frame);
|
||||
else
|
||||
format_element(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <Parsers/ASTQueryParameter.h>
|
||||
#include <Parsers/ASTWithAlias.h>
|
||||
#include <Core/UUID.h>
|
||||
|
||||
@ -17,15 +18,19 @@ struct StorageID;
|
||||
/// Identifier (column, table or alias)
|
||||
class ASTIdentifier : public ASTWithAlias
|
||||
{
|
||||
friend class ReplaceQueryParameterVisitor;
|
||||
public:
|
||||
UUID uuid = UUIDHelpers::Nil;
|
||||
|
||||
explicit ASTIdentifier(const String & short_name);
|
||||
explicit ASTIdentifier(std::vector<String> && name_parts, bool special = false);
|
||||
explicit ASTIdentifier(const String & short_name, ASTPtr && name_param = {});
|
||||
explicit ASTIdentifier(std::vector<String> && name_parts, bool special = false, std::vector<ASTPtr> && name_params = {});
|
||||
|
||||
/** Get the text that identifies this element. */
|
||||
String getID(char delim) const override { return "Identifier" + (delim + name()); }
|
||||
|
||||
/** Get the query param out of a non-compound identifier. */
|
||||
ASTPtr getParam() const;
|
||||
|
||||
ASTPtr clone() const override;
|
||||
|
||||
void collectIdentifierNames(IdentifierNameSet & set) const override { set.insert(name()); }
|
||||
|
@ -146,7 +146,7 @@ bool ParserSubquery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
}
|
||||
|
||||
|
||||
bool ParserIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected &)
|
||||
bool ParserIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
/// Identifier in backquotes or in double quotes
|
||||
if (pos->type == TokenType::QuotedIdentifier)
|
||||
@ -172,7 +172,51 @@ bool ParserIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected &)
|
||||
++pos;
|
||||
return true;
|
||||
}
|
||||
else if (allow_query_parameter && pos->type == TokenType::OpeningCurlyBrace)
|
||||
{
|
||||
++pos;
|
||||
if (pos->type != TokenType::BareWord)
|
||||
{
|
||||
expected.add(pos, "substitution name (identifier)");
|
||||
return false;
|
||||
}
|
||||
|
||||
String name(pos->begin, pos->end);
|
||||
++pos;
|
||||
|
||||
if (pos->type != TokenType::Colon)
|
||||
{
|
||||
expected.add(pos, "colon between name and type");
|
||||
return false;
|
||||
}
|
||||
|
||||
++pos;
|
||||
|
||||
if (pos->type != TokenType::BareWord)
|
||||
{
|
||||
expected.add(pos, "substitution type (identifier)");
|
||||
return false;
|
||||
}
|
||||
|
||||
String type(pos->begin, pos->end);
|
||||
++pos;
|
||||
|
||||
if (type != "Identifier")
|
||||
{
|
||||
expected.add(pos, "substitution type (identifier)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos->type != TokenType::ClosingCurlyBrace)
|
||||
{
|
||||
expected.add(pos, "closing curly brace");
|
||||
return false;
|
||||
}
|
||||
++pos;
|
||||
|
||||
node = std::make_shared<ASTIdentifier>("", std::make_shared<ASTQueryParameter>(name, type));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -180,14 +224,19 @@ bool ParserIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected &)
|
||||
bool ParserCompoundIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
ASTPtr id_list;
|
||||
if (!ParserList(std::make_unique<ParserIdentifier>(), std::make_unique<ParserToken>(TokenType::Dot), false)
|
||||
.parse(pos, id_list, expected))
|
||||
if (!ParserList(std::make_unique<ParserIdentifier>(allow_query_parameter), std::make_unique<ParserToken>(TokenType::Dot), false)
|
||||
.parse(pos, id_list, expected))
|
||||
return false;
|
||||
|
||||
std::vector<String> parts;
|
||||
std::vector<ASTPtr> params;
|
||||
const auto & list = id_list->as<ASTExpressionList &>();
|
||||
for (const auto & child : list.children)
|
||||
{
|
||||
parts.emplace_back(getIdentifierName(child));
|
||||
if (parts.back() == "")
|
||||
params.push_back(child->as<ASTIdentifier>()->getParam());
|
||||
}
|
||||
|
||||
ParserKeyword s_uuid("UUID");
|
||||
UUID uuid = UUIDHelpers::Nil;
|
||||
@ -201,7 +250,7 @@ bool ParserCompoundIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected & ex
|
||||
uuid = parseFromString<UUID>(ast_uuid->as<ASTLiteral>()->value.get<String>());
|
||||
}
|
||||
|
||||
node = std::make_shared<ASTIdentifier>(std::move(parts));
|
||||
node = std::make_shared<ASTIdentifier>(std::move(parts), false, std::move(params));
|
||||
node->as<ASTIdentifier>()->uuid = uuid;
|
||||
|
||||
return true;
|
||||
@ -1174,7 +1223,7 @@ bool ParserAlias::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
bool ParserColumnsMatcher::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
ParserKeyword columns("COLUMNS");
|
||||
ParserList columns_p(std::make_unique<ParserCompoundIdentifier>(), std::make_unique<ParserToken>(TokenType::Comma), false);
|
||||
ParserList columns_p(std::make_unique<ParserCompoundIdentifier>(true), std::make_unique<ParserToken>(TokenType::Comma), false);
|
||||
ParserStringLiteral regex;
|
||||
|
||||
if (!columns.ignore(pos, expected))
|
||||
@ -1252,7 +1301,7 @@ bool ParserColumnsTransformers::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
||||
auto parse_id = [&identifiers, &pos, &expected]
|
||||
{
|
||||
ASTPtr identifier;
|
||||
if (!ParserIdentifier().parse(pos, identifier, expected))
|
||||
if (!ParserIdentifier(true).parse(pos, identifier, expected))
|
||||
return false;
|
||||
|
||||
identifiers.emplace_back(std::move(identifier));
|
||||
@ -1338,7 +1387,7 @@ bool ParserAsterisk::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
|
||||
bool ParserQualifiedAsterisk::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
if (!ParserCompoundIdentifier().parse(pos, node, expected))
|
||||
if (!ParserCompoundIdentifier(false, true).parse(pos, node, expected))
|
||||
return false;
|
||||
|
||||
if (pos->type != TokenType::Dot)
|
||||
@ -1475,7 +1524,7 @@ bool ParserExpressionElement::parseImpl(Pos & pos, ASTPtr & node, Expected & exp
|
||||
|| ParserFunction().parse(pos, node, expected)
|
||||
|| ParserQualifiedAsterisk().parse(pos, node, expected)
|
||||
|| ParserAsterisk().parse(pos, node, expected)
|
||||
|| ParserCompoundIdentifier().parse(pos, node, expected)
|
||||
|| ParserCompoundIdentifier(false, true).parse(pos, node, expected)
|
||||
|| ParserSubstitution().parse(pos, node, expected)
|
||||
|| ParserMySQLGlobalVariable().parse(pos, node, expected);
|
||||
}
|
||||
|
@ -42,9 +42,12 @@ protected:
|
||||
*/
|
||||
class ParserIdentifier : public IParserBase
|
||||
{
|
||||
public:
|
||||
ParserIdentifier(bool allow_query_parameter_ = false) : allow_query_parameter(allow_query_parameter_) {}
|
||||
protected:
|
||||
const char * getName() const override { return "identifier"; }
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
bool allow_query_parameter;
|
||||
};
|
||||
|
||||
|
||||
@ -54,12 +57,16 @@ protected:
|
||||
class ParserCompoundIdentifier : public IParserBase
|
||||
{
|
||||
public:
|
||||
ParserCompoundIdentifier(bool table_name_with_optional_uuid_ = false)
|
||||
: table_name_with_optional_uuid(table_name_with_optional_uuid_) {}
|
||||
ParserCompoundIdentifier(bool table_name_with_optional_uuid_ = false, bool allow_query_parameter_ = false)
|
||||
: table_name_with_optional_uuid(table_name_with_optional_uuid_), allow_query_parameter(allow_query_parameter_)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
const char * getName() const override { return "compound identifier"; }
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
bool table_name_with_optional_uuid;
|
||||
bool allow_query_parameter;
|
||||
};
|
||||
|
||||
/// Just *
|
||||
@ -299,6 +306,17 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/** Prepared statements.
|
||||
* Parse query with parameter expression {name:type}.
|
||||
*/
|
||||
class ParserIdentifierOrSubstitution : public IParserBase
|
||||
{
|
||||
protected:
|
||||
const char * getName() const override { return "identifier substitution"; }
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
};
|
||||
|
||||
|
||||
/** Prepared statements.
|
||||
* Parse query with parameter expression {name:type}.
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@ bool ParserTableExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
|
||||
if (!ParserWithOptionalAlias(std::make_unique<ParserSubquery>(), true).parse(pos, res->subquery, expected)
|
||||
&& !ParserWithOptionalAlias(std::make_unique<ParserFunction>(), true).parse(pos, res->table_function, expected)
|
||||
&& !ParserWithOptionalAlias(std::make_unique<ParserCompoundIdentifier>(), true).parse(pos, res->database_and_table_name, expected))
|
||||
&& !ParserWithOptionalAlias(std::make_unique<ParserCompoundIdentifier>(false, true), true).parse(pos, res->database_and_table_name, expected))
|
||||
return false;
|
||||
|
||||
/// FINAL
|
||||
|
@ -0,0 +1,5 @@
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
45
|
10
tests/queries/0_stateless/01550_query_identifier_parameters.sh
Executable file
10
tests/queries/0_stateless/01550_query_identifier_parameters.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
. "$CURDIR"/../shell_config.sh
|
||||
|
||||
$CLICKHOUSE_CLIENT --param_tbl 'numbers' --query 'select * from system.{tbl:Identifier} limit 1'
|
||||
$CLICKHOUSE_CLIENT --param_db 'system' --param_tbl 'numbers' --query 'select * from {db:Identifier}.{tbl:Identifier} limit 1'
|
||||
$CLICKHOUSE_CLIENT --param_col 'number' --query 'select {col:Identifier} from system.numbers limit 1'
|
||||
$CLICKHOUSE_CLIENT --param_col 'number' --query 'select a.{col:Identifier} from system.numbers a limit 1'
|
||||
$CLICKHOUSE_CLIENT --param_tbl 'numbers' --param_col 'number' --query 'select sum({tbl:Identifier}.{col:Identifier}) FROM (select * from system.{tbl:Identifier} limit 10) numbers'
|
Loading…
Reference in New Issue
Block a user