Throw exception when multi-statements are disabled [#METR-22252].

This commit is contained in:
Alexey Milovidov 2016-08-17 08:38:51 +03:00
parent a9eca34a14
commit 56a091cb6e
7 changed files with 55 additions and 34 deletions

View File

@ -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)
{

View File

@ -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();
}

View File

@ -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(

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}