1#include "duckdb/common/preserved_error.hpp"
2#include "duckdb/main/client_context.hpp"
3#include "duckdb/parser/statement/explain_statement.hpp"
4#include "duckdb/verification/statement_verifier.hpp"
5#include "duckdb/main/database.hpp"
6#include "duckdb/common/box_renderer.hpp"
7
8namespace duckdb {
9
10PreservedError ClientContext::VerifyQuery(ClientContextLock &lock, const string &query,
11 unique_ptr<SQLStatement> statement) {
12 D_ASSERT(statement->type == StatementType::SELECT_STATEMENT);
13 // Aggressive query verification
14
15 // The purpose of this function is to test correctness of otherwise hard to test features:
16 // Copy() of statements and expressions
17 // Serialize()/Deserialize() of expressions
18 // Hash() of expressions
19 // Equality() of statements and expressions
20 // ToString() of statements and expressions
21 // Correctness of plans both with and without optimizers
22
23 const auto &stmt = *statement;
24 vector<unique_ptr<StatementVerifier>> statement_verifiers;
25 unique_ptr<StatementVerifier> prepared_statement_verifier;
26 if (config.query_verification_enabled) {
27 statement_verifiers.emplace_back(args: StatementVerifier::Create(type: VerificationType::COPIED, statement_p: stmt));
28 statement_verifiers.emplace_back(args: StatementVerifier::Create(type: VerificationType::DESERIALIZED, statement_p: stmt));
29 statement_verifiers.emplace_back(args: StatementVerifier::Create(type: VerificationType::DESERIALIZED_V2, statement_p: stmt));
30 statement_verifiers.emplace_back(args: StatementVerifier::Create(type: VerificationType::UNOPTIMIZED, statement_p: stmt));
31 prepared_statement_verifier = StatementVerifier::Create(type: VerificationType::PREPARED, statement_p: stmt);
32#ifdef DUCKDB_DEBUG_ASYNC_SINK_SOURCE
33 // This verification is quite slow, so we only run it for the async sink/source debug mode
34 statement_verifiers.emplace_back(StatementVerifier::Create(VerificationType::NO_OPERATOR_CACHING, stmt));
35#endif
36 }
37 if (config.verify_external) {
38 statement_verifiers.emplace_back(args: StatementVerifier::Create(type: VerificationType::EXTERNAL, statement_p: stmt));
39 }
40
41 auto original = make_uniq<StatementVerifier>(args: std::move(statement));
42 for (auto &verifier : statement_verifiers) {
43 original->CheckExpressions(other: *verifier);
44 }
45 original->CheckExpressions();
46
47 // See below
48 auto statement_copy_for_explain = stmt.Copy();
49
50 // Save settings
51 bool optimizer_enabled = config.enable_optimizer;
52 bool profiling_is_enabled = config.enable_profiler;
53 bool force_external = config.force_external;
54
55 // Disable profiling if it is enabled
56 if (profiling_is_enabled) {
57 config.enable_profiler = false;
58 }
59
60 // Execute the original statement
61 bool any_failed = original->Run(context&: *this, query, run: [&](const string &q, unique_ptr<SQLStatement> s) {
62 return RunStatementInternal(lock, query: q, statement: std::move(s), allow_stream_result: false, verify: false);
63 });
64 if (!any_failed) {
65 statement_verifiers.emplace_back(
66 args: StatementVerifier::Create(type: VerificationType::PARSED, statement_p: *statement_copy_for_explain));
67 }
68 // Execute the verifiers
69 for (auto &verifier : statement_verifiers) {
70 bool failed = verifier->Run(context&: *this, query, run: [&](const string &q, unique_ptr<SQLStatement> s) {
71 return RunStatementInternal(lock, query: q, statement: std::move(s), allow_stream_result: false, verify: false);
72 });
73 any_failed = any_failed || failed;
74 }
75
76 if (!any_failed && prepared_statement_verifier) {
77 // If none failed, we execute the prepared statement verifier
78 bool failed = prepared_statement_verifier->Run(context&: *this, query, run: [&](const string &q, unique_ptr<SQLStatement> s) {
79 return RunStatementInternal(lock, query: q, statement: std::move(s), allow_stream_result: false, verify: false);
80 });
81 if (!failed) {
82 // PreparedStatementVerifier fails if it runs into a ParameterNotAllowedException, which is OK
83 statement_verifiers.push_back(x: std::move(prepared_statement_verifier));
84 }
85 } else {
86 if (ValidChecker::IsInvalidated(o&: *db)) {
87 return original->materialized_result->GetErrorObject();
88 }
89 }
90
91 // Restore config setting
92 config.enable_optimizer = optimizer_enabled;
93 config.force_external = force_external;
94
95 // Check explain, only if q does not already contain EXPLAIN
96 if (original->materialized_result->success) {
97 auto explain_q = "EXPLAIN " + query;
98 auto explain_stmt = make_uniq<ExplainStatement>(args: std::move(statement_copy_for_explain));
99 try {
100 RunStatementInternal(lock, query: explain_q, statement: std::move(explain_stmt), allow_stream_result: false, verify: false);
101 } catch (std::exception &ex) { // LCOV_EXCL_START
102 interrupted = false;
103 return PreservedError("EXPLAIN failed but query did not (" + string(ex.what()) + ")");
104 } // LCOV_EXCL_STOP
105
106#ifdef DUCKDB_VERIFY_BOX_RENDERER
107 // this is pretty slow, so disabled by default
108 // test the box renderer on the result
109 // we mostly care that this does not crash
110 RandomEngine random;
111 BoxRendererConfig config;
112 // test with a random width
113 config.max_width = random.NextRandomInteger() % 500;
114 BoxRenderer renderer(config);
115 renderer.ToString(*this, original->materialized_result->names, original->materialized_result->Collection());
116#endif
117 }
118
119 // Restore profiler setting
120 if (profiling_is_enabled) {
121 config.enable_profiler = true;
122 }
123
124 // Now compare the results
125 // The results of all runs should be identical
126 for (auto &verifier : statement_verifiers) {
127 auto result = original->CompareResults(other: *verifier);
128 if (!result.empty()) {
129 return PreservedError(result);
130 }
131 }
132
133 return PreservedError();
134}
135
136} // namespace duckdb
137