Query parameter type : Identifier

This commit is contained in:
Amos Bird 2020-11-02 18:03:52 +08:00
parent 30325689c4
commit 4cfae808fa
No known key found for this signature in database
GPG Key ID: 80D430DCBECFEDB4
9 changed files with 176 additions and 28 deletions

View File

@ -5,8 +5,10 @@
#include <DataTypes/DataTypeFactory.h> #include <DataTypes/DataTypeFactory.h>
#include <Formats/FormatSettings.h> #include <Formats/FormatSettings.h>
#include <IO/ReadBufferFromString.h> #include <IO/ReadBufferFromString.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTLiteral.h> #include <Parsers/ASTLiteral.h>
#include <Parsers/ASTQueryParameter.h> #include <Parsers/ASTQueryParameter.h>
#include <Interpreters/IdentifierSemantic.h>
#include <Interpreters/ReplaceQueryParameterVisitor.h> #include <Interpreters/ReplaceQueryParameterVisitor.h>
#include <Interpreters/addTypeConversionToAST.h> #include <Interpreters/addTypeConversionToAST.h>
@ -25,6 +27,8 @@ void ReplaceQueryParameterVisitor::visit(ASTPtr & ast)
{ {
if (ast->as<ASTQueryParameter>()) if (ast->as<ASTQueryParameter>())
visitQueryParameter(ast); visitQueryParameter(ast);
else if (ast->as<ASTIdentifier>())
visitIdentifier(ast);
else else
visitChildren(ast); visitChildren(ast);
} }
@ -71,4 +75,27 @@ void ReplaceQueryParameterVisitor::visitQueryParameter(ASTPtr & ast)
ast->setAlias(alias); 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();
}
} }

View File

@ -9,6 +9,7 @@ namespace DB
class ASTQueryParameter; class ASTQueryParameter;
/// Visit substitutions in a query, replace ASTQueryParameter with ASTLiteral. /// Visit substitutions in a query, replace ASTQueryParameter with ASTLiteral.
/// Rebuild ASTIdentifiers if some parts are ASTQueryParameter.
class ReplaceQueryParameterVisitor class ReplaceQueryParameterVisitor
{ {
public: public:
@ -21,6 +22,7 @@ public:
private: private:
const NameToNameMap & query_parameters; const NameToNameMap & query_parameters;
const String & getParamValue(const String & name); const String & getParamValue(const String & name);
void visitIdentifier(ASTPtr & ast);
void visitQueryParameter(ASTPtr & ast); void visitQueryParameter(ASTPtr & ast);
void visitChildren(ASTPtr & ast); void visitChildren(ASTPtr & ast);
}; };

View File

@ -16,26 +16,48 @@ namespace ErrorCodes
extern const int SYNTAX_ERROR; 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>()) : full_name(short_name), name_parts{short_name}, semantic(std::make_shared<IdentifierSemanticImpl>())
{ {
if (name_param == nullptr)
assert(!full_name.empty()); 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>()) : name_parts(name_parts_), semantic(std::make_shared<IdentifierSemanticImpl>())
{ {
assert(!name_parts.empty()); assert(!name_parts.empty());
for (const auto & part [[maybe_unused]] : name_parts)
assert(!part.empty());
semantic->special = special; semantic->special = special;
semantic->legacy_compound = true; 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) if (!special && name_parts.size() >= 2)
semantic->table = name_parts.end()[-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 ASTPtr ASTIdentifier::clone() const
@ -64,13 +86,16 @@ void ASTIdentifier::setShortName(const String & new_name)
const String & ASTIdentifier::name() const const String & ASTIdentifier::name() const
{ {
if (children.empty())
{
assert(!name_parts.empty()); assert(!name_parts.empty());
assert(!full_name.empty()); assert(!full_name.empty());
}
return full_name; 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) auto format_element = [&](const String & elem_name)
{ {
@ -82,17 +107,24 @@ void ASTIdentifier::formatImplWithoutAlias(const FormatSettings & settings, Form
/// It could be compound but short /// It could be compound but short
if (!isShort()) 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) if (i != 0)
settings.ostr << '.'; settings.ostr << '.';
if (name_parts[i].empty())
children[j++]->formatImpl(settings, state, frame);
else
format_element(name_parts[i]); format_element(name_parts[i]);
} }
} }
else else
{ {
format_element(shortName()); const auto & name = shortName();
if (name.empty())
children.front()->formatImpl(settings, state, frame);
else
format_element(name);
} }
} }

View File

@ -2,6 +2,7 @@
#include <optional> #include <optional>
#include <Parsers/ASTQueryParameter.h>
#include <Parsers/ASTWithAlias.h> #include <Parsers/ASTWithAlias.h>
#include <Core/UUID.h> #include <Core/UUID.h>
@ -17,15 +18,19 @@ struct StorageID;
/// Identifier (column, table or alias) /// Identifier (column, table or alias)
class ASTIdentifier : public ASTWithAlias class ASTIdentifier : public ASTWithAlias
{ {
friend class ReplaceQueryParameterVisitor;
public: public:
UUID uuid = UUIDHelpers::Nil; UUID uuid = UUIDHelpers::Nil;
explicit ASTIdentifier(const String & short_name); explicit ASTIdentifier(const String & short_name, ASTPtr && name_param = {});
explicit ASTIdentifier(std::vector<String> && name_parts, bool special = false); explicit ASTIdentifier(std::vector<String> && name_parts, bool special = false, std::vector<ASTPtr> && name_params = {});
/** Get the text that identifies this element. */ /** Get the text that identifies this element. */
String getID(char delim) const override { return "Identifier" + (delim + name()); } 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; ASTPtr clone() const override;
void collectIdentifierNames(IdentifierNameSet & set) const override { set.insert(name()); } void collectIdentifierNames(IdentifierNameSet & set) const override { set.insert(name()); }

View File

@ -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 /// Identifier in backquotes or in double quotes
if (pos->type == TokenType::QuotedIdentifier) if (pos->type == TokenType::QuotedIdentifier)
@ -172,7 +172,51 @@ bool ParserIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected &)
++pos; ++pos;
return true; 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; return false;
} }
@ -180,14 +224,19 @@ bool ParserIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected &)
bool ParserCompoundIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) bool ParserCompoundIdentifier::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{ {
ASTPtr id_list; ASTPtr id_list;
if (!ParserList(std::make_unique<ParserIdentifier>(), std::make_unique<ParserToken>(TokenType::Dot), false) if (!ParserList(std::make_unique<ParserIdentifier>(allow_query_parameter), std::make_unique<ParserToken>(TokenType::Dot), false)
.parse(pos, id_list, expected)) .parse(pos, id_list, expected))
return false; return false;
std::vector<String> parts; std::vector<String> parts;
std::vector<ASTPtr> params;
const auto & list = id_list->as<ASTExpressionList &>(); const auto & list = id_list->as<ASTExpressionList &>();
for (const auto & child : list.children) for (const auto & child : list.children)
{
parts.emplace_back(getIdentifierName(child)); parts.emplace_back(getIdentifierName(child));
if (parts.back() == "")
params.push_back(child->as<ASTIdentifier>()->getParam());
}
ParserKeyword s_uuid("UUID"); ParserKeyword s_uuid("UUID");
UUID uuid = UUIDHelpers::Nil; 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>()); 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; node->as<ASTIdentifier>()->uuid = uuid;
return true; return true;
@ -1174,7 +1223,7 @@ bool ParserAlias::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
bool ParserColumnsMatcher::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) bool ParserColumnsMatcher::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{ {
ParserKeyword columns("COLUMNS"); 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; ParserStringLiteral regex;
if (!columns.ignore(pos, expected)) if (!columns.ignore(pos, expected))
@ -1252,7 +1301,7 @@ bool ParserColumnsTransformers::parseImpl(Pos & pos, ASTPtr & node, Expected & e
auto parse_id = [&identifiers, &pos, &expected] auto parse_id = [&identifiers, &pos, &expected]
{ {
ASTPtr identifier; ASTPtr identifier;
if (!ParserIdentifier().parse(pos, identifier, expected)) if (!ParserIdentifier(true).parse(pos, identifier, expected))
return false; return false;
identifiers.emplace_back(std::move(identifier)); 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) 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; return false;
if (pos->type != TokenType::Dot) if (pos->type != TokenType::Dot)
@ -1475,7 +1524,7 @@ bool ParserExpressionElement::parseImpl(Pos & pos, ASTPtr & node, Expected & exp
|| ParserFunction().parse(pos, node, expected) || ParserFunction().parse(pos, node, expected)
|| ParserQualifiedAsterisk().parse(pos, node, expected) || ParserQualifiedAsterisk().parse(pos, node, expected)
|| ParserAsterisk().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) || ParserSubstitution().parse(pos, node, expected)
|| ParserMySQLGlobalVariable().parse(pos, node, expected); || ParserMySQLGlobalVariable().parse(pos, node, expected);
} }

View File

@ -42,9 +42,12 @@ protected:
*/ */
class ParserIdentifier : public IParserBase class ParserIdentifier : public IParserBase
{ {
public:
ParserIdentifier(bool allow_query_parameter_ = false) : allow_query_parameter(allow_query_parameter_) {}
protected: protected:
const char * getName() const override { return "identifier"; } const char * getName() const override { return "identifier"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
bool allow_query_parameter;
}; };
@ -54,12 +57,16 @@ protected:
class ParserCompoundIdentifier : public IParserBase class ParserCompoundIdentifier : public IParserBase
{ {
public: public:
ParserCompoundIdentifier(bool table_name_with_optional_uuid_ = false) ParserCompoundIdentifier(bool table_name_with_optional_uuid_ = false, bool allow_query_parameter_ = false)
: table_name_with_optional_uuid(table_name_with_optional_uuid_) {} : table_name_with_optional_uuid(table_name_with_optional_uuid_), allow_query_parameter(allow_query_parameter_)
{
}
protected: protected:
const char * getName() const override { return "compound identifier"; } const char * getName() const override { return "compound identifier"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
bool table_name_with_optional_uuid; bool table_name_with_optional_uuid;
bool allow_query_parameter;
}; };
/// Just * /// 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. /** Prepared statements.
* Parse query with parameter expression {name:type}. * Parse query with parameter expression {name:type}.
*/ */

View File

@ -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) 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<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; return false;
/// FINAL /// FINAL

View File

@ -0,0 +1,5 @@
0
0
0
0
45

View 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'