Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v7.3.1 #100

Merged
merged 2 commits into from
Jul 12, 2019
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
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v7.3.1

* [ADDED] Exposed `goqu.NewTx` to allow creating a goqu tx directly from a `sql.Tx` instead of using `goqu.Database#Begin` [#95](https://github.com/doug-martin/goqu/issues/95)
* [ADDED] `goqu.Database.BeginTx` [#98](https://github.com/doug-martin/goqu/issues/98)

## v7.3.0

* [ADDED] UPDATE and INSERT should use struct Field name if db tag is not specified [#57](https://github.com/doug-martin/goqu/issues/57)
Expand Down
35 changes: 32 additions & 3 deletions database.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type (
// libraries such as sqlx instead of the native sql.DB
SQLDatabase interface {
Begin() (*sql.Tx, error)
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
Expand Down Expand Up @@ -69,11 +70,24 @@ func (d *Database) Dialect() string {

// Starts a new Transaction.
func (d *Database) Begin() (*TxDatabase, error) {
tx, err := d.Db.Begin()
sqlTx, err := d.Db.Begin()
if err != nil {
return nil, err
}
return &TxDatabase{dialect: d.dialect, Tx: tx, logger: d.logger}, nil
tx := NewTx(d.dialect, sqlTx)
tx.Logger(d.logger)
return tx, nil
}

// Starts a new Transaction. See sql.DB#BeginTx for option description
func (d *Database) BeginTx(ctx context.Context, opts *sql.TxOptions) (*TxDatabase, error) {
sqlTx, err := d.Db.BeginTx(ctx, opts)
if err != nil {
return nil, err
}
tx := NewTx(d.dialect, sqlTx)
tx.Logger(d.logger)
return tx, nil
}

// Creates a new Dataset that uses the correct adapter and supports queries.
Expand Down Expand Up @@ -385,14 +399,29 @@ func (d *Database) ScanValContext(ctx context.Context, i interface{}, query stri

// A wrapper around a sql.Tx and works the same way as Database
type (
// Interface for sql.Tx, an interface is used so you can use with other
// libraries such as sqlx instead of the native sql.DB
SQLTx interface {
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
Commit() error
Rollback() error
}
TxDatabase struct {
logger Logger
dialect string
Tx *sql.Tx
Tx SQLTx
qf exec.QueryFactory
}
)

// Creates a new TxDatabase
func NewTx(dialect string, tx SQLTx) *TxDatabase {
return &TxDatabase{dialect: dialect, Tx: tx}
}

// returns this databases dialect
func (td *TxDatabase) Dialect() string {
return td.dialect
Expand Down
34 changes: 34 additions & 0 deletions database_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package goqu_test

import (
"context"
"database/sql"
"fmt"
"time"

Expand Down Expand Up @@ -40,6 +41,39 @@ func ExampleDatabase_Begin() {
// Updated users in transaction [ids:=[1 2 3]]
}

func ExampleDatabase_BeginTx() {
db := getDb()

ctx := context.Background()
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
if err != nil {
fmt.Println("Error starting transaction", err.Error())
}

// use tx.From to get a dataset that will execute within this transaction
update := tx.From("goqu_user").
Where(goqu.Ex{"last_name": "Yukon"}).
Returning("id").
Update(goqu.Record{"last_name": "Ucon"})

var ids []int64
if err := update.ScanVals(&ids); err != nil {
if rErr := tx.Rollback(); rErr != nil {
fmt.Println("An error occurred while issuing ROLLBACK\n\t", rErr.Error())
} else {
fmt.Println("An error occurred while updating users ROLLBACK transaction\n\t", err.Error())
}
return
}
if err := tx.Commit(); err != nil {
fmt.Println("An error occurred while issuing COMMIT\n\t", err.Error())
} else {
fmt.Printf("Updated users in transaction [ids:=%+v]", ids)
}
// Output:
// Updated users in transaction [ids:=[1 2 3]]
}

func ExampleDatabase_Dialect() {
db := getDb()

Expand Down
17 changes: 17 additions & 0 deletions database_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package goqu

import (
"context"
"fmt"
"testing"

Expand Down Expand Up @@ -250,6 +251,22 @@ func (dt *databaseTest) TestBegin() {
assert.EqualError(t, err, "goqu: transaction error")
}

func (dt *databaseTest) TestBeginTx() {
t := dt.T()
ctx := context.Background()
mDb, mock, err := sqlmock.New()
assert.NoError(t, err)
mock.ExpectBegin()
mock.ExpectBegin().WillReturnError(errors.New("transaction error"))
db := New("mock", mDb)
tx, err := db.BeginTx(ctx, nil)
assert.NoError(t, err)
assert.Equal(t, tx.Dialect(), "mock")

_, err = db.BeginTx(ctx, nil)
assert.EqualError(t, err, "goqu: transaction error")
}

func TestDatabaseSuite(t *testing.T) {
suite.Run(t, new(databaseTest))
}
Expand Down