Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions src/fuzzyduck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ void FuzzyDuck::BeginFuzzing() {
if (seed == 0) {
seed = random_engine.NextRandomInteger();
}
random_engine.SetSeed(seed);
if (max_queries == 0) {
throw BinderException("Provide a max_queries argument greater than 0");
}
Expand Down Expand Up @@ -63,17 +64,24 @@ void FuzzyDuck::FuzzAllFunctions() {
}

string FuzzyDuck::GenerateQuery() {
LogTask("Generating query with seed " + to_string(seed));
auto &engine = RandomEngine::Get(context);
// set the seed
engine.SetSeed(seed);
// get the next seed
seed = engine.NextRandomInteger();

// generate the statement
StatementGenerator generator(context);
auto statement = generator.GenerateStatement();
return statement->ToString();
// 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));
for (idx_t i = 0; i < number_of_statements; i++) {
statement += generator.GenerateStatement()->ToString() + "; ";
}
} else {
// normal statement
LogTask("Generating Single-Statement query with seed " + to_string(seed));
statement = generator.GenerateStatement()->ToString();
}
return statement;
}

void sleep_thread(Connection *con, atomic<bool> *is_active, atomic<bool> *timed_out, idx_t timeout_duration) {
Expand Down
22 changes: 17 additions & 5 deletions src/include/statement_generator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
#pragma once

#include "duckdb.hpp"
#include "duckdb/parser/parsed_data/detach_info.hpp"
#include "duckdb/parser/query_node.hpp"

#define TESTING_DIRECTORY_NAME "duckdb_unittest_tempdir"

namespace duckdb {
class SQLStatement;
class SelectStatement;
class InsertStatement;
class UpdateStatement;
class DeleteStatement;
class SetStatement;
class TableRef;
class SelectNode;
class SetOperationNode;
Expand Down Expand Up @@ -47,14 +51,24 @@ class StatementGenerator {

vector<string> GenerateAllFunctionCalls();

private:
unique_ptr<SQLStatement> GenerateStatement(StatementType type);
//! Returns true with a percentage change (0-100)
bool RandomPercentage(idx_t percentage);
idx_t RandomValue(idx_t max);
string GetRandomAttachedDataBase();
unique_ptr<SQLStatement> GenerateStatement(StatementType type); // came from private

private:
unique_ptr<MultiStatement> GenerateAttachUse();
unique_ptr<SetStatement> GenerateSet();
unique_ptr<AttachStatement> GenerateAttach();
unique_ptr<DetachStatement> GenerateDetach();
unique_ptr<SelectStatement> GenerateSelect();
unique_ptr<CreateStatement> GenerateCreate();
unique_ptr<QueryNode> GenerateQueryNode();

unique_ptr<CreateInfo> GenerateCreateInfo();
unique_ptr<AttachInfo> GenerateAttachInfo();
unique_ptr<DetachInfo> GenerateDetachInfo();

void GenerateCTEs(QueryNode &node);
unique_ptr<TableRef> GenerateTableRef();
Expand Down Expand Up @@ -93,14 +107,12 @@ class StatementGenerator {
string GenerateCast(const LogicalType &target, const string &source_name, bool add_varchar);
bool FunctionArgumentsAlwaysNull(const string &name);

idx_t RandomValue(idx_t max);
bool RandomBoolean();
//! Returns true with a percentage change (0-100)
bool RandomPercentage(idx_t percentage);
string RandomString(idx_t length);
unique_ptr<ParsedExpression> RandomExpression(idx_t percentage);

//! Generate identifier for a column or parent using "t" or "c" prefixes. ie. t0, or c0
string GenerateDataBaseName();
string GenerateIdentifier();
string GenerateTableIdentifier();
string GenerateSchemaIdentifier();
Expand Down
116 changes: 114 additions & 2 deletions src/statement_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
#include "duckdb/common/random_engine.hpp"
#include "duckdb/common/types/uuid.hpp"
#include "duckdb/function/table/system_functions.hpp"
#include "duckdb/main/attached_database.hpp"
#include "duckdb/main/database_manager.hpp"
#include "duckdb/parser/expression/list.hpp"
#include "duckdb/parser/parsed_data/create_function_info.hpp"
#include "duckdb/parser/parsed_data/create_schema_info.hpp"
#include "duckdb/parser/parsed_data/create_table_info.hpp"
#include "duckdb/parser/parsed_data/create_view_info.hpp"
#include "duckdb/parser/parsed_data/create_function_info.hpp"
#include "duckdb/parser/parsed_data/create_type_info.hpp"
#include "duckdb/parser/parsed_data/create_view_info.hpp"
#include "duckdb/parser/parsed_expression_iterator.hpp"
#include "duckdb/parser/query_node/select_node.hpp"
#include "duckdb/parser/query_node/set_operation_node.hpp"
#include "duckdb/parser/statement/attach_statement.hpp"
#include "duckdb/parser/statement/create_statement.hpp"
#include "duckdb/parser/statement/delete_statement.hpp"
#include "duckdb/parser/statement/detach_statement.hpp"
#include "duckdb/parser/statement/insert_statement.hpp"
#include "duckdb/parser/statement/multi_statement.hpp"
#include "duckdb/parser/statement/select_statement.hpp"
#include "duckdb/parser/statement/set_statement.hpp"
#include "duckdb/parser/statement/update_statement.hpp"
#include "duckdb/parser/tableref/list.hpp"

Expand All @@ -28,6 +34,7 @@ struct GeneratorContext {
vector<reference<CatalogEntry>> table_functions;
vector<reference<CatalogEntry>> pragma_functions;
vector<reference<CatalogEntry>> tables_and_views;
vector<reference<AttachedDatabase>> attached_databases;
};

StatementGenerator::StatementGenerator(ClientContext &context) : context(context), parent(nullptr), depth(0) {
Expand All @@ -46,8 +53,14 @@ StatementGenerator::~StatementGenerator() {
}

std::shared_ptr<GeneratorContext> StatementGenerator::GetDatabaseState(ClientContext &context) {
// start a transaction so that catalog scans can take place.
if (!context.transaction.HasActiveTransaction()) {
context.transaction.BeginTransaction();
}
auto result = std::make_shared<GeneratorContext>();
result->test_types = TestAllTypesFun::GetTestTypes();
auto &db_manager = DatabaseManager::Get(context);
result->attached_databases = db_manager.GetDatabases(context);

auto schemas = Catalog::GetAllSchemas(context);
// extract the functions
Expand All @@ -73,13 +86,29 @@ std::shared_ptr<GeneratorContext> StatementGenerator::GetDatabaseState(ClientCon
result->tables_and_views.push_back(entry);
});
}
if (context.transaction.HasActiveTransaction()) {
context.transaction.Commit();
}
return result;
}

unique_ptr<SQLStatement> StatementGenerator::GenerateStatement() {
if (RandomPercentage(80)) {
return GenerateStatement(StatementType::SELECT_STATEMENT);
}
if (RandomPercentage(40)) {
if (RandomPercentage(50)) {
// We call this directly so we have a higher chance to fuzz persistent databases
return GenerateAttachUse();
}
return GenerateStatement(StatementType::ATTACH_STATEMENT);
}
if (RandomPercentage(60)) {
return GenerateStatement(StatementType::DETACH_STATEMENT);
}
if (RandomPercentage(30)) {
return GenerateStatement(StatementType::SET_STATEMENT);
}
return GenerateStatement(StatementType::CREATE_STATEMENT);
}

Expand All @@ -89,6 +118,13 @@ unique_ptr<SQLStatement> StatementGenerator::GenerateStatement(StatementType typ
return GenerateSelect();
case StatementType::CREATE_STATEMENT:
return GenerateCreate();
case StatementType::ATTACH_STATEMENT:
return GenerateAttach();
case StatementType::DETACH_STATEMENT:
return GenerateDetach();
// generate USE statement
case StatementType::SET_STATEMENT:
return GenerateSet();
default:
throw InternalException("Unsupported type");
}
Expand All @@ -109,6 +145,76 @@ unique_ptr<CreateStatement> StatementGenerator::GenerateCreate() {
return create;
}

unique_ptr<AttachStatement> StatementGenerator::GenerateAttach() {
auto attach = make_uniq<AttachStatement>();
attach->info = GenerateAttachInfo();
return attach;
}

unique_ptr<DetachStatement> StatementGenerator::GenerateDetach() {
auto detach = make_uniq<DetachStatement>();
detach->info = GenerateDetachInfo();
return detach;
}

// generate USE statement
unique_ptr<SetStatement> StatementGenerator::GenerateSet() {
auto name_expr = make_uniq<ConstantExpression>(GenerateDataBaseName());
if (RandomPercentage(90)) {
auto name = GetRandomAttachedDataBase();
name_expr = make_uniq<ConstantExpression>(Value(name));
}
auto set = make_uniq<SetVariableStatement>("schema", std::move(name_expr), SetScope::AUTOMATIC);
return set;
}

unique_ptr<MultiStatement> StatementGenerator::GenerateAttachUse() {
auto multi_statement = make_uniq<MultiStatement>();
multi_statement->statements.push_back(std::move(GenerateAttach()));
multi_statement->statements.push_back(std::move(GenerateSet()));
return multi_statement;
}

//===--------------------------------------------------------------------===//
// Generate Detach Info
//===--------------------------------------------------------------------===//

unique_ptr<DetachInfo> StatementGenerator::GenerateDetachInfo() {
auto info = make_uniq<DetachInfo>();
if (RandomPercentage(20)) {
info->name = "RANDOM_NAME_" + RandomString(15);
} else {
info->name = GetRandomAttachedDataBase();
}
return info;
}

std::string StatementGenerator::GetRandomAttachedDataBase() {
auto state = GetDatabaseState(context);
auto st_name = state->attached_databases[RandomValue(state->attached_databases.size())];
auto name = st_name.get().name;
return name;
}

//===--------------------------------------------------------------------===//
// Generate Attach Info
//===--------------------------------------------------------------------===//

unique_ptr<AttachInfo> StatementGenerator::GenerateAttachInfo() {
auto info = make_uniq<AttachInfo>();
auto fs = FileSystem::CreateLocal();
// check if the directory exists
if (!fs->DirectoryExists(TESTING_DIRECTORY_NAME)) {
fs->CreateDirectory(TESTING_DIRECTORY_NAME);
}
info->name = RandomString(10);
info->path = TESTING_DIRECTORY_NAME + string("/fuzz_gen_db_") + info->name + string(".db");
if (RandomPercentage(30)) {
info->options["READ_ONLY"] = Value(true);
}
return info;
}

//===--------------------------------------------------------------------===//
// Create Info Node
//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -937,6 +1043,12 @@ unique_ptr<ParsedExpression> StatementGenerator::GenerateLambda() {
return make_uniq<LambdaExpression>(std::move(lhs), std::move(rhs));
}

string StatementGenerator::GenerateDataBaseName() {
auto identifier = "DB" + to_string(GetIndex());
current_relation_names.push_back(identifier);
return identifier;
}

string StatementGenerator::GenerateTableIdentifier() {
auto identifier = "t" + to_string(GetIndex());
current_relation_names.push_back(identifier);
Expand Down