#include #include "Client.h" #include "Parsers/formatAST.h" #include #include #include #include #include #include #include #include #include #include #include #include #if USE_BUZZHOUSE # include # include # include #endif namespace DB { namespace Setting { extern const SettingsDialect dialect; } namespace ErrorCodes { extern const int NOT_IMPLEMENTED; extern const int SYNTAX_ERROR; extern const int TOO_DEEP_RECURSION; } std::optional Client::processFuzzingStep(const String & query_to_execute, const ASTPtr & parsed_query, const bool permissive) { processParsedSingleQuery(query_to_execute, query_to_execute, parsed_query); const auto * exception = server_exception ? server_exception.get() : client_exception.get(); // Sometimes you may get TOO_DEEP_RECURSION from the server, // and TOO_DEEP_RECURSION should not fail the fuzzer check. if (permissive && have_error && exception->code() == ErrorCodes::TOO_DEEP_RECURSION) { have_error = false; server_exception.reset(); client_exception.reset(); return true; } if (have_error) { fmt::print(stderr, "Error on processing query '{}': {}\n", parsed_query->formatForErrorMessage(), exception->message()); // Try to reconnect after errors, for two reasons: // 1. We might not have realized that the server died, e.g. if // it sent us a trace and closed connection properly. // 2. The connection might have gotten into a wrong state and // the next query will get false positive about // "Unknown packet from server". try { connection->forceConnected(connection_parameters.timeouts); } catch (...) { // Just report it, we'll terminate below. fmt::print(stderr, "Error while reconnecting to the server: {}\n", getCurrentExceptionMessage(true)); // The reconnection might fail, but we'll still be connected // in the sense of `connection->isConnected() = true`, // in case when the requested database doesn't exist. // Disconnect manually now, so that the following code doesn't // have any doubts, and the connection state is predictable. connection->disconnect(); } } if (!connection->isConnected()) { // Probably the server is dead because we found an assertion // failure. Fail fast. fmt::print(stderr, "Lost connection to the server.\n"); // Print the changed settings because they might be needed to // reproduce the error. printChangedSettings(); return permissive; //for BuzzHouse, don't continue on error } return std::nullopt; } /// Returns false when server is not available. bool Client::processWithFuzzing(const String & full_query) { ASTPtr orig_ast; try { const char * begin = full_query.data(); orig_ast = parseQuery( begin, begin + full_query.size(), client_context->getSettingsRef(), /*allow_multi_statements=*/true); } catch (const Exception & e) { if (e.code() != ErrorCodes::SYNTAX_ERROR && e.code() != ErrorCodes::TOO_DEEP_RECURSION) throw; } if (!orig_ast) { // Can't continue after a parsing error return true; } // `USE db` should not be executed // since this will break every query after `DROP db` if (orig_ast->as()) { return true; } // Kusto is not a subject for fuzzing (yet) if (client_context->getSettingsRef()[Setting::dialect] == DB::Dialect::kusto) { return true; } if (auto * q = orig_ast->as()) { if (auto * set_dialect = q->changes.tryGet("dialect"); set_dialect && set_dialect->safeGet() == "kusto") return true; } // Don't repeat: // - INSERT -- Because the tables may grow too big. // - CREATE -- Because first we run the unmodified query, it will succeed, // and the subsequent queries will fail. // When we run out of fuzzer errors, it may be interesting to // add fuzzing of create queries that wraps columns into // LowCardinality or Nullable. // Also there are other kinds of create queries such as CREATE // DICTIONARY, we could fuzz them as well. // - DROP -- No point in this (by the same reasons). // - SET -- The time to fuzz the settings has not yet come // (see comments in Client/QueryFuzzer.cpp) size_t this_query_runs = query_fuzzer_runs; ASTs queries_for_fuzzed_tables; if (orig_ast->as()) { this_query_runs = 1; } else if (const auto * create = orig_ast->as()) { if (QueryFuzzer::isSuitableForFuzzing(*create)) this_query_runs = create_query_fuzzer_runs; else this_query_runs = 1; } else if (const auto * /*insert*/ _ = orig_ast->as()) { this_query_runs = 1; queries_for_fuzzed_tables = fuzzer.getInsertQueriesForFuzzedTables(full_query); } else if (const auto * /*optimize*/ _ = orig_ast->as()) { this_query_runs = 1; queries_for_fuzzed_tables = fuzzer.getOptimizeQueriesForFuzzedTables(full_query); } else if (const auto * drop = orig_ast->as()) { this_query_runs = 1; queries_for_fuzzed_tables = fuzzer.getDropQueriesForFuzzedTables(*drop); } String query_to_execute; ASTPtr fuzz_base = orig_ast; for (size_t fuzz_step = 0; fuzz_step < this_query_runs; ++fuzz_step) { fmt::print(stderr, "Fuzzing step {} out of {}\n", fuzz_step, this_query_runs); ASTPtr ast_to_process; try { WriteBufferFromOwnString dump_before_fuzz; fuzz_base->dumpTree(dump_before_fuzz); auto base_before_fuzz = fuzz_base->formatForErrorMessage(); ast_to_process = fuzz_base->clone(); WriteBufferFromOwnString dump_of_cloned_ast; ast_to_process->dumpTree(dump_of_cloned_ast); // Run the original query as well. if (fuzz_step > 0) { fuzzer.fuzzMain(ast_to_process); } auto base_after_fuzz = fuzz_base->formatForErrorMessage(); // Check that the source AST didn't change after fuzzing. This // helps debug AST cloning errors, where the cloned AST doesn't // clone all its children, and erroneously points to some source // child elements. if (base_before_fuzz != base_after_fuzz) { printChangedSettings(); fmt::print( stderr, "Base before fuzz: {}\n" "Base after fuzz: {}\n", base_before_fuzz, base_after_fuzz); fmt::print(stderr, "Dump before fuzz:\n{}\n", dump_before_fuzz.str()); fmt::print(stderr, "Dump of cloned AST:\n{}\n", dump_of_cloned_ast.str()); fmt::print(stderr, "Dump after fuzz:\n"); WriteBufferFromOStream cerr_buf(std::cerr, 4096); fuzz_base->dumpTree(cerr_buf); cerr_buf.finalize(); fmt::print( stderr, "Found error: IAST::clone() is broken for some AST node. This is a bug. The original AST ('dump before fuzz') and its " "cloned copy ('dump of cloned AST') refer to the same nodes, which must never happen. This means that their parent " "node doesn't implement clone() correctly."); _exit(1); } auto fuzzed_text = ast_to_process->formatForErrorMessage(); if (fuzz_step > 0 && fuzzed_text == base_before_fuzz) { fmt::print(stderr, "Got boring AST\n"); continue; } query_to_execute = ast_to_process->formatForErrorMessage(); if (auto res = processFuzzingStep(query_to_execute, ast_to_process, true)) return *res; } catch (...) { if (!ast_to_process) fmt::print(stderr, "Error while forming new query: {}\n", getCurrentExceptionMessage(true)); // Some functions (e.g. protocol parsers) don't throw, but // set last_exception instead, so we'll also do it here for // uniformity. // Surprisingly, this is a client exception, because we get the // server exception w/o throwing (see onReceiveException()). client_exception = std::make_unique(getCurrentExceptionMessageAndPattern(print_stack_trace), getCurrentExceptionCode()); have_error = true; } // The server is still alive, so we're going to continue fuzzing. // Determine what we're going to use as the starting AST. if (have_error) { // 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. server_exception.reset(); client_exception.reset(); fuzzer.notifyQueryFailed(ast_to_process); have_error = false; } else if (ast_to_process->formatForErrorMessage().size() > 500) { // ast too long, start from original ast fmt::print(stderr, "Current AST is too long, discarding it and using the original AST as a start\n"); fuzz_base = orig_ast; } else { // fuzz starting from this successful query fmt::print(stderr, "Query succeeded, using this AST as a start\n"); fuzz_base = ast_to_process; #if USE_BUZZHOUSE if (fc && fc->measure_performance && ei && (orig_ast->as() || orig_ast->as() || orig_ast->as())) { uint64_t memory_usage1 = 0; uint64_t memory_usage2 = 0; uint64_t query_duration_ms1 = 0; uint64_t query_duration_ms2 = 0; bool measure_performance = true; measure_performance &= ei->getPerformanceMetricsForLastQuery(BuzzHouse::PeerTableDatabase::None, query_duration_ms1, memory_usage1); ei->replicateSettings(BuzzHouse::PeerTableDatabase::ClickHouse); measure_performance &= ei->performQuery(BuzzHouse::PeerTableDatabase::ClickHouse, query_to_execute); measure_performance &= ei->getPerformanceMetricsForLastQuery(BuzzHouse::PeerTableDatabase::ClickHouse, query_duration_ms2, memory_usage2); if (measure_performance) { fc->comparePerformanceResults("AST fuzzer", query_duration_ms1, memory_usage1, query_duration_ms2, memory_usage2); } } #endif } #if USE_BUZZHOUSE if (fc && fc->measure_performance && ei) { auto u = ei->performQuery(BuzzHouse::PeerTableDatabase::ClickHouse, query_to_execute); UNUSED(u); } #endif } for (const auto & query : queries_for_fuzzed_tables) { std::cout << std::endl; WriteBufferFromOStream ast_buf(std::cout, 4096); formatAST(*query, ast_buf, false /*highlight*/); ast_buf.finalize(); if (const auto * insert = query->as()) { /// For inserts with data it's really useful to have the data itself available in the logs, as formatAST doesn't print it if (insert->hasInlinedData()) { String bytes; { auto read_buf = getReadBufferFromASTInsertQuery(query); WriteBufferFromString write_buf(bytes); copyData(*read_buf, write_buf); } std::cout << std::endl << bytes; } } std::cout << std::endl << std::endl; try { query_to_execute = query->formatForErrorMessage(); if (auto res = processFuzzingStep(query_to_execute, query, false)) return *res; } catch (...) { client_exception = std::make_unique(getCurrentExceptionMessageAndPattern(print_stack_trace), getCurrentExceptionCode()); have_error = true; } if (have_error) { server_exception.reset(); client_exception.reset(); fuzzer.notifyQueryFailed(query); have_error = false; } #if USE_BUZZHOUSE if (fc && fc->measure_performance && ei) { auto u = ei->performQuery(BuzzHouse::PeerTableDatabase::ClickHouse, query_to_execute); UNUSED(u); } #endif } return true; } #if USE_BUZZHOUSE bool Client::logAndProcessQuery(std::ofstream & outf, const String & full_query) { outf << full_query << std::endl; return processTextAsSingleQuery(full_query); } bool Client::processBuzzHouseQuery(const String & full_query) { bool server_up = true; ASTPtr orig_ast; have_error = false; try { const char * begin = full_query.data(); if ((orig_ast = parseQuery(begin, begin + full_query.size(), client_context->getSettingsRef(), false))) { String query_to_execute = orig_ast->formatForAnything(); const auto res = processFuzzingStep(query_to_execute, orig_ast, false); server_up &= res.value_or(true); } else { have_error = true; } } catch (...) { // Some functions (e.g. protocol parsers) don't throw, but // set last_exception instead, so we'll also do it here for // uniformity. // Surprisingly, this is a client exception, because we get the // server exception w/o throwing (see onReceiveException()). server_up &= connection->isConnected(); client_exception = std::make_unique(getCurrentExceptionMessageAndPattern(print_stack_trace), getCurrentExceptionCode()); have_error = true; } if (have_error) { // 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. server_exception.reset(); client_exception.reset(); } return server_up; } using sighandler_t = void (*)(int); sighandler_t volatile prev_signal = nullptr; std::sig_atomic_t volatile buzz_done = 0; static void finishBuzzHouse(int num) { if (prev_signal) { prev_signal(num); } buzz_done = 1; } /// Returns false when server is not available. bool Client::buzzHouse() { bool server_up = true; String full_query; /// Set time to run, but what if a query runs for too long? buzz_done = 0; if (fc->time_to_run > 0) { prev_signal = std::signal(SIGALRM, finishBuzzHouse); } alarm(fc->time_to_run); full_query.reserve(8192); if (fc->read_log) { std::ifstream infile(fc->log_path); while (server_up && !buzz_done && std::getline(infile, full_query)) { server_up &= processBuzzHouseQuery(full_query); full_query.resize(0); } } else { String full_query2; std::vector peer_queries; bool first = true; bool replica_setup = true; bool has_cloud_features = true; BuzzHouse::RandomGenerator rg(fc->seed); std::ofstream outf(fc->log_path, std::ios::out | std::ios::trunc); BuzzHouse::SQLQuery sq1; BuzzHouse::SQLQuery sq2; BuzzHouse::SQLQuery sq3; BuzzHouse::SQLQuery sq4; uint32_t nsuccessfull_create_database = 0; uint32_t total_create_database_tries = 0; const uint32_t max_initial_databases = std::min(UINT32_C(3), fc->max_databases); uint32_t nsuccessfull_create_table = 0; uint32_t total_create_table_tries = 0; const uint32_t max_initial_tables = std::min(UINT32_C(10), fc->max_tables); GOOGLE_PROTOBUF_VERIFY_VERSION; has_cloud_features &= processTextAsSingleQuery("DROP DATABASE IF EXISTS fuzztest;"); has_cloud_features &= processTextAsSingleQuery("CREATE DATABASE fuzztest Engine=Shared;"); std::cout << "Cloud features " << (has_cloud_features ? "" : "not ") << "detected" << std::endl; replica_setup &= processTextAsSingleQuery("CREATE TABLE tx (c0 Int) Engine=ReplicatedMergeTree() ORDER BY tuple();"); std::cout << "Replica setup " << (replica_setup ? "" : "not ") << "detected" << std::endl; auto u = processTextAsSingleQuery("DROP TABLE IF EXISTS tx;"); UNUSED(u); auto v = processTextAsSingleQuery("DROP DATABASE IF EXISTS fuzztest;"); UNUSED(v); outf << "--Session seed: " << rg.getSeed() << std::endl; DB::Strings defaultSettings = {"engine_file_truncate_on_insert", "allow_aggregate_partitions_independently", "allow_archive_path_syntax", "allow_asynchronous_read_from_io_pool_for_merge_tree", "allow_changing_replica_until_first_data_packet", "allow_create_index_without_type", "allow_custom_error_code_in_throwif", "allow_ddl", "allow_deprecated_database_ordinary", "allow_deprecated_error_prone_window_functions", "allow_deprecated_snowflake_conversion_functions", "allow_deprecated_syntax_for_merge_tree", "allow_distributed_ddl", "allow_drop_detached", "allow_execute_multiif_columnar", "allow_experimental_analyzer", "allow_experimental_codecs", "allow_experimental_database_materialized_mysql", "allow_experimental_database_materialized_postgresql", "allow_experimental_dynamic_type", "allow_experimental_full_text_index", "allow_experimental_funnel_functions", "allow_experimental_hash_functions", "allow_experimental_inverted_index", "allow_experimental_join_right_table_sorting", "allow_experimental_json_type", "allow_experimental_kafka_offsets_storage_in_keeper", "allow_experimental_live_view", "allow_experimental_materialized_postgresql_table", "allow_experimental_nlp_functions", "allow_experimental_parallel_reading_from_replicas", "allow_experimental_query_deduplication", "allow_experimental_shared_set_join", "allow_experimental_statistics", "allow_experimental_time_series_table", "allow_experimental_variant_type", "allow_experimental_vector_similarity_index", "allow_experimental_window_view", "allow_get_client_http_header", "allow_hyperscan", "allow_introspection_functions", "allow_materialized_view_with_bad_select", "allow_named_collection_override_by_default", "allow_non_metadata_alters", "allow_nonconst_timezone_arguments", "allow_nondeterministic_mutations", "allow_nondeterministic_optimize_skip_unused_shards", "allow_prefetched_read_pool_for_local_filesystem", "allow_prefetched_read_pool_for_remote_filesystem", "allow_push_predicate_when_subquery_contains_with", "allow_reorder_prewhere_conditions", "allow_settings_after_format_in_insert", "allow_simdjson", "allow_statistics_optimize", "allow_suspicious_codecs", "allow_suspicious_fixed_string_types", "allow_suspicious_indices", "allow_suspicious_low_cardinality_types", "allow_suspicious_primary_key", "allow_suspicious_ttl_expressions", "allow_suspicious_variant_types", "allow_suspicious_types_in_group_by", "allow_suspicious_types_in_order_by", "allow_unrestricted_reads_from_keeper", "enable_analyzer", "enable_zstd_qat_codec", "type_json_skip_duplicated_paths", "allow_experimental_database_iceberg", "allow_experimental_bfloat16_type", "allow_not_comparable_types_in_order_by", "allow_not_comparable_types_in_comparison_functions"}; defaultSettings.emplace_back(rg.nextBool() ? "s3_truncate_on_insert" : "s3_create_new_file_on_insert"); full_query.resize(0); for (const auto & entry : defaultSettings) { full_query += fmt::format("{}{} = 1", first ? "" : ", ", entry); first = false; } auto w = logAndProcessQuery(outf, fmt::format("SET {};", full_query)); UNUSED(w); if (ei->hasClickHouseExtraServerConnection()) { ei->setDefaultSettings(BuzzHouse::PeerTableDatabase::ClickHouse, defaultSettings); } /// Load server configurations for the fuzzer fc->loadServerConfigurations(); loadFuzzerServerSettings(*fc); loadFuzzerTableSettings(*fc); loadFuzzerOracleSettings(*fc); loadSystemTables(*fc); full_query2.reserve(8192); BuzzHouse::StatementGenerator gen(*fc, *ei, has_cloud_features, replica_setup); BuzzHouse::QueryOracle qo(*fc); while (server_up && !buzz_done) { sq1.Clear(); full_query.resize(0); if (total_create_database_tries < 10 && nsuccessfull_create_database < max_initial_databases) { gen.generateNextCreateDatabase(rg, sq1.mutable_inner_query()->mutable_create_database()); BuzzHouse::SQLQueryToString(full_query, sq1); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); gen.updateGenerator(sq1, *ei, !have_error); nsuccessfull_create_database += (have_error ? 0 : 1); total_create_database_tries++; } else if ( gen.collectionHas>(gen.attached_databases) && total_create_table_tries < 50 && nsuccessfull_create_table < max_initial_tables) { gen.generateNextCreateTable(rg, sq1.mutable_inner_query()->mutable_create_table()); BuzzHouse::SQLQueryToString(full_query, sq1); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); gen.updateGenerator(sq1, *ei, !have_error); nsuccessfull_create_table += (have_error ? 0 : 1); total_create_table_tries++; } else { const uint32_t correctness_oracle = 30; const uint32_t settings_oracle = 30; const uint32_t dump_oracle = 15 * static_cast(fc->use_dump_table_oracle && gen.collectionHas(gen.attached_tables_for_dump_table_oracle)); const uint32_t peer_oracle = 30 * static_cast(gen.collectionHas(gen.attached_tables_for_table_peer_oracle)); const uint32_t run_query = 910; const uint32_t prob_space = correctness_oracle + settings_oracle + dump_oracle + peer_oracle + run_query; std::uniform_int_distribution next_dist(1, prob_space); const uint32_t nopt = next_dist(rg.generator); if (nopt < (correctness_oracle + settings_oracle + dump_oracle + peer_oracle + 1)) { qo.resetOracleValues(); } if (correctness_oracle && nopt < (correctness_oracle + 1)) { /// Correctness test query qo.generateCorrectnessTestFirstQuery(rg, gen, sq1); BuzzHouse::SQLQueryToString(full_query, sq1); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.processFirstOracleQueryResult(!have_error, *ei); sq2.Clear(); full_query.resize(0); qo.generateCorrectnessTestSecondQuery(sq1, sq2); BuzzHouse::SQLQueryToString(full_query, sq2); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.processSecondOracleQueryResult(!have_error, *ei, "Correctness query"); } else if (settings_oracle && nopt < (correctness_oracle + settings_oracle + 1)) { /// Test running query with different settings qo.generateFirstSetting(rg, sq1); BuzzHouse::SQLQueryToString(full_query, sq1); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.setIntermediateStepSuccess(!have_error); sq2.Clear(); full_query2.resize(0); qo.generateOracleSelectQuery(rg, BuzzHouse::PeerQuery::None, gen, sq2); BuzzHouse::SQLQueryToString(full_query2, sq2); outf << full_query2 << std::endl; server_up &= processBuzzHouseQuery(full_query2); qo.processFirstOracleQueryResult(!have_error, *ei); sq3.Clear(); full_query.resize(0); qo.generateSecondSetting(sq1, sq3); BuzzHouse::SQLQueryToString(full_query, sq3); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.setIntermediateStepSuccess(!have_error); outf << full_query2 << std::endl; server_up &= processBuzzHouseQuery(full_query2); qo.processSecondOracleQueryResult(!have_error, *ei, "Multi setting query"); } else if (dump_oracle && nopt < (correctness_oracle + settings_oracle + dump_oracle + 1)) { const BuzzHouse::SQLTable & t = rg.pickRandomlyFromVector(gen.filterCollection(gen.attached_tables_for_dump_table_oracle)); /// Test in and out formats full_query2.resize(0); qo.dumpTableContent(rg, gen, t, sq1); BuzzHouse::SQLQueryToString(full_query2, sq1); outf << full_query2 << std::endl; server_up &= processBuzzHouseQuery(full_query2); qo.processFirstOracleQueryResult(!have_error, *ei); sq2.Clear(); qo.generateExportQuery(rg, gen, t, sq2); BuzzHouse::SQLQueryToString(full_query, sq2); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.setIntermediateStepSuccess(!have_error); sq3.Clear(); full_query.resize(0); qo.generateClearQuery(t, sq3); BuzzHouse::SQLQueryToString(full_query, sq3); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.setIntermediateStepSuccess(!have_error); sq4.Clear(); full_query.resize(0); qo.generateImportQuery(gen, t, sq2, sq4); BuzzHouse::SQLQueryToString(full_query, sq4); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.setIntermediateStepSuccess(!have_error); outf << full_query2 << std::endl; server_up &= processBuzzHouseQuery(full_query2); qo.processSecondOracleQueryResult(!have_error, *ei, "Dump and read table"); } else if (peer_oracle && nopt < (correctness_oracle + settings_oracle + dump_oracle + peer_oracle + 1)) { /// Test results with peer tables bool has_success = false; BuzzHouse::PeerQuery nquery = ((!ei->hasMySQLConnection() && !ei->hasPostgreSQLConnection() && !ei->hasSQLiteConnection()) || rg.nextBool()) && gen.collectionHas(gen.attached_tables_for_clickhouse_table_peer_oracle) ? BuzzHouse::PeerQuery::ClickHouseOnly : BuzzHouse::PeerQuery::AllPeers; const bool clickhouse_only = nquery == BuzzHouse::PeerQuery::ClickHouseOnly; sq1.Clear(); sq2.Clear(); qo.generateOracleSelectQuery(rg, nquery, gen, sq1); qo.replaceQueryWithTablePeers(rg, sq1, gen, peer_queries, sq2); if (clickhouse_only) { ei->replicateSettings(BuzzHouse::PeerTableDatabase::ClickHouse); } qo.truncatePeerTables(gen); for (const auto & entry : peer_queries) { full_query2.resize(0); BuzzHouse::SQLQueryToString(full_query2, entry); outf << full_query2 << std::endl; server_up &= processBuzzHouseQuery(full_query2); qo.setIntermediateStepSuccess(!have_error); } qo.optimizePeerTables(gen); full_query.resize(0); BuzzHouse::SQLQueryToString(full_query, sq1); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); qo.processFirstOracleQueryResult(!have_error, *ei); full_query2.resize(0); BuzzHouse::SQLQueryToString(full_query2, sq2); outf << full_query2 << std::endl; if (clickhouse_only) { has_success = ei->performQuery(BuzzHouse::PeerTableDatabase::ClickHouse, full_query2); } else { server_up &= processBuzzHouseQuery(full_query2); has_success = !have_error; } qo.processSecondOracleQueryResult(has_success, *ei, "Peer table query"); } else if (run_query && nopt < (correctness_oracle + settings_oracle + dump_oracle + peer_oracle + run_query + 1)) { gen.generateNextStatement(rg, sq1); BuzzHouse::SQLQueryToString(full_query, sq1); outf << full_query << std::endl; server_up &= processBuzzHouseQuery(full_query); gen.updateGenerator(sq1, *ei, !have_error); } else { chassert(0); } } } } return server_up; } #else bool Client::buzzHouse() { throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Clickhouse was compiled without BuzzHouse enabled"); } #endif }