From 25367f37caa6b23c27cac92fa14e2579e5621fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net> Date: Sat, 1 Aug 2015 07:30:03 -0500 Subject: [PATCH] Provide Driver() method for SQL database transactions. This can be used to execute raw SQL queries within a transaction block. Closes #57. --- mysql/database.go | 7 +------ mysql/database_test.go | 14 ++++++++----- mysql/tx.go | 39 +++++++++++++++++++++++++++++++++++++ postgresql/database.go | 7 +------ postgresql/database_test.go | 12 +++++++++--- postgresql/tx.go | 39 +++++++++++++++++++++++++++++++++++++ ql/database.go | 7 +------ ql/database_test.go | 12 +++++++++--- ql/tx.go | 39 +++++++++++++++++++++++++++++++++++++ sqlite/database.go | 7 +------ sqlite/database_test.go | 12 +++++++++--- sqlite/tx.go | 39 +++++++++++++++++++++++++++++++++++++ 12 files changed, 196 insertions(+), 38 deletions(-) create mode 100644 mysql/tx.go create mode 100644 postgresql/tx.go create mode 100644 ql/tx.go create mode 100644 sqlite/tx.go diff --git a/mysql/database.go b/mysql/database.go index 85fd6a68..a392a08a 100644 --- a/mysql/database.go +++ b/mysql/database.go @@ -49,11 +49,6 @@ type database struct { cachedStatements *cache.Cache } -type tx struct { - *sqltx.Tx - *database -} - type cachedStatement struct { *sqlx.Stmt query string @@ -357,7 +352,7 @@ func (d *database) Transaction() (db.Tx, error) { clone.tx = sqltx.New(sqlTx) - return tx{Tx: clone.tx, database: clone}, nil + return &tx{Tx: clone.tx, database: clone}, nil } // Exec compiles and executes a statement that does not return any rows. diff --git a/mysql/database_test.go b/mysql/database_test.go index 0dc9f8d6..010cc476 100644 --- a/mysql/database_test.go +++ b/mysql/database_test.go @@ -25,7 +25,6 @@ import ( "database/sql" "errors" "fmt" - "log" "math/rand" "os" "reflect" @@ -617,7 +616,6 @@ func TestResultFetch(t *testing.T) { } if err == nil { - log.Println("rowMap[id]:", rowMap["id"], reflect.TypeOf(rowMap["id"])) if pk, ok := rowMap["id"].(int64); !ok || pk == 0 { t.Fatalf("Expecting a not null ID.") } @@ -1301,6 +1299,12 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } + // Won't fail + sqlxTx := tx.Driver().(*sqlx.Tx) + if _, err = sqlxTx.Exec("INSERT INTO `artist` (`id`, `name`) VALUES(?, ?)", 4, "Fourth"); err != nil { + t.Fatal(err) + } + if err = tx.Commit(); err != nil { t.Fatal(err) } @@ -1309,7 +1313,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatalf("Should have failed, as we've already commited.") } - // Let's verify we have 3 rows. + // Let's verify we have 4 rows. if artist, err = sess.Collection("artist"); err != nil { t.Fatal(err) } @@ -1318,8 +1322,8 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if count != 3 { - t.Fatalf("Expecting 3 elements.") + if count != 4 { + t.Fatalf("Expecting exactly 4 results.") } } diff --git a/mysql/tx.go b/mysql/tx.go new file mode 100644 index 00000000..9d31c083 --- /dev/null +++ b/mysql/tx.go @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package mysql + +import ( + "upper.io/db/util/sqlutil/tx" +) + +type tx struct { + *sqltx.Tx + *database +} + +// Driver returns the current transaction session. +func (t *tx) Driver() interface{} { + if t != nil && t.Tx != nil { + return t.Tx.Tx + } + return nil +} diff --git a/postgresql/database.go b/postgresql/database.go index b8048a92..42fe1bbb 100644 --- a/postgresql/database.go +++ b/postgresql/database.go @@ -50,11 +50,6 @@ type database struct { cachedStatements *cache.Cache } -type tx struct { - *sqltx.Tx - *database -} - type cachedStatement struct { *sqlx.Stmt query string @@ -348,7 +343,7 @@ func (d *database) Transaction() (db.Tx, error) { clone.tx = sqltx.New(sqlTx) - return tx{Tx: clone.tx, database: clone}, nil + return &tx{Tx: clone.tx, database: clone}, nil } // Exec compiles and executes a statement that does not return any rows. diff --git a/postgresql/database_test.go b/postgresql/database_test.go index 65c3443d..3abfbde7 100644 --- a/postgresql/database_test.go +++ b/postgresql/database_test.go @@ -1495,6 +1495,12 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } + // Won't fail + sqlTx := tx.Driver().(*sqlx.Tx) + if _, err = sqlTx.Exec(`INSERT INTO "artist" ("id", "name") VALUES($1, $2)`, 4, "Fourth"); err != nil { + t.Fatal(err) + } + if err = tx.Commit(); err != nil { t.Fatal(err) } @@ -1503,7 +1509,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatalf("Should have failed, as we've already commited.") } - // Let's verify we have 3 rows. + // Let's verify we have 4 rows. if artist, err = sess.Collection("artist"); err != nil { t.Fatal(err) } @@ -1512,8 +1518,8 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if count != 3 { - t.Fatalf("Expecting only one element.") + if count != 4 { + t.Fatalf("Expecting exactly 4 results.") } } diff --git a/postgresql/tx.go b/postgresql/tx.go new file mode 100644 index 00000000..ed0eea5f --- /dev/null +++ b/postgresql/tx.go @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package postgresql + +import ( + "upper.io/db/util/sqlutil/tx" +) + +type tx struct { + *sqltx.Tx + *database +} + +// Driver returns the current transaction session. +func (t *tx) Driver() interface{} { + if t != nil && t.Tx != nil { + return t.Tx.Tx + } + return nil +} diff --git a/ql/database.go b/ql/database.go index e091fe7b..590c37f5 100644 --- a/ql/database.go +++ b/ql/database.go @@ -50,11 +50,6 @@ type database struct { cachedStatements *cache.Cache } -type tx struct { - *sqltx.Tx - *database -} - type cachedStatement struct { *sqlx.Stmt query string @@ -333,7 +328,7 @@ func (d *database) Transaction() (db.Tx, error) { clone.tx = sqltx.New(sqlTx) - return tx{Tx: clone.tx, database: clone}, nil + return &tx{Tx: clone.tx, database: clone}, nil } // Exec compiles and executes a statement that does not return any rows. diff --git a/ql/database_test.go b/ql/database_test.go index 326f7691..7bdf01fc 100644 --- a/ql/database_test.go +++ b/ql/database_test.go @@ -1123,6 +1123,12 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } + // Won't fail + sqlxTx := tx.Driver().(*sqlx.Tx) + if _, err = sqlxTx.Exec(`INSERT INTO artist (name) VALUES($1)`, "Fourth"); err != nil { + t.Fatal(err) + } + if err = tx.Commit(); err != nil { t.Fatal(err) } @@ -1131,7 +1137,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatalf("Should have failed, as we've already commited.") } - // Let's verify we have 3 rows. + // Let's verify we have 4 rows. if artist, err = sess.Collection("artist"); err != nil { t.Fatal(err) } @@ -1140,8 +1146,8 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if count != 3 { - t.Fatalf("Expecting only one element.") + if count != 4 { + t.Fatalf("Expecting exactly 4 results.") } } diff --git a/ql/tx.go b/ql/tx.go new file mode 100644 index 00000000..7cc58c37 --- /dev/null +++ b/ql/tx.go @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package ql + +import ( + "upper.io/db/util/sqlutil/tx" +) + +type tx struct { + *sqltx.Tx + *database +} + +// Driver returns the current transaction session. +func (t *tx) Driver() interface{} { + if t != nil && t.Tx != nil { + return t.Tx.Tx + } + return nil +} diff --git a/sqlite/database.go b/sqlite/database.go index f725a861..c0f685dd 100644 --- a/sqlite/database.go +++ b/sqlite/database.go @@ -54,11 +54,6 @@ type database struct { cachedStatements *cache.Cache } -type tx struct { - *sqltx.Tx - *database -} - type cachedStatement struct { *sqlx.Stmt query string @@ -340,7 +335,7 @@ func (d *database) Transaction() (db.Tx, error) { clone.tx = sqltx.New(sqlTx) - return tx{Tx: clone.tx, database: clone}, nil + return &tx{Tx: clone.tx, database: clone}, nil } // Exec compiles and executes a statement that does not return any rows. diff --git a/sqlite/database_test.go b/sqlite/database_test.go index 082b8434..1fa39c00 100644 --- a/sqlite/database_test.go +++ b/sqlite/database_test.go @@ -1230,6 +1230,12 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } + // Won't fail + sqlxTx := tx.Driver().(*sqlx.Tx) + if _, err = sqlxTx.Exec(`INSERT INTO "artist" ("id", "name") VALUES(?, ?)`, 4, "Fourth"); err != nil { + t.Fatal(err) + } + if err = tx.Commit(); err != nil { t.Fatal(err) } @@ -1238,7 +1244,7 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatalf("Should have failed, as we've already commited.") } - // Let's verify we have 3 rows. + // Let's verify we have 4 rows. if artist, err = sess.Collection("artist"); err != nil { t.Fatal(err) } @@ -1247,8 +1253,8 @@ func TestTransactionsAndRollback(t *testing.T) { t.Fatal(err) } - if count != 3 { - t.Fatalf("Expecting 3 elements.") + if count != 4 { + t.Fatalf("Expecting exactly 4 results.") } } diff --git a/sqlite/tx.go b/sqlite/tx.go new file mode 100644 index 00000000..04ff10be --- /dev/null +++ b/sqlite/tx.go @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package sqlite + +import ( + "upper.io/db/util/sqlutil/tx" +) + +type tx struct { + *sqltx.Tx + *database +} + +// Driver returns the current transaction session. +func (t *tx) Driver() interface{} { + if t != nil && t.Tx != nil { + return t.Tx.Tx + } + return nil +} -- GitLab