Skip to content

Commit

Permalink
Merge pull request #1 from hmeriann/multi-statement
Browse files Browse the repository at this point in the history
Multi statement, Attach, Detach, Use statements support
  • Loading branch information
Tmonster authored Jun 13, 2024
2 parents 721460f + ff68a90 commit b0fce59
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 16 deletions.
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

0 comments on commit b0fce59

Please sign in to comment.