Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
2dot5
ClickHouse
提交
a2dafac4
C
ClickHouse
项目概览
2dot5
/
ClickHouse
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
ClickHouse
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
a2dafac4
编写于
1月 19, 2021
作者:
A
Alexander Kuzmenkov
提交者:
GitHub
1月 19, 2021
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #19254 from ClickHouse/aku/test-hint
Consolidate the test hint handling
上级
f5931a22
6fefc29f
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
202 addition
and
125 deletion
+202
-125
programs/client/Client.cpp
programs/client/Client.cpp
+202
-87
programs/client/TestHint.h
programs/client/TestHint.h
+0
-38
未找到文件。
programs/client/Client.cpp
浏览文件 @
a2dafac4
...
...
@@ -195,15 +195,15 @@ private:
/// Parsed query. Is used to determine some settings (e.g. format, output file).
ASTPtr
parsed_query
;
/// The last exception that was received from the server. Is used for the return code in batch mode.
std
::
unique_ptr
<
Exception
>
last_exception_received_from_server
;
/// The last exception that was received from the server. Is used for the
/// return code in batch mode.
std
::
unique_ptr
<
Exception
>
server_exception
;
/// Likewise, the last exception that occurred on the client.
std
::
unique_ptr
<
Exception
>
client_exception
;
/// If the last query resulted in exception.
bool
received_exception_from_server
=
false
;
int
expected_server_error
=
0
;
int
expected_client_error
=
0
;
int
actual_server_error
=
0
;
int
actual_client_error
=
0
;
/// If the last query resulted in exception. `server_exception` or
/// `client_exception` must be set.
bool
have_error
=
false
;
UInt64
server_revision
=
0
;
String
server_version
;
...
...
@@ -669,18 +669,16 @@ private:
}
catch
(
const
Exception
&
e
)
{
actual_client_error
=
e
.
code
();
if
(
!
actual_client_error
||
actual_client_error
!=
expected_client_error
)
{
std
::
cerr
<<
std
::
endl
<<
"Exception on client:"
<<
std
::
endl
<<
"Code: "
<<
e
.
code
()
<<
". "
<<
e
.
displayText
()
<<
std
::
endl
;
// We don't need to handle the test hints in the interactive
// mode.
std
::
cerr
<<
std
::
endl
<<
"Exception on client:"
<<
std
::
endl
<<
"Code: "
<<
e
.
code
()
<<
". "
<<
e
.
displayText
()
<<
std
::
endl
;
if
(
config
().
getBool
(
"stacktrace"
,
false
))
std
::
cerr
<<
"Stack trace:"
<<
std
::
endl
<<
e
.
getStackTraceString
()
<<
std
::
endl
;
if
(
config
().
getBool
(
"stacktrace"
,
false
))
std
::
cerr
<<
"Stack trace:"
<<
std
::
endl
<<
e
.
getStackTraceString
()
<<
std
::
endl
;
std
::
cerr
<<
std
::
endl
;
}
std
::
cerr
<<
std
::
endl
;
/// Client-side exception during query execution can result in the loss of
/// sync in the connection protocol.
...
...
@@ -706,9 +704,20 @@ private:
nonInteractive
();
/// If exception code isn't zero, we should return non-zero return code anyway.
if
(
last_exception_received_from_server
)
return
last_exception_received_from_server
->
code
()
!=
0
?
last_exception_received_from_server
->
code
()
:
-
1
;
// If exception code isn't zero, we should return non-zero return
// code anyway.
const
auto
*
exception
=
server_exception
?
server_exception
.
get
()
:
client_exception
.
get
();
if
(
exception
)
{
return
exception
->
code
()
!=
0
?
exception
->
code
()
:
-
1
;
}
if
(
have_error
)
{
// Shouldn't be set without an exception, but check it just in
// case so that at least we don't lose an error.
return
-
1
;
}
return
0
;
}
...
...
@@ -903,6 +912,40 @@ private:
}
}
void
reportQueryError
()
const
{
// If we probably have progress bar, we should add additional
// newline, otherwise exception may display concatenated with
// the progress bar.
if
(
need_render_progress
)
std
::
cerr
<<
'\n'
;
if
(
server_exception
)
{
std
::
string
text
=
server_exception
->
displayText
();
auto
embedded_stack_trace_pos
=
text
.
find
(
"Stack trace"
);
if
(
std
::
string
::
npos
!=
embedded_stack_trace_pos
&&
!
config
().
getBool
(
"stacktrace"
,
false
))
{
text
.
resize
(
embedded_stack_trace_pos
);
}
std
::
cerr
<<
"Received exception from server (version "
<<
server_version
<<
"):"
<<
std
::
endl
<<
"Code: "
<<
server_exception
->
code
()
<<
". "
<<
text
<<
std
::
endl
;
}
if
(
client_exception
)
{
fmt
::
print
(
stderr
,
"Error on processing query '{}':
\n
{}"
,
full_query
,
client_exception
->
message
());
}
// A debug check -- at least some exception must be set, if the error
// flag is set, and vice versa.
assert
(
have_error
==
(
client_exception
||
server_exception
));
}
bool
processMultiQuery
(
const
String
&
all_queries_text
)
{
// It makes sense not to base any control flow on this, so that it is
...
...
@@ -1067,51 +1110,135 @@ private:
continue
;
}
// Look for the hint in the text of query + insert data, if any.
// e.g. insert into t format CSV 'a' -- { serverError 123 }.
TestHint
test_hint
(
test_mode
,
full_query
);
expected_client_error
=
test_hint
.
clientError
();
expected_server_error
=
test_hint
.
serverError
();
try
{
processParsedSingleQuery
();
}
catch
(...)
{
// Surprisingly, this is a client error. A server error would
// have been reported w/o throwing (see onReceiveSeverException()).
client_exception
=
std
::
make_unique
<
Exception
>
(
getCurrentExceptionMessage
(
true
),
getCurrentExceptionCode
());
have_error
=
true
;
}
// For INSERTs with inline data: use the end of inline data as
// reported by the format parser (it is saved in sendData()).
// This allows us to handle queries like:
// insert into t values (1); select 1
// , where the inline data is delimited by semicolon and not by a
// newline.
if
(
insert_ast
&&
insert_ast
->
data
)
{
this_query_end
=
insert_ast
->
end
;
adjustQueryEnd
(
this_query_end
,
all_queries_end
,
context
.
getSettingsRef
().
max_parser_depth
);
}
if
(
insert_ast
&&
insert_ast
->
data
)
// Now we know for sure where the query ends.
// Look for the hint in the text of query + insert data + trailing
// comments,
// e.g. insert into t format CSV 'a' -- { serverError 123 }.
// Use the updated query boundaries we just calculated.
TestHint
test_hint
(
test_mode
,
std
::
string
(
this_query_begin
,
this_query_end
-
this_query_begin
));
// Check whether the error (or its absence) matches the test hints
// (or their absence).
bool
error_matches_hint
=
true
;
if
(
have_error
)
{
if
(
test_hint
.
serverError
())
{
// For VALUES format: use the end of inline data as reported
// by the format parser (it is saved in sendData()). This
// allows us to handle queries like:
// insert into t values (1); select 1
//, where the inline data is delimited by semicolon and not
// by a newline.
this_query_end
=
parsed_query
->
as
<
ASTInsertQuery
>
()
->
end
;
adjustQueryEnd
(
this_query_end
,
all_queries_end
,
context
.
getSettingsRef
().
max_parser_depth
);
if
(
!
server_exception
)
{
error_matches_hint
=
false
;
fmt
::
print
(
stderr
,
"Expected server error code '{}' but got no server error.
\n
"
,
test_hint
.
serverError
());
}
else
if
(
server_exception
->
code
()
!=
test_hint
.
serverError
())
{
error_matches_hint
=
false
;
std
::
cerr
<<
"Expected server error code: "
<<
test_hint
.
serverError
()
<<
" but got: "
<<
server_exception
->
code
()
<<
"."
<<
std
::
endl
;
}
}
if
(
test_hint
.
clientError
())
{
if
(
!
client_exception
)
{
error_matches_hint
=
false
;
fmt
::
print
(
stderr
,
"Expected client error code '{}' but got no client error.
\n
"
,
test_hint
.
clientError
());
}
else
if
(
client_exception
->
code
()
!=
test_hint
.
clientError
())
{
error_matches_hint
=
false
;
fmt
::
print
(
stderr
,
"Expected client error code '{}' but got '{}'.
\n
"
,
test_hint
.
clientError
(),
client_exception
->
code
());
}
}
if
(
!
test_hint
.
clientError
()
&&
!
test_hint
.
serverError
())
{
// No error was expected but it still occurred. This is the
// default case w/o test hint, doesn't need additional
// diagnostics.
error_matches_hint
=
false
;
}
}
catch
(...)
else
{
last_exception_received_from_server
=
std
::
make_unique
<
Exception
>
(
getCurrentExceptionMessage
(
true
),
getCurrentExceptionCode
());
actual_client_error
=
last_exception_received_from_server
->
code
();
if
(
!
ignore_error
&&
(
!
actual_client_error
||
actual_client_error
!=
expected_client_error
))
std
::
cerr
<<
"Error on processing query: "
<<
full_query
<<
std
::
endl
<<
last_exception_received_from_server
->
message
();
received_exception_from_server
=
true
;
if
(
test_hint
.
clientError
())
{
fmt
::
print
(
stderr
,
"The query succeeded but the client error '{}' was expected.
\n
"
,
test_hint
.
clientError
());
error_matches_hint
=
false
;
}
if
(
test_hint
.
serverError
())
{
fmt
::
print
(
stderr
,
"The query succeeded but the server error '{}' was expected.
\n
"
,
test_hint
.
serverError
());
error_matches_hint
=
false
;
}
}
if
(
!
test_hint
.
checkActual
(
actual_server_error
,
actual_client_error
,
received_exception_from_server
,
last_exception_received_from_server
)
)
// If the error is expected, force reconnect and ignore it.
if
(
have_error
&&
error_matches_hint
)
{
client_exception
.
reset
();
server_exception
.
reset
();
have_error
=
false
;
connection
->
forceConnected
(
connection_parameters
.
timeouts
);
}
if
(
received_exception_from_server
&&
!
ignore_error
)
// Report error.
if
(
have_error
)
{
reportQueryError
();
}
// Stop processing queries if needed.
if
(
have_error
&&
!
ignore_error
)
{
if
(
is_interactive
)
{
break
;
}
else
{
return
false
;
}
}
this_query_begin
=
this_query_end
;
...
...
@@ -1219,15 +1346,20 @@ private:
// Some functions (e.g. protocol parsers) don't throw, but
// set last_exception instead, so we'll also do it here for
// uniformity.
last_exception_received_from_server
=
std
::
make_unique
<
Exception
>
(
getCurrentExceptionMessage
(
true
),
getCurrentExceptionCode
());
received_exception_from_server
=
true
;
// Surprisingly, this is a client exception, because we get the
// server exception w/o throwing (see onReceiveException()).
client_exception
=
std
::
make_unique
<
Exception
>
(
getCurrentExceptionMessage
(
true
),
getCurrentExceptionCode
());
have_error
=
true
;
}
if
(
received_exception_from_serve
r
)
if
(
have_erro
r
)
{
const
auto
*
exception
=
server_exception
?
server_exception
.
get
()
:
client_exception
.
get
();
fmt
::
print
(
stderr
,
"Error on processing query '{}': {}
\n
"
,
ast_to_process
->
formatForErrorMessage
(),
last_exception_received_from_server
->
message
());
exception
->
message
());
}
if
(
!
connection
->
isConnected
())
...
...
@@ -1240,13 +1372,14 @@ private:
// The server is still alive so we're going to continue fuzzing.
// Determine what we're going to use as the starting AST.
if
(
received_exception_from_serve
r
)
if
(
have_erro
r
)
{
// Query completed with error, keep the previous starting AST.
// Also discard the exception that we now know to be non-fatal,
// so that it doesn't influence the exit code.
last_exception_received_from_server
.
reset
(
nullptr
);
received_exception_from_server
=
false
;
server_exception
.
reset
();
client_exception
.
reset
();
have_error
=
false
;
}
else
if
(
ast_to_process
->
formatForErrorMessage
().
size
()
>
500
)
{
...
...
@@ -1290,6 +1423,11 @@ private:
}
processParsedSingleQuery
();
if
(
have_error
)
{
reportQueryError
();
}
}
// Parameters are in global variables:
...
...
@@ -1300,8 +1438,9 @@ private:
void
processParsedSingleQuery
()
{
resetOutput
();
last_exception_received_from_server
.
reset
();
received_exception_from_server
=
false
;
client_exception
.
reset
();
server_exception
.
reset
();
have_error
=
false
;
if
(
echo_queries
)
{
...
...
@@ -1366,7 +1505,7 @@ private:
}
/// Do not change context (current DB, settings) in case of an exception.
if
(
!
received_exception_from_serve
r
)
if
(
!
have_erro
r
)
{
if
(
const
auto
*
set_query
=
parsed_query
->
as
<
ASTSetQuery
>
())
{
...
...
@@ -1777,8 +1916,7 @@ private:
return
true
;
case
Protocol
::
Server
::
Exception
:
onReceiveExceptionFromServer
(
*
packet
.
exception
);
last_exception_received_from_server
=
std
::
move
(
packet
.
exception
);
onReceiveExceptionFromServer
(
std
::
move
(
packet
.
exception
));
return
false
;
case
Protocol
::
Server
::
Log
:
...
...
@@ -1810,8 +1948,7 @@ private:
return
true
;
case
Protocol
::
Server
::
Exception
:
onReceiveExceptionFromServer
(
*
packet
.
exception
);
last_exception_received_from_server
=
std
::
move
(
packet
.
exception
);
onReceiveExceptionFromServer
(
std
::
move
(
packet
.
exception
));
return
false
;
case
Protocol
::
Server
::
Log
:
...
...
@@ -1844,8 +1981,7 @@ private:
return
true
;
case
Protocol
::
Server
::
Exception
:
onReceiveExceptionFromServer
(
*
packet
.
exception
);
last_exception_received_from_server
=
std
::
move
(
packet
.
exception
);
onReceiveExceptionFromServer
(
std
::
move
(
packet
.
exception
));
return
false
;
case
Protocol
::
Server
::
Log
:
...
...
@@ -2154,32 +2290,11 @@ private:
}
void
onReceiveExceptionFromServer
(
const
Exception
&
e
)
void
onReceiveExceptionFromServer
(
std
::
unique_ptr
<
Exception
>
&
&
e
)
{
have_error
=
true
;
server_exception
=
std
::
move
(
e
);
resetOutput
();
received_exception_from_server
=
true
;
actual_server_error
=
e
.
code
();
if
(
expected_server_error
)
{
if
(
actual_server_error
==
expected_server_error
)
return
;
std
::
cerr
<<
"Expected error code: "
<<
expected_server_error
<<
" but got: "
<<
actual_server_error
<<
"."
<<
std
::
endl
;
}
std
::
string
text
=
e
.
displayText
();
auto
embedded_stack_trace_pos
=
text
.
find
(
"Stack trace"
);
if
(
std
::
string
::
npos
!=
embedded_stack_trace_pos
&&
!
config
().
getBool
(
"stacktrace"
,
false
))
text
.
resize
(
embedded_stack_trace_pos
);
/// If we probably have progress bar, we should add additional newline,
/// otherwise exception may display concatenated with the progress bar.
if
(
need_render_progress
)
std
::
cerr
<<
'\n'
;
std
::
cerr
<<
"Received exception from server (version "
<<
server_version
<<
"):"
<<
std
::
endl
<<
"Code: "
<<
e
.
code
()
<<
". "
<<
text
<<
std
::
endl
;
}
...
...
programs/client/TestHint.h
浏览文件 @
a2dafac4
...
...
@@ -11,12 +11,6 @@
namespace
DB
{
namespace
ErrorCodes
{
extern
const
int
UNEXPECTED_ERROR_CODE
;
}
/// Checks expected server and client error codes in testmode.
/// To enable it add special comment after the query: "-- { serverError 60 }" or "-- { clientError 20 }".
/// Also you can enable echoing all queries by writing "-- { echo }".
...
...
@@ -24,7 +18,6 @@ class TestHint
{
public:
TestHint
(
bool
enabled_
,
const
String
&
query_
)
:
enabled
(
enabled_
),
query
(
query_
)
{
if
(
!
enabled_
)
...
...
@@ -64,42 +57,11 @@ public:
}
}
/// @returns true if it's possible to continue without reconnect
bool
checkActual
(
int
&
actual_server_error
,
int
&
actual_client_error
,
bool
&
got_exception
,
std
::
unique_ptr
<
Exception
>
&
last_exception
)
const
{
if
(
!
enabled
)
{
return
true
;
}
if
(
allErrorsExpected
(
actual_server_error
,
actual_client_error
))
{
got_exception
=
false
;
last_exception
.
reset
();
actual_server_error
=
0
;
actual_client_error
=
0
;
return
false
;
}
if
(
lostExpectedError
(
actual_server_error
,
actual_client_error
))
{
std
::
cerr
<<
"Success when error expected in query: "
<<
query
<<
"It expects server error "
<<
server_error
<<
", client error "
<<
client_error
<<
"."
<<
std
::
endl
;
got_exception
=
true
;
last_exception
=
std
::
make_unique
<
Exception
>
(
"Success when error expected"
,
ErrorCodes
::
UNEXPECTED_ERROR_CODE
);
/// return error to OS
return
false
;
}
return
true
;
}
int
serverError
()
const
{
return
server_error
;
}
int
clientError
()
const
{
return
client_error
;
}
bool
echoQueries
()
const
{
return
echo
;
}
private:
bool
enabled
=
false
;
const
String
&
query
;
int
server_error
=
0
;
int
client_error
=
0
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录