From 2d3ec078c9c6d082897d5f6d92a0d9643cbb7ef2 Mon Sep 17 00:00:00 2001 From: Krystian Chmura <11900380+slsyy@users.noreply.github.com> Date: Sun, 22 Dec 2024 22:59:04 +0100 Subject: [PATCH] feat(mysql): make multistatements parameter optional (#249) The #208 PR introduced an optimisation, which requires the `multiStatements=true` parameter in connection string to work properly It looks like that the requirement for that parameter (which is stated in `README.md`) was not honored by users (e.g #240), so I want to keep the old behavior for convenience This commit: * remove mention about parameter requirement from `README.md` * revert default to `one query per one statement` * allows to keep `one query per multiple statements` logic with a `testfixtures.AllowMultipleStatementsInOneQuery` flag * some tests for both use cases --- README.md | 17 ++++++++++++++--- docker-compose.yml | 2 +- mysql.go | 29 ++++++++++++++++++++++++++--- mysql_test.go | 8 +++++++- testfixtures.go | 17 +++++++++++++++++ 5 files changed, 65 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 94cd035..239d7a0 100644 --- a/README.md +++ b/README.md @@ -399,9 +399,6 @@ Tested using the [github.com/lib/pq](https://github.com/lib/pq) and ### MySQL / MariaDB -Just make sure the connection string have -[the multistatement parameter](https://github.com/go-sql-driver/mysql#multistatements) -set to true, and use: ```go testfixtures.New( @@ -412,6 +409,20 @@ testfixtures.New( Tested using the [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) driver. +#### Multistatements parameter + +You can use [the multistatement parameter](https://github.com/go-sql-driver/mysql#multistatements) in the connection +string to execute some of the setup statements in one query (instead of one query per statement) for a faster execution. + + +```go +testfixtures.New( + ... + testfixtures.Dialect("mysql"), // or "mariadb" + testfixtures.AllowMultipleStatementsInOneQuery(), +) +``` + ### SQLite SQLite is also supported. It is recommended to create foreign keys as diff --git a/docker-compose.yml b/docker-compose.yml index 7266c20..2992753 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: environment: PGPASSWORD: postgres PG_CONN_STRING: host=postgresql user=postgres dbname=testfixtures_test port=5432 sslmode=disable - MYSQL_CONN_STRING: root:mariadb@tcp(mariadb)/testfixtures_test?multiStatements=true + MYSQL_CONN_STRING: root:mariadb@tcp(mariadb)/testfixtures_test SQLITE_CONN_STRING: testfixtures_test.sqlite3 SQLSERVER_CONN_STRING: server=sqlserver;database=master;user id=sa;password=SQL@1server;encrypt=disable CRDB_CONN_STRING: host=cockroachdb user=root dbname=defaultdb port=26257 sslmode=disable diff --git a/mysql.go b/mysql.go index 843afec..8de9c03 100644 --- a/mysql.go +++ b/mysql.go @@ -9,8 +9,9 @@ import ( type mySQL struct { baseHelper - skipResetSequences bool - resetSequencesTo int64 + skipResetSequences bool + resetSequencesTo int64 + allowMultipleStatementsInOneQuery bool tables []string tablesChecksum map[string]int64 @@ -113,14 +114,36 @@ func (h *mySQL) resetSequences(db *sql.DB) error { resetSequencesTo = 10000 } + if h.allowMultipleStatementsInOneQuery { + return h.resetSequencesInOneQuery(db, resetSequencesTo) + } + return h.resetSequencesInMultipleQueries(db, resetSequencesTo) + +} + +func (h *mySQL) resetSequencesInOneQuery(db *sql.DB, resetSequencesTo int64) error { b := strings.Builder{} for _, t := range h.tables { - b.WriteString(fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d;", h.quoteKeyword(t), resetSequencesTo)) + b.WriteString(h.makeResetSequenceQuery(t, resetSequencesTo)) } _, err := db.Exec(b.String()) return err } +func (h *mySQL) resetSequencesInMultipleQueries(db *sql.DB, resetSequencesTo int64) error { + for _, t := range h.tables { + _, err := db.Exec(h.makeResetSequenceQuery(t, resetSequencesTo)) + if err != nil { + return err + } + } + return nil +} + +func (h *mySQL) makeResetSequenceQuery(tableName string, resetSequencesTo int64) string { + return fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d;", h.quoteKeyword(tableName), resetSequencesTo) +} + func (h *mySQL) isTableModified(q queryable, tableName string) (bool, error) { oldChecksum, found := h.tablesChecksum[tableName] if !found { diff --git a/mysql_test.go b/mysql_test.go index a6b51a2..61fec87 100644 --- a/mysql_test.go +++ b/mysql_test.go @@ -11,6 +11,12 @@ import ( func TestMySQL(t *testing.T) { db := openDB(t, "mysql", os.Getenv("MYSQL_CONN_STRING")) - loadSchemaInOneQuery(t, db, "testdata/schema/mysql.sql") + loadSchemaInBatchesBySplitter(t, db, "testdata/schema/mysql.sql", []byte(";\n")) testLoader(t, db, "mysql") } + +func TestMySQLWithMultipleStatementsSupport(t *testing.T) { + db := openDB(t, "mysql", os.Getenv("MYSQL_CONN_STRING")+"?multiStatements=true") + loadSchemaInOneQuery(t, db, "testdata/schema/mysql.sql") + testLoader(t, db, "mysql", AllowMultipleStatementsInOneQuery()) +} diff --git a/testfixtures.go b/testfixtures.go index 9376ae1..065221e 100644 --- a/testfixtures.go +++ b/testfixtures.go @@ -194,6 +194,23 @@ func SkipResetSequences() func(*Loader) error { } } +// AllowMultipleStatementsInOneQuery is a performance tweak for running some setup statements in one query for better performance. +// +// Your database and connection must be configured to support it: https://github.com/go-sql-driver/mysql?tab=readme-ov-file#multistatements +// +// Only valid for MySQL as it is not enabled by default. +func AllowMultipleStatementsInOneQuery() func(*Loader) error { + return func(l *Loader) error { + switch helper := l.helper.(type) { + case *mySQL: + helper.allowMultipleStatementsInOneQuery = true + default: + return fmt.Errorf("testfixtures: AllowMultipleStatementsInOneQuery is valid for MySQL database") + } + return nil + } +} + // ResetSequencesTo sets the value the sequences will be reset to. // // Defaults to 10000.