mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-03 13:02:00 +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<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)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <Parsers/CommonParsers.h>
|
||||
#include <Parsers/ParserSetQuery.h>
|
||||
#include <Parsers/ExpressionElementParsers.h>
|
||||
|
||||
#include <Core/Names.h>
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
@ -20,22 +21,6 @@ namespace ErrorCodes
|
||||
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
|
||||
{
|
||||
@ -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`.
|
||||
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))
|
||||
break;
|
||||
|
||||
/// Either a setting or a parameter for prepared statement (if name starts with QUERY_PARAMETER_NAME_PREFIX)
|
||||
SettingChange current;
|
||||
auto old_pos = pos;
|
||||
Parameter parameter;
|
||||
|
||||
if (!parseNameValuePair(current, pos, expected))
|
||||
return false;
|
||||
if (parseNameValuePairParameter(parameter, pos, expected))
|
||||
{
|
||||
query_parameters.emplace(std::move(parameter));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.name.starts_with(QUERY_PARAMETER_NAME_PREFIX))
|
||||
query_parameters.emplace(convertToQueryParameter(std::move(current)));
|
||||
SettingChange setting;
|
||||
pos = old_pos;
|
||||
|
||||
if (parseNameValuePair(setting, pos, expected))
|
||||
changes.push_back(std::move(setting));
|
||||
else
|
||||
changes.push_back(std::move(current));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto query = std::make_shared<ASTSetQuery>();
|
||||
|
@ -17,8 +17,12 @@ constexpr char QUERY_PARAMETER_NAME_PREFIX[] = "param_";
|
||||
class ParserSetQuery : public IParserBase
|
||||
{
|
||||
public:
|
||||
using Parameter = std::pair<std::string, std::string>;
|
||||
|
||||
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 parseNameValuePairParameter(Parameter & change, IParser::Pos & pos, Expected & expected);
|
||||
protected:
|
||||
const char * getName() const override { return "SET query"; }
|
||||
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(((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(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
|
||||
|
@ -91,4 +91,15 @@ $CLICKHOUSE_CLIENT \
|
||||
--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)))})"
|
||||
|
||||
# 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)"
|
||||
|
Loading…
Reference in New Issue
Block a user