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 | |
15 | namespace duckdb { |
16 | |
17 | StatementVerifier::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 | |
23 | StatementVerifier::StatementVerifier(unique_ptr<SQLStatement> statement_p) |
24 | : StatementVerifier(VerificationType::ORIGINAL, "Original" , std::move(statement_p)) { |
25 | } |
26 | |
27 | StatementVerifier::~StatementVerifier() noexcept { |
28 | } |
29 | |
30 | unique_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 | |
54 | void 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 | |
87 | void 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 | |
105 | bool 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 | |
132 | string 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 | |