dbms: added line and column information in case of syntax error in multiline query [#METR-10728].

This commit is contained in:
Alexey Milovidov 2014-04-13 12:52:50 +04:00
parent 1700e1f761
commit 813406b01b
6 changed files with 67 additions and 31 deletions

View File

@ -3,6 +3,7 @@
#include <list>
#include <memory>
#include <DB/Core/Defines.h>
#include <DB/Core/Types.h>
#include <DB/Parsers/IAST.h>
@ -29,7 +30,7 @@ public:
* в expected записать, что ожидалось в максимальной позиции,
* до которой удалось распарсить, если парсинг был неуспешным,
* или что парсит этот парсер, если парсинг был успешным.
* Везде предполагается, что строка, в которую входит диапазон [begin, end) 0-terminated.
* Строка, в которую входит диапазон [begin, end) может быть не 0-terminated.
*/
virtual bool parse(Pos & pos, Pos end, ASTPtr & node, const char *& expected) = 0;
@ -65,4 +66,60 @@ public:
typedef std::unique_ptr<IParser> ParserPtr;
/** Из позиции в (возможно многострочном) запросе получить номер строки и номер столбца в строке.
* Используется в сообщении об ошибках.
*/
inline std::pair<size_t, size_t> getLineAndCol(IParser::Pos begin, IParser::Pos pos)
{
size_t line = 0;
IParser::Pos nl;
while (nullptr != (nl = reinterpret_cast<IParser::Pos>(memchr(begin, '\n', pos - begin))))
{
++line;
begin = nl + 1;
}
/// Нумеруются с единицы.
return { line + 1, pos - begin + 1 };
}
/** Получить сообщение о синтаксической ошибке.
*/
inline std::string getSyntaxErrorMessage(
bool parse_res, /// false, если не удалось распарсить; true, если удалось, но не до конца строки или точки с запятой.
IParser::Pos begin,
IParser::Pos end,
IParser::Pos pos,
const char * expected,
const std::string & description = "")
{
std::stringstream message;
message << "Syntax error ";
if (!description.empty())
message << "(" << description << ")";
message << ": failed at position " << (pos - begin + 1);
/// Если запрос многострочный.
IParser::Pos nl = reinterpret_cast<IParser::Pos>(memchr(begin, '\n', end - begin));
if (nullptr != nl && nl + 1 != end)
{
size_t line = 0;
size_t col = 0;
std::tie(line, col) = getLineAndCol(begin, pos);
message << " (line " << line << ", col " << col << ")";
}
message << ": " << std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
<< ", expected " << (parse_res ? "end of query" : expected) << ".";
return message.str();
}
}

View File

@ -534,18 +534,12 @@ private:
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';'))
{
std::stringstream message;
message << "Syntax error: failed at position "
<< (pos - begin) << ": "
<< std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
<< ", expected " << (parse_res ? "end of query" : expected) << "."
<< std::endl << std::endl;
std::string message = getSyntaxErrorMessage(parse_res, begin, end, pos, expected);
if (is_interactive)
std::cerr << message.str();
std::cerr << message << std::endl << std::endl;
else
throw Exception(message.str(), ErrorCodes::SYNTAX_ERROR);
throw Exception(message, ErrorCodes::SYNTAX_ERROR);
return nullptr;
}

View File

@ -353,10 +353,7 @@ ASTPtr Context::getCreateQuery(const String & database_name, const String & tabl
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';'))
throw Exception("Syntax error while parsing query from file " + metadata_path + ": failed at position "
+ toString(pos - begin) + ": "
+ std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
+ ", expected " + (parse_res ? "end of query" : expected) + ".",
throw Exception(getSyntaxErrorMessage(parse_res, begin, end, pos, expected, "in file " + metadata_path),
DB::ErrorCodes::SYNTAX_ERROR);
ASTCreateQuery & ast_create_query = dynamic_cast<ASTCreateQuery &>(*ast);

View File

@ -81,10 +81,7 @@ void InterpreterRenameQuery::execute()
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';'))
throw Exception("Syntax error in file " + from_metadata_path + ": failed at position "
+ toString(pos - create_query.data()) + ": "
+ std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
+ ", expected " + (parse_res ? "end of query" : expected) + ".",
throw Exception(getSyntaxErrorMessage(parse_res, create_query.data(), end, pos, expected, "in file " + from_metadata_path),
ErrorCodes::SYNTAX_ERROR);
dynamic_cast<ASTCreateQuery &>(*ast).table = to_table_name;

View File

@ -68,10 +68,7 @@ void executeQuery(
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';'))
throw Exception("Syntax error: failed at position "
+ toString(pos - begin) + ": "
+ std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
+ ", expected " + (parse_res ? "end of query" : expected) + ".",
throw Exception(getSyntaxErrorMessage(parse_res, begin, end, pos, expected),
ErrorCodes::SYNTAX_ERROR);
/// Засунем запрос в строку. Она выводится в лог и в processlist. Если запрос INSERT, то не будем включать данные для вставки.
@ -141,10 +138,7 @@ BlockIO executeQuery(
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';'))
throw Exception("Syntax error: failed at position "
+ toString(pos - begin) + ": "
+ std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
+ ", expected " + (parse_res ? "end of query" : expected) + ".",
throw Exception(getSyntaxErrorMessage(parse_res, begin, end, pos, expected),
ErrorCodes::SYNTAX_ERROR);
/// Проверка ограничений.

View File

@ -33,10 +33,7 @@ static void executeCreateQuery(const String & query, Context & context, const St
/// Распарсенный запрос должен заканчиваться на конец входных данных или на точку с запятой.
if (!parse_res || (pos != end && *pos != ';'))
throw Exception("Syntax error while executing query from file " + file_name + ": failed at position "
+ toString(pos - begin) + ": "
+ std::string(pos, std::min(SHOW_CHARS_ON_SYNTAX_ERROR, end - pos))
+ ", expected " + (parse_res ? "end of query" : expected) + ".",
throw Exception(getSyntaxErrorMessage(parse_res, begin, end, pos, expected, "in file " + file_name),
DB::ErrorCodes::SYNTAX_ERROR);
ASTCreateQuery & ast_create_query = dynamic_cast<ASTCreateQuery &>(*ast);
@ -46,7 +43,7 @@ static void executeCreateQuery(const String & query, Context & context, const St
InterpreterCreateQuery interpreter(ast, context);
interpreter.execute(true);
}
void loadMetadata(Context & context)
{