mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Query parameter type : Identifier
This commit is contained in:
parent
30325689c4
commit
4cfae808fa
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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>())
|
||||||
{
|
{
|
||||||
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>())
|
: 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
|
||||||
{
|
{
|
||||||
assert(!name_parts.empty());
|
if (children.empty())
|
||||||
assert(!full_name.empty());
|
{
|
||||||
|
assert(!name_parts.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 << '.';
|
||||||
|
|
||||||
format_element(name_parts[i]);
|
if (name_parts[i].empty())
|
||||||
|
children[j++]->formatImpl(settings, state, frame);
|
||||||
|
else
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()); }
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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}.
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
||||||
|
@ -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