mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 07:31:57 +00:00
dbms: added line and column information in case of syntax error in multiline query [#METR-10728].
This commit is contained in:
parent
1700e1f761
commit
813406b01b
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
/// Проверка ограничений.
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user