mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-04 13:32:13 +00:00
Don't require manual string serialisation to set query parameters
This commit is contained in:
parent
3d9d1369b1
commit
fe787aa04e
@ -1029,7 +1029,6 @@ bool ParserCollectionOfLiterals<Collection>::parseImpl(Pos & pos, ASTPtr & node,
|
|||||||
|
|
||||||
template bool ParserCollectionOfLiterals<Array>::parseImpl(Pos & pos, ASTPtr & node, Expected & expected);
|
template bool ParserCollectionOfLiterals<Array>::parseImpl(Pos & pos, ASTPtr & node, Expected & expected);
|
||||||
template bool ParserCollectionOfLiterals<Tuple>::parseImpl(Pos & pos, ASTPtr & node, Expected & expected);
|
template bool ParserCollectionOfLiterals<Tuple>::parseImpl(Pos & pos, ASTPtr & node, Expected & expected);
|
||||||
template bool ParserCollectionOfLiterals<Map>::parseImpl(Pos & pos, ASTPtr & node, Expected & expected);
|
|
||||||
|
|
||||||
bool ParserLiteral::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
bool ParserLiteral::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <Parsers/CommonParsers.h>
|
#include <Parsers/CommonParsers.h>
|
||||||
#include <Parsers/ParserSetQuery.h>
|
#include <Parsers/ParserSetQuery.h>
|
||||||
|
#include <Parsers/ExpressionElementParsers.h>
|
||||||
|
|
||||||
#include <Core/Names.h>
|
#include <Core/Names.h>
|
||||||
#include <IO/ReadBufferFromString.h>
|
#include <IO/ReadBufferFromString.h>
|
||||||
@ -20,22 +21,6 @@ namespace ErrorCodes
|
|||||||
extern const int BAD_ARGUMENTS;
|
extern const int BAD_ARGUMENTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static NameToNameMap::value_type convertToQueryParameter(SettingChange change)
|
|
||||||
{
|
|
||||||
auto name = change.name.substr(strlen(QUERY_PARAMETER_NAME_PREFIX));
|
|
||||||
if (name.empty())
|
|
||||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Parameter name cannot be empty");
|
|
||||||
|
|
||||||
auto value = applyVisitor(FieldVisitorToString(), change.value);
|
|
||||||
/// writeQuoted is not always quoted in line with SQL standard https://github.com/ClickHouse/ClickHouse/blob/master/src/IO/WriteHelpers.h
|
|
||||||
if (value.starts_with('\''))
|
|
||||||
{
|
|
||||||
ReadBufferFromOwnString buf(value);
|
|
||||||
readQuoted(value, buf);
|
|
||||||
}
|
|
||||||
return {name, value};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ParserLiteralOrMap : public IParserBase
|
class ParserLiteralOrMap : public IParserBase
|
||||||
{
|
{
|
||||||
@ -89,6 +74,116 @@ protected:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Parse `param_name = value`
|
||||||
|
/// Compared to the usual setting, parameter can be
|
||||||
|
/// Literal, Array and Tuple of literals, Identifier, Map
|
||||||
|
bool ParserSetQuery::parseNameValuePairParameter(ParserSetQuery::Parameter & change, IParser::Pos & pos, Expected & expected)
|
||||||
|
{
|
||||||
|
ParserLiteral literal_p;
|
||||||
|
ParserArrayOfLiterals array_p;
|
||||||
|
ParserTupleOfLiterals tuple_p;
|
||||||
|
ParserCompoundIdentifier identifier_p;
|
||||||
|
ParserToken s_eq(TokenType::Equals);
|
||||||
|
|
||||||
|
ASTPtr name, value;
|
||||||
|
String name_str, value_str;
|
||||||
|
|
||||||
|
if (!identifier_p.parse(pos, name, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!s_eq.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tryGetIdentifierNameInto(name, name_str);
|
||||||
|
|
||||||
|
if (!name_str.starts_with(QUERY_PARAMETER_NAME_PREFIX))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
name_str = name_str.substr(strlen(QUERY_PARAMETER_NAME_PREFIX));
|
||||||
|
if (name_str.empty())
|
||||||
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Parameter name cannot be empty");
|
||||||
|
|
||||||
|
if (literal_p.parse(pos, value, expected)
|
||||||
|
|| array_p.parse(pos, value, expected)
|
||||||
|
|| tuple_p.parse(pos, value, expected))
|
||||||
|
{
|
||||||
|
|
||||||
|
value_str = applyVisitor(FieldVisitorToString(), value->as<ASTLiteral>()->value);
|
||||||
|
|
||||||
|
/// writeQuoted is not always quoted in line with SQL standard https://github.com/ClickHouse/ClickHouse/blob/master/src/IO/WriteHelpers.h
|
||||||
|
if (value_str.starts_with('\''))
|
||||||
|
{
|
||||||
|
ReadBufferFromOwnString buf(value_str);
|
||||||
|
readQuoted(value_str, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (identifier_p.parse(pos, value, expected))
|
||||||
|
{
|
||||||
|
tryGetIdentifierNameInto(value, value_str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParserToken l_br_p(TokenType::OpeningCurlyBrace);
|
||||||
|
ParserToken r_br_p(TokenType::ClosingCurlyBrace);
|
||||||
|
ParserToken comma_p(TokenType::Comma);
|
||||||
|
ParserToken colon_p(TokenType::Colon);
|
||||||
|
|
||||||
|
if (!l_br_p.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int depth = 1;
|
||||||
|
|
||||||
|
value_str = '{';
|
||||||
|
|
||||||
|
while (depth > 0)
|
||||||
|
{
|
||||||
|
if (r_br_p.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
value_str += '}';
|
||||||
|
--depth;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value_str.back() != '{')
|
||||||
|
{
|
||||||
|
if (!comma_p.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value_str += ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTPtr key;
|
||||||
|
ASTPtr val;
|
||||||
|
|
||||||
|
if (!literal_p.parse(pos, key, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!colon_p.ignore(pos, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value_str += applyVisitor(FieldVisitorToString(), key->as<ASTLiteral>()->value);
|
||||||
|
value_str += ":";
|
||||||
|
|
||||||
|
if (l_br_p.ignore(pos, expected))
|
||||||
|
{
|
||||||
|
value_str += '{';
|
||||||
|
++depth;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!literal_p.parse(pos, val, expected)
|
||||||
|
&& !array_p.parse(pos, val, expected)
|
||||||
|
&& !tuple_p.parse(pos, val, expected))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value_str += applyVisitor(FieldVisitorToString(), val->as<ASTLiteral>()->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
change = {std::move(name_str), std::move(value_str)};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse `name = value`.
|
/// Parse `name = value`.
|
||||||
bool ParserSetQuery::parseNameValuePair(SettingChange & change, IParser::Pos & pos, Expected & expected)
|
bool ParserSetQuery::parseNameValuePair(SettingChange & change, IParser::Pos & pos, Expected & expected)
|
||||||
{
|
{
|
||||||
@ -143,16 +238,22 @@ bool ParserSetQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|||||||
if ((!changes.empty() || !query_parameters.empty()) && !s_comma.ignore(pos))
|
if ((!changes.empty() || !query_parameters.empty()) && !s_comma.ignore(pos))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/// Either a setting or a parameter for prepared statement (if name starts with QUERY_PARAMETER_NAME_PREFIX)
|
auto old_pos = pos;
|
||||||
SettingChange current;
|
Parameter parameter;
|
||||||
|
|
||||||
if (!parseNameValuePair(current, pos, expected))
|
if (parseNameValuePairParameter(parameter, pos, expected))
|
||||||
return false;
|
{
|
||||||
|
query_parameters.emplace(std::move(parameter));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (current.name.starts_with(QUERY_PARAMETER_NAME_PREFIX))
|
SettingChange setting;
|
||||||
query_parameters.emplace(convertToQueryParameter(std::move(current)));
|
pos = old_pos;
|
||||||
|
|
||||||
|
if (parseNameValuePair(setting, pos, expected))
|
||||||
|
changes.push_back(std::move(setting));
|
||||||
else
|
else
|
||||||
changes.push_back(std::move(current));
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto query = std::make_shared<ASTSetQuery>();
|
auto query = std::make_shared<ASTSetQuery>();
|
||||||
|
@ -17,8 +17,12 @@ constexpr char QUERY_PARAMETER_NAME_PREFIX[] = "param_";
|
|||||||
class ParserSetQuery : public IParserBase
|
class ParserSetQuery : public IParserBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using Parameter = std::pair<std::string, std::string>;
|
||||||
|
|
||||||
explicit ParserSetQuery(bool parse_only_internals_ = false) : parse_only_internals(parse_only_internals_) {}
|
explicit ParserSetQuery(bool parse_only_internals_ = false) : parse_only_internals(parse_only_internals_) {}
|
||||||
|
|
||||||
static bool parseNameValuePair(SettingChange & change, IParser::Pos & pos, Expected & expected);
|
static bool parseNameValuePair(SettingChange & change, IParser::Pos & pos, Expected & expected);
|
||||||
|
static bool parseNameValuePairParameter(Parameter & change, IParser::Pos & pos, Expected & expected);
|
||||||
protected:
|
protected:
|
||||||
const char * getName() const override { return "SET query"; }
|
const char * getName() const override { return "SET query"; }
|
||||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||||
|
@ -13,4 +13,10 @@ _CAST(((\'abc\', 22), (\'def\', 33)), \'Map(String, UInt8)\') Map(String, UInt8)
|
|||||||
_CAST([[4, 5, 6], [7], [8, 9]], \'Array(Array(UInt8))\') Array(Array(UInt8))
|
_CAST([[4, 5, 6], [7], [8, 9]], \'Array(Array(UInt8))\') Array(Array(UInt8))
|
||||||
_CAST(((10, [11, 12]), (13, [14, 15])), \'Map(UInt8, Array(UInt8))\') Map(UInt8, Array(UInt8))
|
_CAST(((10, [11, 12]), (13, [14, 15])), \'Map(UInt8, Array(UInt8))\') Map(UInt8, Array(UInt8))
|
||||||
_CAST(((\'ghj\', ((\'klm\', [16, 17]))), (\'nop\', ((\'rst\', [18])))), \'Map(String, Map(String, Array(UInt8)))\') Map(String, Map(String, Array(UInt8)))
|
_CAST(((\'ghj\', ((\'klm\', [16, 17]))), (\'nop\', ((\'rst\', [18])))), \'Map(String, Map(String, Array(UInt8)))\') Map(String, Map(String, Array(UInt8)))
|
||||||
|
_CAST(42, \'Int64\') Int64
|
||||||
|
_CAST([1, 2, 3], \'Array(UInt8)\') Array(UInt8)
|
||||||
|
_CAST(((\'abc\', 22), (\'def\', 33)), \'Map(String, UInt8)\') Map(String, UInt8)
|
||||||
|
_CAST([[4, 5, 6], [7], [8, 9]], \'Array(Array(UInt8))\') Array(Array(UInt8))
|
||||||
|
_CAST(((10, [11, 12]), (13, [14, 15])), \'Map(UInt8, Array(UInt8))\') Map(UInt8, Array(UInt8))
|
||||||
|
_CAST(((\'ghj\', ((\'klm\', [16, 17]))), (\'nop\', ((\'rst\', [18])))), \'Map(String, Map(String, Array(UInt8)))\') Map(String, Map(String, Array(UInt8)))
|
||||||
a Int8
|
a Int8
|
||||||
|
@ -91,4 +91,15 @@ $CLICKHOUSE_CLIENT \
|
|||||||
--param_map_map_arr="{'ghj': {'klm': [16, 17]}, 'nop': {'rst': [18]}}" \
|
--param_map_map_arr="{'ghj': {'klm': [16, 17]}, 'nop': {'rst': [18]}}" \
|
||||||
-q "describe table(select {id: Int64}, {arr: Array(UInt8)}, {map: Map(String, UInt8)}, {mul_arr: Array(Array(UInt8))}, {map_arr: Map(UInt8, Array(UInt8))}, {map_map_arr: Map(String, Map(String, Array(UInt8)))})"
|
-q "describe table(select {id: Int64}, {arr: Array(UInt8)}, {map: Map(String, UInt8)}, {mul_arr: Array(Array(UInt8))}, {map_arr: Map(UInt8, Array(UInt8))}, {map_map_arr: Map(String, Map(String, Array(UInt8)))})"
|
||||||
|
|
||||||
|
# parameters can be set without manual string serialisation
|
||||||
|
$CLICKHOUSE_CLIENT -n -q "
|
||||||
|
set param_id = 42;
|
||||||
|
set param_arr = [1, 2, 3];
|
||||||
|
set param_map = {'abc': 22, 'def': 33};
|
||||||
|
set param_mul_arr = [[4, 5, 6], [7], [8, 9]];
|
||||||
|
set param_map_arr = {10: [11, 12], 13: [14, 15]};
|
||||||
|
set param_map_map_arr = {'ghj': {'klm': [16, 17]}, 'nop': {'rst': [18]}};
|
||||||
|
describe table(select {id: Int64}, {arr: Array(UInt8)}, {map: Map(String, UInt8)}, {mul_arr: Array(Array(UInt8))}, {map_arr: Map(UInt8, Array(UInt8))}, {map_map_arr: Map(String, Map(String, Array(UInt8)))});"
|
||||||
|
|
||||||
|
|
||||||
$CLICKHOUSE_CLIENT --param_p=42 -q "describe table (select * from (select {p:Int8} as a group by a) order by a)"
|
$CLICKHOUSE_CLIENT --param_p=42 -q "describe table (select * from (select {p:Int8} as a group by a) order by a)"
|
||||||
|
Loading…
Reference in New Issue
Block a user