mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 17:41:59 +00:00
Throw exception when multi-statements are disabled [#METR-22252].
This commit is contained in:
parent
a9eca34a14
commit
56a091cb6e
@ -64,6 +64,11 @@ inline bool isWordCharASCII(char c)
|
||||
|| c == '_';
|
||||
}
|
||||
|
||||
inline bool isWhitespaceASCII(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
||||
}
|
||||
|
||||
/// Works assuming isAlphaASCII.
|
||||
inline char toLowerIfAlphaASCII(char c)
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <DB/Core/Types.h>
|
||||
#include <DB/Common/Exception.h>
|
||||
#include <DB/Common/StringUtils.h>
|
||||
|
||||
#include <DB/IO/ReadBuffer.h>
|
||||
#include <DB/IO/VarInt.h>
|
||||
@ -805,12 +806,7 @@ void readText(std::vector<T> & x, ReadBuffer & buf)
|
||||
/// Пропустить пробельные символы.
|
||||
inline void skipWhitespaceIfAny(ReadBuffer & buf)
|
||||
{
|
||||
while (!buf.eof()
|
||||
&& (*buf.position() == ' '
|
||||
|| *buf.position() == '\t'
|
||||
|| *buf.position() == '\n'
|
||||
|| *buf.position() == '\r'
|
||||
|| *buf.position() == '\f'))
|
||||
while (!buf.eof() && isWhitespaceASCII(*buf.position()))
|
||||
++buf.position();
|
||||
}
|
||||
|
||||
|
@ -6,22 +6,24 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/// Распарсить запрос или записать сообщение об ошибке в out_error_message.
|
||||
/// Parse query or set 'out_error_message'.
|
||||
ASTPtr tryParseQuery(
|
||||
IParser & parser,
|
||||
IParser::Pos & pos, /// Сдвигается до конца распарсенного фрагмента.
|
||||
IParser::Pos & pos, /// Moved to end of parsed fragment.
|
||||
IParser::Pos end,
|
||||
std::string & out_error_message,
|
||||
bool hilite,
|
||||
const std::string & description);
|
||||
const std::string & description,
|
||||
bool allow_multi_statements); /// If false, check for non-space characters after semicolon and set error message if any.
|
||||
|
||||
|
||||
/// Распарсить запрос или кинуть исключение с сообщением об ошибке.
|
||||
/// Parse query or throw an exception with error message.
|
||||
ASTPtr parseQueryAndMovePosition(
|
||||
IParser & parser,
|
||||
IParser::Pos & pos, /// Сдвигается до конца распарсенного фрагмента.
|
||||
IParser::Pos & pos, /// Moved to end of parsed fragment.
|
||||
IParser::Pos end,
|
||||
const std::string & description);
|
||||
const std::string & description,
|
||||
bool allow_multi_statements);
|
||||
|
||||
|
||||
ASTPtr parseQuery(
|
||||
|
@ -549,7 +549,7 @@ private:
|
||||
while (begin < end)
|
||||
{
|
||||
const char * pos = begin;
|
||||
ASTPtr ast = parseQuery(pos, end);
|
||||
ASTPtr ast = parseQuery(pos, end, true);
|
||||
if (!ast)
|
||||
return true;
|
||||
|
||||
@ -607,7 +607,7 @@ private:
|
||||
if (!parsed_query)
|
||||
{
|
||||
const char * begin = query.data();
|
||||
parsed_query = parseQuery(begin, begin + query.size());
|
||||
parsed_query = parseQuery(begin, begin + query.size(), false);
|
||||
}
|
||||
|
||||
if (!parsed_query)
|
||||
@ -697,10 +697,10 @@ private:
|
||||
}
|
||||
|
||||
|
||||
/// Обработать запрос, который требует передачи блоков данных на сервер.
|
||||
/// Process query, which require sending blocks of data to the server.
|
||||
void processInsertQuery()
|
||||
{
|
||||
/// Отправляем часть запроса - без данных, так как данные будут отправлены отдельно.
|
||||
/// Send part of query without data, because data will be sent separately.
|
||||
const ASTInsertQuery & parsed_insert_query = typeid_cast<const ASTInsertQuery &>(*parsed_query);
|
||||
String query_without_data = parsed_insert_query.data
|
||||
? query.substr(0, parsed_insert_query.data - query.data())
|
||||
@ -712,19 +712,19 @@ private:
|
||||
connection->sendQuery(query_without_data, "", QueryProcessingStage::Complete, &context.getSettingsRef(), true);
|
||||
sendExternalTables();
|
||||
|
||||
/// Получаем структуру таблицы.
|
||||
/// Receive description of table structure.
|
||||
Block sample;
|
||||
if (receiveSampleBlock(sample))
|
||||
{
|
||||
/// Если была получена структура, т.е. сервер не выкинул исключения,
|
||||
/// отправляем эту структуру вместе с данными.
|
||||
/// If structure was received (thus, server has not thrown an exception),
|
||||
/// send this structure as structure for our data.
|
||||
sendData(sample);
|
||||
receivePacket();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ASTPtr parseQuery(IParser::Pos & pos, const char * end)
|
||||
ASTPtr parseQuery(IParser::Pos & pos, const char * end, bool allow_multi_statements)
|
||||
{
|
||||
ParserQuery parser;
|
||||
ASTPtr res;
|
||||
@ -732,7 +732,7 @@ private:
|
||||
if (is_interactive)
|
||||
{
|
||||
String message;
|
||||
res = tryParseQuery(parser, pos, end, message, true, "");
|
||||
res = tryParseQuery(parser, pos, end, message, true, "", allow_multi_statements);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
@ -741,7 +741,7 @@ private:
|
||||
}
|
||||
}
|
||||
else
|
||||
res = DB::parseQueryAndMovePosition(parser, pos, end, "");
|
||||
res = parseQueryAndMovePosition(parser, pos, end, "", allow_multi_statements);
|
||||
|
||||
if (is_interactive)
|
||||
{
|
||||
|
@ -1231,7 +1231,7 @@ bool parse(DB::ASTPtr & ast, const std::string & query)
|
||||
std::string message;
|
||||
auto begin = query.data();
|
||||
auto end = begin + query.size();
|
||||
ast = DB::tryParseQuery(parser, begin, end, message, false, "");
|
||||
ast = DB::tryParseQuery(parser, begin, end, message, false, "", false);
|
||||
return ast != nullptr;
|
||||
}
|
||||
|
||||
|
@ -239,7 +239,7 @@ bool parse(DB::ASTPtr & ast, const std::string & query)
|
||||
std::string message;
|
||||
auto begin = query.data();
|
||||
auto end = begin + query.size();
|
||||
ast = DB::tryParseQuery(parser, begin, end, message, false, "");
|
||||
ast = DB::tryParseQuery(parser, begin, end, message, false, "", false);
|
||||
return ast != nullptr;
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ namespace ErrorCodes
|
||||
}
|
||||
|
||||
|
||||
/** Из позиции в (возможно многострочном) запросе получить номер строки и номер столбца в строке.
|
||||
* Используется в сообщении об ошибках.
|
||||
/** From position in (possible multiline) query, get line number and column number in line.
|
||||
* Used in syntax error message.
|
||||
*/
|
||||
static std::pair<size_t, size_t> getLineAndCol(IParser::Pos begin, IParser::Pos pos)
|
||||
{
|
||||
@ -24,7 +24,7 @@ static std::pair<size_t, size_t> getLineAndCol(IParser::Pos begin, IParser::Pos
|
||||
begin = nl + 1;
|
||||
}
|
||||
|
||||
/// Нумеруются с единицы.
|
||||
/// Lines numbered from 1.
|
||||
return { line + 1, pos - begin + 1 };
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ static std::string getSyntaxErrorMessage(
|
||||
{
|
||||
message << ": failed at position " << (max_parsed_pos - begin + 1);
|
||||
|
||||
/// Если запрос многострочный.
|
||||
/// If query is multiline.
|
||||
IParser::Pos nl = reinterpret_cast<IParser::Pos>(memchr(begin, '\n', end - begin));
|
||||
if (nullptr != nl && nl + 1 != end)
|
||||
{
|
||||
@ -66,6 +66,7 @@ static std::string getSyntaxErrorMessage(
|
||||
message << " (line " << line << ", col " << col << ")";
|
||||
}
|
||||
|
||||
/// Hilite place of syntax error.
|
||||
if (hilite)
|
||||
{
|
||||
message << ":\n\n";
|
||||
@ -77,7 +78,7 @@ static std::string getSyntaxErrorMessage(
|
||||
&& static_cast<unsigned char>(max_parsed_pos[bytes_to_hilite]) <= 0xBF)
|
||||
++bytes_to_hilite;
|
||||
|
||||
message << "\033[41;1m" << std::string(max_parsed_pos, bytes_to_hilite) << "\033[0m"; /// Ярко-красный фон.
|
||||
message << "\033[41;1m" << std::string(max_parsed_pos, bytes_to_hilite) << "\033[0m"; /// Bright red background.
|
||||
message.write(max_parsed_pos + bytes_to_hilite, end - max_parsed_pos - bytes_to_hilite);
|
||||
message << "\n\n";
|
||||
|
||||
@ -103,7 +104,8 @@ ASTPtr tryParseQuery(
|
||||
IParser::Pos end,
|
||||
std::string & out_error_message,
|
||||
bool hilite,
|
||||
const std::string & description)
|
||||
const std::string & description,
|
||||
bool allow_multi_statements)
|
||||
{
|
||||
if (pos == end || *pos == ';')
|
||||
{
|
||||
@ -118,13 +120,28 @@ ASTPtr tryParseQuery(
|
||||
ASTPtr res;
|
||||
bool parse_res = parser.parse(pos, end, res, max_parsed_pos, expected);
|
||||
|
||||
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
|
||||
/// Parsed query must end with end of data or semicolon.
|
||||
if (!parse_res || (pos != end && *pos != ';'))
|
||||
{
|
||||
out_error_message = getSyntaxErrorMessage(begin, end, max_parsed_pos, expected, hilite, description);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// If multi-statements are not allowed, then after semicolon, there must be no non-space characters.
|
||||
if (!allow_multi_statements && pos < end && *pos == ';')
|
||||
{
|
||||
++pos;
|
||||
while (pos < end && isWhitespaceASCII(*pos))
|
||||
++pos;
|
||||
|
||||
if (pos < end)
|
||||
{
|
||||
out_error_message = getSyntaxErrorMessage(begin, end, pos, nullptr, hilite,
|
||||
(description.empty() ? std::string() : std::string(". ")) + "Multi-statements are not allowed");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -133,10 +150,11 @@ ASTPtr parseQueryAndMovePosition(
|
||||
IParser & parser,
|
||||
IParser::Pos & pos,
|
||||
IParser::Pos end,
|
||||
const std::string & description)
|
||||
const std::string & description,
|
||||
bool allow_multi_statements)
|
||||
{
|
||||
std::string error_message;
|
||||
ASTPtr res = tryParseQuery(parser, pos, end, error_message, false, description);
|
||||
ASTPtr res = tryParseQuery(parser, pos, end, error_message, false, description, allow_multi_statements);
|
||||
|
||||
if (res)
|
||||
return res;
|
||||
@ -152,7 +170,7 @@ ASTPtr parseQuery(
|
||||
const std::string & description)
|
||||
{
|
||||
auto pos = begin;
|
||||
return parseQueryAndMovePosition(parser, pos, end, description);
|
||||
return parseQueryAndMovePosition(parser, pos, end, description, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user