1#include "duckdb/verification/statement_verifier.hpp"
2
3#include "duckdb/common/preserved_error.hpp"
4#include "duckdb/common/types/column/column_data_collection.hpp"
5#include "duckdb/parser/parser.hpp"
6#include "duckdb/verification/copied_statement_verifier.hpp"
7#include "duckdb/verification/deserialized_statement_verifier.hpp"
8#include "duckdb/verification/deserialized_statement_verifier_v2.hpp"
9#include "duckdb/verification/external_statement_verifier.hpp"
10#include "duckdb/verification/parsed_statement_verifier.hpp"
11#include "duckdb/verification/prepared_statement_verifier.hpp"
12#include "duckdb/verification/unoptimized_statement_verifier.hpp"
13#include "duckdb/verification/no_operator_caching_verifier.hpp"
14
15namespace duckdb {
16
17StatementVerifier::StatementVerifier(VerificationType type, string name, unique_ptr<SQLStatement> statement_p)
18 : type(type), name(std::move(name)),
19 statement(unique_ptr_cast<SQLStatement, SelectStatement>(src: std::move(statement_p))),
20 select_list(statement->node->GetSelectList()) {
21}
22
23StatementVerifier::StatementVerifier(unique_ptr<SQLStatement> statement_p)
24 : StatementVerifier(VerificationType::ORIGINAL, "Original", std::move(statement_p)) {
25}
26
27StatementVerifier::~StatementVerifier() noexcept {
28}
29
30unique_ptr<StatementVerifier> StatementVerifier::Create(VerificationType type, const SQLStatement &statement_p) {
31 switch (type) {
32 case VerificationType::COPIED:
33 return CopiedStatementVerifier::Create(statement_p);
34 case VerificationType::DESERIALIZED:
35 return DeserializedStatementVerifier::Create(statement: statement_p);
36 case VerificationType::DESERIALIZED_V2:
37 return DeserializedStatementVerifierV2::Create(statement: statement_p);
38 case VerificationType::PARSED:
39 return ParsedStatementVerifier::Create(statement: statement_p);
40 case VerificationType::UNOPTIMIZED:
41 return UnoptimizedStatementVerifier::Create(statement_p);
42 case VerificationType::NO_OPERATOR_CACHING:
43 return NoOperatorCachingVerifier::Create(statement_p);
44 case VerificationType::PREPARED:
45 return PreparedStatementVerifier::Create(statement_p);
46 case VerificationType::EXTERNAL:
47 return ExternalStatementVerifier::Create(statement: statement_p);
48 case VerificationType::INVALID:
49 default:
50 throw InternalException("Invalid statement verification type!");
51 }
52}
53
54void StatementVerifier::CheckExpressions(const StatementVerifier &other) const {
55 // Only the original statement should check other statements
56 D_ASSERT(type == VerificationType::ORIGINAL);
57
58 // Check equality
59 if (other.RequireEquality()) {
60 D_ASSERT(statement->Equals(*other.statement));
61 }
62
63#ifdef DEBUG
64 // Now perform checking on the expressions
65 D_ASSERT(select_list.size() == other.select_list.size());
66 const auto expr_count = select_list.size();
67 if (other.RequireEquality()) {
68 for (idx_t i = 0; i < expr_count; i++) {
69 // Run the ToString, to verify that it doesn't crash
70 select_list[i]->ToString();
71
72 if (select_list[i]->HasSubquery()) {
73 continue;
74 }
75
76 // Check that the expressions are equivalent
77 D_ASSERT(select_list[i]->Equals(*other.select_list[i]));
78 // Check that the hashes are equivalent too
79 D_ASSERT(select_list[i]->Hash() == other.select_list[i]->Hash());
80
81 other.select_list[i]->Verify();
82 }
83 }
84#endif
85}
86
87void StatementVerifier::CheckExpressions() const {
88#ifdef DEBUG
89 D_ASSERT(type == VerificationType::ORIGINAL);
90 // Perform additional checking within the expressions
91 const auto expr_count = select_list.size();
92 for (idx_t outer_idx = 0; outer_idx < expr_count; outer_idx++) {
93 auto hash = select_list[outer_idx]->Hash();
94 for (idx_t inner_idx = 0; inner_idx < expr_count; inner_idx++) {
95 auto hash2 = select_list[inner_idx]->Hash();
96 if (hash != hash2) {
97 // if the hashes are not equivalent, the expressions should not be equivalent
98 D_ASSERT(!select_list[outer_idx]->Equals(*select_list[inner_idx]));
99 }
100 }
101 }
102#endif
103}
104
105bool StatementVerifier::Run(
106 ClientContext &context, const string &query,
107 const std::function<unique_ptr<QueryResult>(const string &, unique_ptr<SQLStatement>)> &run) {
108 bool failed = false;
109
110 context.interrupted = false;
111 context.config.enable_optimizer = !DisableOptimizer();
112 context.config.enable_caching_operators = !DisableOperatorCaching();
113 context.config.force_external = ForceExternal();
114 try {
115 auto result = run(query, std::move(statement));
116 if (result->HasError()) {
117 failed = true;
118 }
119 materialized_result = unique_ptr_cast<QueryResult, MaterializedQueryResult>(src: std::move(result));
120 } catch (const Exception &ex) {
121 failed = true;
122 materialized_result = make_uniq<MaterializedQueryResult>(args: PreservedError(ex));
123 } catch (std::exception &ex) {
124 failed = true;
125 materialized_result = make_uniq<MaterializedQueryResult>(args: PreservedError(ex));
126 }
127 context.interrupted = false;
128
129 return failed;
130}
131
132string StatementVerifier::CompareResults(const StatementVerifier &other) {
133 D_ASSERT(type == VerificationType::ORIGINAL);
134 string error;
135 if (materialized_result->HasError() != other.materialized_result->HasError()) { // LCOV_EXCL_START
136 string result = other.name + " statement differs from original result!\n";
137 result += "Original Result:\n" + materialized_result->ToString();
138 result += other.name + ":\n" + other.materialized_result->ToString();
139 return result;
140 } // LCOV_EXCL_STOP
141 if (materialized_result->HasError()) {
142 return "";
143 }
144 if (!ColumnDataCollection::ResultEquals(left: materialized_result->Collection(), right: other.materialized_result->Collection(),
145 error_message&: error)) { // LCOV_EXCL_START
146 string result = other.name + " statement differs from original result!\n";
147 result += "Original Result:\n" + materialized_result->ToString();
148 result += other.name + ":\n" + other.materialized_result->ToString();
149 result += "\n\n---------------------------------\n" + error;
150 return result;
151 } // LCOV_EXCL_STOP
152
153 return "";
154}
155
156} // namespace duckdb
157