diff --git a/.gitmodules b/.gitmodules index 01b02cb..86e8a7e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,4 +5,4 @@ [submodule "extension-ci-tools"] path = extension-ci-tools url = https://github.com/duckdb/extension-ci-tools - branch = main \ No newline at end of file + branch = main diff --git a/src/fuzzyduck.cpp b/src/fuzzyduck.cpp index b7ff650..c7ee8ec 100644 --- a/src/fuzzyduck.cpp +++ b/src/fuzzyduck.cpp @@ -26,7 +26,10 @@ void FuzzyDuck::BeginFuzzing() { auto &fs = FileSystem::GetFileSystem(context); TryRemoveFile(complete_log); complete_log_handle = - fs.OpenFile(complete_log, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW); + fs.OpenFile(complete_log, FileFlags::FILE_FLAGS_WRITE | FileFlags::FILE_FLAGS_FILE_CREATE_NEW); + } + if (enable_verification) { + RunQuery("PRAGMA enable_verification"); } } @@ -64,15 +67,16 @@ void FuzzyDuck::FuzzAllFunctions() { } string FuzzyDuck::GenerateQuery() { - // generate the statement + // generate statement StatementGenerator generator(context); + generator.verification_enabled = enable_verification; // accumulate statement(s) auto statement = string(""); if (generator.RandomPercentage(10)) { // multi statement idx_t number_of_statements = generator.RandomValue(1000); LogTask("Generating Multi-Statement query of " + to_string(number_of_statements) + " statements with seed " + - to_string(seed)); + to_string(seed)); for (idx_t i = 0; i < number_of_statements; i++) { statement += generator.GenerateStatement()->ToString() + "; "; } @@ -157,6 +161,7 @@ void FuzzyDuck::LogToCurrent(const string &message) { file->Sync(); file->Close(); } + void FuzzyDuck::LogToComplete(const string &message) { if (!complete_log_handle) { return; diff --git a/src/include/fuzzyduck.hpp b/src/include/fuzzyduck.hpp index 4393e87..e6d5363 100644 --- a/src/include/fuzzyduck.hpp +++ b/src/include/fuzzyduck.hpp @@ -25,6 +25,7 @@ class FuzzyDuck { string complete_log; string log; bool verbose_output = false; + bool enable_verification = false; idx_t timeout = 30; public: diff --git a/src/include/statement_generator.hpp b/src/include/statement_generator.hpp index 2f361bb..dfc5e22 100644 --- a/src/include/statement_generator.hpp +++ b/src/include/statement_generator.hpp @@ -53,6 +53,7 @@ class StatementGenerator { //! Returns true with a percentage change (0-100) bool RandomPercentage(idx_t percentage); + bool verification_enabled = false; idx_t RandomValue(idx_t max); string GetRandomAttachedDataBase(); unique_ptr GenerateStatement(StatementType type); // came from private @@ -63,9 +64,9 @@ class StatementGenerator { unique_ptr GenerateDelete(); unique_ptr GenerateDetach(); unique_ptr GenerateAttachUse(); - unique_ptr GenerateSelect(); unique_ptr GenerateSet(); + unique_ptr GenerateSelect(); unique_ptr GenerateQueryNode(); unique_ptr GenerateAttachInfo(); @@ -99,6 +100,8 @@ class StatementGenerator { unique_ptr GenerateCase(); unique_ptr GenerateOrderBy(); + unique_ptr GenerateOrderByAll(); + LogicalType GenerateLogicalType(); diff --git a/src/sqlsmith_extension.cpp b/src/sqlsmith_extension.cpp index d88f4fe..f995e73 100644 --- a/src/sqlsmith_extension.cpp +++ b/src/sqlsmith_extension.cpp @@ -22,6 +22,7 @@ struct SQLSmithFunctionData : public TableFunctionData { bool dump_all_queries = false; bool dump_all_graphs = false; bool verbose_output = false; + bool enable_verification = false; string complete_log; string log; bool finished = false; @@ -67,6 +68,7 @@ static void SQLSmithFunction(ClientContext &context, TableFunctionInput &data_p, options.dump_all_queries = data.dump_all_queries; options.dump_all_graphs = data.dump_all_graphs; options.verbose_output = data.verbose_output; + options.enable_verification = data.enable_verification; options.complete_log = data.complete_log; options.log = data.log; duckdb_sqlsmith::run_sqlsmith(DatabaseInstance::GetDatabase(context), options); @@ -139,6 +141,8 @@ static duckdb::unique_ptr FuzzyDuckBind(ClientContext &context, Ta result->fuzzer.log = StringValue::Get(kv.second); } else if (kv.first == "verbose_output") { result->fuzzer.verbose_output = BooleanValue::Get(kv.second); + } else if (kv.first == "enable_verification") { + result->fuzzer.enable_verification = BooleanValue::Get(kv.second); } } return_types.emplace_back(LogicalType::BOOLEAN); @@ -186,6 +190,7 @@ void SqlsmithExtension::Load(DuckDB &db) { fuzzy_duck_fun.named_parameters["log"] = LogicalType::VARCHAR; fuzzy_duck_fun.named_parameters["complete_log"] = LogicalType::VARCHAR; fuzzy_duck_fun.named_parameters["verbose_output"] = LogicalType::BOOLEAN; + fuzzy_duck_fun.named_parameters["enable_verification"] = LogicalType::BOOLEAN; ExtensionUtil::RegisterFunction(db_instance, fuzzy_duck_fun); TableFunction fuzz_all_functions("fuzz_all_functions", {}, FuzzAllFunctions, FuzzyDuckBind); diff --git a/src/statement_generator.cpp b/src/statement_generator.cpp index 93ea2b5..fe85e48 100644 --- a/src/statement_generator.cpp +++ b/src/statement_generator.cpp @@ -42,8 +42,8 @@ StatementGenerator::StatementGenerator(ClientContext &context) : context(context } StatementGenerator::StatementGenerator(StatementGenerator &parent_p) - : context(parent_p.context), parent(&parent_p), generator_context(parent_p.generator_context), - depth(parent_p.depth + 1) { + : verification_enabled(parent_p.verification_enabled), context(parent_p.context), parent(&parent_p), + generator_context(parent_p.generator_context), depth(parent_p.depth + 1) { if (depth > MAX_DEPTH) { throw InternalException("depth too high"); } @@ -393,10 +393,12 @@ unique_ptr StatementGenerator::GenerateQueryNode() { if (is_distinct) { result->modifiers.push_back(make_uniq()); } - if (RandomPercentage(20)) { - result->modifiers.push_back(GenerateOrderBy()); - } - if (RandomPercentage(20)) { + if (verification_enabled) { + result->modifiers.push_back(GenerateOrderByAll()); + } else if (!verification_enabled) { + if (RandomPercentage(5)) { + result->modifiers.push_back(GenerateOrderBy()); + } if (RandomPercentage(50)) { auto limit_percent_modifier = make_uniq(); if (RandomPercentage(30)) { @@ -753,8 +755,8 @@ unique_ptr StatementGenerator::GenerateFunction() { switch (function.type) { case CatalogType::SCALAR_FUNCTION_ENTRY: { auto &scalar_entry = function.Cast(); - auto actual_function = scalar_entry.functions.GetFunctionByOffset(RandomValue(scalar_entry.functions.Size())); - + auto offset = RandomValue(scalar_entry.functions.Size()); + auto actual_function = scalar_entry.functions.GetFunctionByOffset(offset); name = scalar_entry.name; arguments = actual_function.arguments; min_parameters = actual_function.arguments.size(); @@ -767,7 +769,7 @@ unique_ptr StatementGenerator::GenerateFunction() { case CatalogType::AGGREGATE_FUNCTION_ENTRY: { auto &aggregate_entry = function.Cast(); auto actual_function = - aggregate_entry.functions.GetFunctionByOffset(RandomValue(aggregate_entry.functions.Size())); + aggregate_entry.functions.GetFunctionByOffset(RandomValue(aggregate_entry.functions.Size())); name = aggregate_entry.name; min_parameters = actual_function.arguments.size(); @@ -816,19 +818,27 @@ unique_ptr StatementGenerator::GenerateFunction() { distinct); } +unique_ptr StatementGenerator::GenerateOrderByAll() { + auto result = make_uniq(); + auto order_type = Choose({OrderType::ASCENDING, OrderType::DESCENDING, OrderType::ORDER_DEFAULT}); + auto null_type = Choose( + {OrderByNullType::NULLS_FIRST, OrderByNullType::NULLS_LAST, OrderByNullType::ORDER_DEFAULT}); + result->orders.emplace_back(order_type, null_type, GenerateStar()); + return result; +} + unique_ptr StatementGenerator::GenerateOrderBy() { - auto result = make_uniq(); + auto result = make_uniq(); while (true) { auto order_type = Choose({OrderType::ASCENDING, OrderType::DESCENDING, OrderType::ORDER_DEFAULT}); auto null_type = Choose( - {OrderByNullType::NULLS_FIRST, OrderByNullType::NULLS_LAST, OrderByNullType::ORDER_DEFAULT}); + {OrderByNullType::NULLS_FIRST, OrderByNullType::NULLS_LAST, OrderByNullType::ORDER_DEFAULT}); result->orders.emplace_back(order_type, null_type, GenerateExpression()); - // continue with a random chance if (RandomPercentage(50)) { break; } } - return result; + return result; } unique_ptr StatementGenerator::GenerateOperator() { @@ -1044,25 +1054,26 @@ unique_ptr StatementGenerator::GenerateStar() { result->relation_name = GenerateRelationName(); } } - - while (RandomPercentage(20)) { - auto column_name = GenerateColumnName(); - if (column_name.empty()) { - break; + if (!verification_enabled) { + while (RandomPercentage(20)) { + auto column_name = GenerateColumnName(); + if (column_name.empty()) { + break; + } + result->exclude_list.insert(column_name); } - result->exclude_list.insert(column_name); - } - while (RandomPercentage(20)) { - auto column_name = GenerateColumnName(); - if (column_name.empty()) { - break; + while (RandomPercentage(20)) { + auto column_name = GenerateColumnName(); + if (column_name.empty()) { + break; + } + result->replace_list.insert(make_pair(column_name, GenerateExpression())); } - result->replace_list.insert(make_pair(column_name, GenerateExpression())); - } - if (RandomPercentage(50) || expression_depth > 0) { - result->columns = true; - if (RandomPercentage(50)) { - result->expr = GenerateLambda(); + if (RandomPercentage(50) || expression_depth > 0) { + result->columns = true; + if (RandomPercentage(50)) { + result->expr = GenerateLambda(); + } } } return std::move(result); @@ -1121,7 +1132,6 @@ unique_ptr StatementGenerator::GenerateSubquery() { return GenerateConstant(); } auto subquery = make_uniq(); - { StatementGenerator child_generator(*this); subquery->subquery = unique_ptr_cast(child_generator.GenerateSelect()); diff --git a/src/third_party/sqlsmith/include/sqlsmith.hh b/src/third_party/sqlsmith/include/sqlsmith.hh index 4bcf23b..cd00d8b 100644 --- a/src/third_party/sqlsmith/include/sqlsmith.hh +++ b/src/third_party/sqlsmith/include/sqlsmith.hh @@ -16,6 +16,7 @@ struct SQLSmithOptions { bool dump_all_queries = false; bool dump_all_graphs = false; bool verbose_output = false; + bool enable_verification = false; std::string complete_log; std::string log; };