| 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 | |