diff --git a/drivers/entry.go b/drivers/entry.go index 055d053..513e2af 100644 --- a/drivers/entry.go +++ b/drivers/entry.go @@ -1,7 +1,7 @@ package drivers import ( - "log" + "fmt" "github.com/PumpkinSeed/sqlfuzz/drivers/mysql" "github.com/PumpkinSeed/sqlfuzz/drivers/postgres" @@ -9,26 +9,24 @@ import ( ) // New creates a new driver instance based on the flags -func New(f types.Flags) types.Driver { +func New(f types.Flags) (types.Driver, error) { switch f.Driver { case "mysql": - return mysql.New(f) + return mysql.New(f), nil case "postgres": - return postgres.New(f) + return postgres.New(f), nil default: - log.Fatal("Driver not implemented") - return nil + return nil, fmt.Errorf("driver %q not implemented", f.Driver) } } -func NewTestable(f types.Flags) types.Testable { +func NewTestable(f types.Flags) (types.Testable, error) { switch f.Driver { case "mysql": - return mysql.New(f) + return mysql.New(f), nil case "postgres": - return postgres.New(f) + return postgres.New(f), nil default: - log.Fatal("Testable not implemented") - return nil + return nil, fmt.Errorf("testable %q not implemented", f.Driver) } } diff --git a/drivers/mysql/helpers.go b/drivers/mysql/helpers.go index 059874f..e776853 100644 --- a/drivers/mysql/helpers.go +++ b/drivers/mysql/helpers.go @@ -1,15 +1,16 @@ package mysql import ( + "fmt" "strconv" "strings" ) -func length(field string, t string) []int16 { +func length(field string, t string) ([]int16, error) { field = strings.ToLower(field) t = strings.ToLower(t) if !(strings.Contains(field, "(") && strings.Contains(field, ")")) { - return nil + return nil, nil } str := strings.ReplaceAll(field, t, "") str = strings.ReplaceAll(str, "(", "") @@ -21,15 +22,15 @@ func length(field string, t string) []int16 { for _, v := range p { data, err := strconv.Atoi(v) if err != nil { - panic(err) + return nil, fmt.Errorf("length: failed to parse %q: %w", v, err) } result = append(result, int16(data)) } - return result + return result, nil } v, err := strconv.Atoi(str) if err != nil { - panic(err) + return nil, fmt.Errorf("length: failed to parse %q: %w", str, err) } - return []int16{int16(v)} + return []int16{int16(v)}, nil } diff --git a/drivers/mysql/helpers_test.go b/drivers/mysql/helpers_test.go index 28a0a16..584a6f4 100644 --- a/drivers/mysql/helpers_test.go +++ b/drivers/mysql/helpers_test.go @@ -29,7 +29,11 @@ func TestLength(t *testing.T) { } for _, scenario := range scenarios { - out := length(scenario.input, scenario.t) + out, err := length(scenario.input, scenario.t) + if err != nil { + t.Errorf("Unexpected error for input %q: %v", scenario.input, err) + continue + } if !reflect.DeepEqual(scenario.output, out) { t.Errorf("Output doesn't match with the scenario: %v, out: %v", scenario.output, out) } diff --git a/drivers/mysql/mysql.go b/drivers/mysql/mysql.go index 866a152..086399f 100644 --- a/drivers/mysql/mysql.go +++ b/drivers/mysql/mysql.go @@ -11,11 +11,11 @@ import ( const ( MySQLDescribeTemplate = `select column_name, data_type, character_maximum_length, column_default, is_nullable,numeric_precision,numeric_scale,extra,column_key - from INFORMATION_SCHEMA.COLUMNS where table_name = '%s'` + from INFORMATION_SCHEMA.COLUMNS where table_name = ?` MySQLDescribeTableQuery = "SHOW TABLES;" - mysqlFKQuery = `SELECT CONSTRAINT_NAME,TABLE_NAME,COLUMN_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME - from INFORMATION_SCHEMA.KEY_COLUMN_USAGE - where REFERENCED_TABLE_NAME <> 'NULL' and REFERENCED_COLUMN_NAME <> 'NULL' and TABLE_NAME = '%s'` + mysqlFKQuery = `SELECT CONSTRAINT_NAME,TABLE_NAME,COLUMN_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME + from INFORMATION_SCHEMA.KEY_COLUMN_USAGE + where REFERENCED_TABLE_NAME <> 'NULL' and REFERENCED_COLUMN_NAME <> 'NULL' and TABLE_NAME = ?` ) var ( @@ -178,12 +178,11 @@ func (m MySQL) MapField(descriptor types.FieldDescriptor) types.Field { } func (MySQL) Describe(table string, db *sql.DB) ([]types.FieldDescriptor, error) { - describeQuery := fmt.Sprintf(MySQLDescribeTemplate, table) - results, err := db.Query(describeQuery) + results, err := db.Query(MySQLDescribeTemplate, table) if err != nil { return nil, err } - fkRows, err := db.Query(fmt.Sprintf(mysqlFKQuery, strings.ToLower(table))) + fkRows, err := db.Query(mysqlFKQuery, strings.ToLower(table)) if err != nil { return nil, err } @@ -214,7 +213,7 @@ func (m MySQL) MultiDescribe(tables []string, db *sql.DB) (tableToDescriptorMap } func (MySQL) GetLatestColumnValue(table, column string, db *sql.DB) (interface{}, error) { - query := fmt.Sprintf("select %v from %v order by %v desc limit 1", column, table, column) + query := fmt.Sprintf("select `%s` from `%s` order by `%s` desc limit 1", column, table, column) rows, err := db.Query(query) if err != nil { return nil, err diff --git a/drivers/postgres/postgres.go b/drivers/postgres/postgres.go index 19b7029..521d2d6 100644 --- a/drivers/postgres/postgres.go +++ b/drivers/postgres/postgres.go @@ -68,26 +68,26 @@ const CreateTable = `CREATE TABLE IF NOT EXISTS %s ( const ( PSQLDescribeTemplate = `select column_name, data_type, character_maximum_length, column_default, is_nullable,numeric_precision,numeric_scale - from INFORMATION_SCHEMA.COLUMNS where table_name = '%s'` + from INFORMATION_SCHEMA.COLUMNS where table_name = $1` PSQLConnectionTemplate = "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable" PSQLInsertTemplate = `INSERT INTO %s("%s") VALUES(%s)` PSQLShowTablesQuery = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema';" - psqlForeignKeysQuery = ` + psqlForeignKeysQuery = ` SELECT - tc.constraint_name, - tc.table_name, - kcu.column_name, + tc.constraint_name, + tc.table_name, + kcu.column_name, ccu.table_name AS foreign_table_name, - ccu.column_name AS foreign_column_name -FROM - information_schema.table_constraints AS tc + ccu.column_name AS foreign_column_name +FROM + information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema -WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name='%s' +WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name=$1 ` ) @@ -243,11 +243,11 @@ func (p Postgres) MultiDescribe(tables []string, db *sql.DB) (tableToDescriptorM } func (p Postgres) Describe(table string, db *sql.DB) ([]types.FieldDescriptor, error) { - results, err := db.Query(fmt.Sprintf(PSQLDescribeTemplate, strings.ToLower(table))) + results, err := db.Query(PSQLDescribeTemplate, strings.ToLower(table)) if err != nil { return nil, err } - fkResults, err := db.Query(fmt.Sprintf(psqlForeignKeysQuery, strings.ToLower(table))) + fkResults, err := db.Query(psqlForeignKeysQuery, strings.ToLower(table)) if err != nil { return nil, err } @@ -255,7 +255,7 @@ func (p Postgres) Describe(table string, db *sql.DB) ([]types.FieldDescriptor, e } func (p Postgres) GetLatestColumnValue(table, column string, db *sql.DB) (interface{}, error) { - query := fmt.Sprintf("select %s from %s order by %s desc limit 1", column, table, column) + query := fmt.Sprintf(`select "%s" from "%s" order by "%s" desc limit 1`, column, table, column) rows, err := db.Query(query) if err != nil { return nil, err diff --git a/drivers/utils/sqlcommons.go b/drivers/utils/sqlcommons.go index 7f705a3..1bc8306 100644 --- a/drivers/utils/sqlcommons.go +++ b/drivers/utils/sqlcommons.go @@ -31,7 +31,7 @@ func MultiDescribeHelper(tables []string, processedTables map[string]struct{}, d continue } foreignTableName := field.ForeignKeyDescriptor.ForeignTableName - if _, ok := processedTables[foreignTableName]; ok && !knownTables[foreignTableName] { + if _, ok := processedTables[foreignTableName]; !ok && !knownTables[foreignTableName] { newlyReferencedTables = append(newlyReferencedTables, foreignTableName) knownTables[foreignTableName] = true } diff --git a/go.mod b/go.mod index 6b8c4cf..476b8ca 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,16 @@ go 1.14 require ( github.com/brianvoe/gofakeit/v5 v5.10.1 - github.com/friendsofgo/errors v0.9.2 // indirect github.com/go-sql-driver/mysql v1.5.0 - github.com/gofrs/uuid v3.3.0+incompatible // indirect github.com/lib/pq v1.9.0 github.com/rs/xid v1.2.1 - github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/null v8.0.0+incompatible +) + +require ( + github.com/friendsofgo/errors v0.9.2 // indirect + github.com/gofrs/uuid v3.3.0+incompatible // indirect + github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/sqlboiler v3.7.1+incompatible // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect ) diff --git a/go.sum b/go.sum index 80ed359..bc877b3 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,9 @@ -github.com/brianvoe/gofakeit v1.2.0 h1:GGbzCqQx9ync4ObAUhRa3F/M73eL9VZL3X09WoTwphM= -github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= github.com/brianvoe/gofakeit/v5 v5.10.1 h1:XamPDOAIoxcjEaeE+B4VvRsXk/g5OXm4gReH2fJCxNU= github.com/brianvoe/gofakeit/v5 v5.10.1/go.mod h1:/ZENnKqX+XrN8SORLe/fu5lZDIo1tuPncWuRD+eyhSI= github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk= github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/gofrs/uuid v1.2.0 h1:coDhrjgyJaglxSjxuJdqQSSdUpG3w6p1OwN2od6frBU= github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= @@ -17,8 +14,8 @@ github.com/volatiletech/inflect v0.0.1 h1:2a6FcMQyhmPZcLa+uet3VJ8gLn/9svWhJxJYwv github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= github.com/volatiletech/null v8.0.0+incompatible h1:7wP8m5d/gZ6kW/9GnrLtMCRre2dlEnaQ9Km5OXlK4zg= github.com/volatiletech/null v8.0.0+incompatible/go.mod h1:0wD98JzdqB+rLyZ70fN05VDbXbafIb0KU0MdVhCzmOQ= -github.com/volatiletech/sqlboiler v1.0.0 h1:p0aCjD4tyDfjDjL6jakkfnrWWC4Z2wd1SNM7sS3rzAQ= github.com/volatiletech/sqlboiler v3.7.1+incompatible h1:dm9/NjDskQVwAarmpeZ2UqLn1NKE8M3WHSHBS4jw2x8= github.com/volatiletech/sqlboiler v3.7.1+incompatible/go.mod h1:jLfDkkHWPbS2cWRLkyC20vQWaIQsASEY7gM7zSo11Yw= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 9e56e8d..37cf06a 100644 --- a/main.go +++ b/main.go @@ -15,17 +15,21 @@ import ( func main() { f := flags.Get() gofakeit.Seed(int64(f.Seed)) - driver := drivers.New(f.Driver) - db := connector.Connection(driver, f) + driver, err := drivers.New(f.Driver) + if err != nil { + log.Fatal(err) + } + db, err := connector.Connection(driver, f) + if err != nil { + log.Fatal(err) + } defer db.Close() var tables []string if f.Table == "" { - var err error tables, err = driver.ShowTables(db) if err != nil { - log.Print(err) - return + log.Fatal(err) } } else { tables = []string{f.Table} @@ -34,13 +38,11 @@ func main() { f.Table = table fields, err := driver.Describe(f.Table, db) if err != nil { - log.Print(err.Error()) - return + log.Fatal(err) } t := time.Now() if err := fuzzer.Run(fields, f); err != nil { - log.Print(err.Error()) - return + log.Fatal(err) } log.Printf("Fuzzing %s table taken: %v \n", table, time.Since(t)) } diff --git a/main_test.go b/main_test.go index cdb859f..fb63b38 100644 --- a/main_test.go +++ b/main_test.go @@ -33,9 +33,18 @@ func TestFuzz(t *testing.T) { f.Seed = 1 gofakeit.Seed(int64(f.Seed)) - driver := drivers.New(f.Driver) - testable := drivers.NewTestable(f.Driver) - db := connector.Connection(driver, f) + driver, err := drivers.New(f.Driver) + if err != nil { + t.Fatal(err) + } + testable, err := drivers.NewTestable(f.Driver) + if err != nil { + t.Fatal(err) + } + db, err := connector.Connection(driver, f) + if err != nil { + t.Fatal(err) + } defer db.Close() if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", f.Table)); err != nil { t.Fatal(err) @@ -91,9 +100,18 @@ func TestFuzzPostgres(t *testing.T) { f.Seed = 1 gofakeit.Seed(int64(f.Seed)) - driver := drivers.New(f.Driver) - testable := drivers.NewTestable(f.Driver) - db := connector.Connection(driver, f) + driver, err := drivers.New(f.Driver) + if err != nil { + t.Fatal(err) + } + testable, err := drivers.NewTestable(f.Driver) + if err != nil { + t.Fatal(err) + } + db, err := connector.Connection(driver, f) + if err != nil { + t.Fatal(err) + } defer db.Close() if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", f.Table)); err != nil { t.Fatal(err) @@ -159,13 +177,22 @@ func TestMysqlMultiInsert(t *testing.T) { f.Seed = 1 gofakeit.Seed(int64(f.Seed)) - driver := drivers.New(f.Driver) - testable := drivers.NewTestable(f.Driver) + driver, err := drivers.New(f.Driver) + if err != nil { + t.Fatal(err) + } + testable, err := drivers.NewTestable(f.Driver) + if err != nil { + t.Fatal(err) + } test, err := testable.GetTestCase("multi") if err != nil { - t.Error(fmt.Sprintf("postgres : error fetching test case for multi. %v", err.Error())) + t.Fatal(fmt.Sprintf("mysql: error fetching test case for multi. %v", err.Error())) + } + db, err := connector.Connection(driver, f) + if err != nil { + t.Fatal(err) } - db := connector.Connection(driver, f) defer db.Close() if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", f.Table)); err != nil { t.Fatal(err) @@ -201,13 +228,22 @@ func TestPostgresMultiInsert(t *testing.T) { f.Seed = 1 gofakeit.Seed(int64(f.Seed)) - driver := drivers.New(f.Driver) - testable := drivers.NewTestable(f.Driver) + driver, err := drivers.New(f.Driver) + if err != nil { + t.Fatal(err) + } + testable, err := drivers.NewTestable(f.Driver) + if err != nil { + t.Fatal(err) + } test, err := testable.GetTestCase("multi") if err != nil { - t.Error(fmt.Sprintf("postgres : error fetching test case for multi. %v", err.Error())) + t.Fatal(fmt.Sprintf("postgres: error fetching test case for multi. %v", err.Error())) + } + db, err := connector.Connection(driver, f) + if err != nil { + t.Fatal(err) } - db := connector.Connection(driver, f) defer db.Close() if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", f.Table)); err != nil { t.Fatal(err) diff --git a/pkg/action/action.go b/pkg/action/action.go index d6b9e5a..c922935 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -8,7 +8,6 @@ import ( "fmt" "log" "math" - "math/rand" "strconv" "strings" "time" @@ -75,9 +74,10 @@ func (sqlInsertInput SQLInsertInput) multiInsert() error { if foreignTableFields, ok := tableFieldValuesMap[field.ForeignKeyDescriptor.ForeignTableName]; ok { if val, ok := foreignTableFields[field.ForeignKeyDescriptor.ForeignColumnName]; ok { values = append(values, val) - continue // TODO this can be an error + continue } } + // Foreign key value not found in cache — fall through to DB lookup val, err := multiInsertParams.Driver.GetLatestColumnValue( field.ForeignKeyDescriptor.ForeignTableName, field.ForeignKeyDescriptor.ForeignColumnName, @@ -86,8 +86,11 @@ func (sqlInsertInput SQLInsertInput) multiInsert() error { if err != nil { return err } + if val == nil { + return fmt.Errorf("action: no foreign key value found for %s.%s referenced by %s", + field.ForeignKeyDescriptor.ForeignTableName, field.ForeignKeyDescriptor.ForeignColumnName, field.Field) + } values = append(values, val) - // Get from table. If no value present in table as well, throw error. } query := multiInsertParams.Driver.Insert(f, table) _, err := multiInsertParams.DB.Exec(query, values...) @@ -202,17 +205,15 @@ func generateData(driver types.Driver, fieldDescriptor types.FieldDescriptor) in return nil } -// randomString generates a length size random string +// randomString generates a length size random string using gofakeit's seeded RNG +// so that results are reproducible when a seed is set func randomString(length int16) string { var charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - var seededRand = rand.New( - rand.NewSource(time.Now().UnixNano())) - b := make([]byte, length) for i := range b { - b[i] = charset[seededRand.Intn(len(charset))] + b[i] = charset[gofakeit.Number(0, len(charset)-1)] } return string(b) diff --git a/pkg/connector/connector.go b/pkg/connector/connector.go index 001e199..84e555b 100644 --- a/pkg/connector/connector.go +++ b/pkg/connector/connector.go @@ -2,24 +2,22 @@ package connector import ( "database/sql" - "log" "github.com/PumpkinSeed/sqlfuzz/drivers/types" "github.com/PumpkinSeed/sqlfuzz/pkg/flags" _ "github.com/lib/pq" ) -// Connection building a singleton connection to the database for give driver -func Connection(d types.Driver, f flags.Flags) *sql.DB { +// Connection builds a connection to the database for the given driver +func Connection(d types.Driver, f flags.Flags) (*sql.DB, error) { db, err := connect(d) if err != nil { - log.Fatal(err) - return nil + return nil, err } db.SetConnMaxLifetime(f.ConnMaxLifetimeInSec) db.SetMaxIdleConns(f.MaxIdleConns) db.SetMaxOpenConns(f.MaxOpenConns) - return db + return db, nil } // connect doing the direct connection open to the SQL database diff --git a/pkg/fuzzer/runner.go b/pkg/fuzzer/runner.go index 64e59b7..5e93870 100644 --- a/pkg/fuzzer/runner.go +++ b/pkg/fuzzer/runner.go @@ -1,7 +1,7 @@ package fuzzer import ( - "database/sql" + "fmt" "log" "sync" @@ -13,20 +13,26 @@ import ( _ "github.com/lib/pq" ) -func getDriverAndDB(f flags.Flags) (types.Driver, *sql.DB) { - driver := drivers.New(f.Driver) - db := connector.Connection(driver, f) - return driver, db +func getDriverAndDB(f flags.Flags) (types.Driver, error) { + driver, err := drivers.New(f.Driver) + if err != nil { + return nil, fmt.Errorf("fuzzer: %w", err) + } + return driver, nil } func runHelper(f flags.Flags, input action.SQLInsertInput) error { numJobs := f.Num workers := f.Workers jobs := make(chan struct{}, numJobs) + + var mu sync.Mutex + var firstErr error + wg := &sync.WaitGroup{} wg.Add(workers) for w := 0; w < workers; w++ { - go worker(jobs, wg, f, input) + go worker(jobs, wg, &mu, &firstErr, input) } for j := 0; j < numJobs; j++ { @@ -35,28 +41,33 @@ func runHelper(f flags.Flags, input action.SQLInsertInput) error { close(jobs) wg.Wait() - return nil + return firstErr } -func worker(jobs <-chan struct{}, wg *sync.WaitGroup, f flags.Flags, input action.SQLInsertInput) { +func worker(jobs <-chan struct{}, wg *sync.WaitGroup, mu *sync.Mutex, firstErr *error, input action.SQLInsertInput) { defer wg.Done() - driver := drivers.New(f.Driver) - db := connector.Connection(driver, f) - defer func() { - if err := db.Close(); err != nil { - log.Print(err) - } - }() for range jobs { if err := input.Insert(); err != nil { log.Println(err) + mu.Lock() + if *firstErr == nil { + *firstErr = err + } + mu.Unlock() } } } // Run the commands in a worker pool func Run(fields []types.FieldDescriptor, f flags.Flags) error { - driver, db := getDriverAndDB(f) + driver, err := getDriverAndDB(f) + if err != nil { + return err + } + db, err := connector.Connection(driver, f) + if err != nil { + return fmt.Errorf("fuzzer: %w", err) + } defer func() { if err := db.Close(); err != nil { log.Print(err) @@ -74,7 +85,14 @@ func Run(fields []types.FieldDescriptor, f flags.Flags) error { } func RunMulti(tableToFieldsMap map[string][]types.FieldDescriptor, insertionOrder []string, f flags.Flags) error { - driver, db := getDriverAndDB(f) + driver, err := getDriverAndDB(f) + if err != nil { + return err + } + db, err := connector.Connection(driver, f) + if err != nil { + return fmt.Errorf("fuzzer: %w", err) + } defer func() { if err := db.Close(); err != nil { log.Print(err)