Skip to content

Commit

Permalink
make savepoint syntax configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
l3pp4rd committed Jul 25, 2019
1 parent fa6b7f5 commit 6ca798a
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 51 deletions.
1 change: 0 additions & 1 deletion .gitignore

This file was deleted.

6 changes: 2 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
language: go
go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
# older versions are not tested, due to SQL driver compatibility
- 1.12.x

services:
- mysql
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ ifndef CI
MYSQLCMD=docker-compose exec mysql mysql
endif

PSQLCMD=psql
PSQLCMD=psql
ifndef CI
PSQLCMD=docker-compose exec postgres psql
endif
Expand Down
64 changes: 45 additions & 19 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ import (
"database/sql/driver"
"fmt"
"io"
"math/rand"
"strconv"
"sync"
)

Expand All @@ -67,8 +65,13 @@ import (
// connection.
//
// When Open is called any number of times it returns
// the same transaction connection. Any Begin, Commit calls
// will not start or close the transaction.
// the same transaction connection.
//
// Any Begin, Commit calls will not start or close the transaction.
// Instead the savepoint will be created, released or rolled back.
// In case if your SQL driver does not support save points - use nil
// for the SavePointOption argument. If driver has non default
// save point logic, you can override the default with SavePointOption.
//
// When Close is called, the transaction is rolled back.
//
Expand All @@ -80,28 +83,32 @@ import (
// Note: if you open a secondary database, make sure to differianciate
// the dsn string when opening the sql.DB. The transaction will be
// isolated within that dsn
func Register(name, drv, dsn string) {
func Register(name, drv, dsn string, options ...func(*conn) error) {
sql.Register(name, &txDriver{
dsn: dsn,
drv: drv,
conns: make(map[string]*conn),
dsn: dsn,
drv: drv,
conns: make(map[string]*conn),
options: options,
})
}

// txDriver is an sql driver which runs on single transaction
// when the Close is called, transaction is rolled back
type conn struct {
sync.Mutex
tx *sql.Tx
dsn string
opened uint
drv *txDriver
tx *sql.Tx
dsn string
opened uint
drv *txDriver
saves uint
savePoint SavePoint
}

type txDriver struct {
sync.Mutex
db *sql.DB
conns map[string]*conn
db *sql.DB
conns map[string]*conn
options []func(*conn) error

drv string
dsn string
Expand All @@ -121,7 +128,13 @@ func (d *txDriver) Open(dsn string) (driver.Conn, error) {
}
c, ok := d.conns[dsn]
if !ok {
c = &conn{dsn: dsn, drv: d}
c = &conn{dsn: dsn, drv: d, savePoint: &defaultSavePoint{}}
for _, opt := range d.options {
if e := opt(c); e != nil {
return c, e
}
}

c.tx, err = d.db.Begin()
d.conns[dsn] = c
}
Expand Down Expand Up @@ -151,30 +164,43 @@ type tx struct {
}

func (c *conn) Begin() (driver.Tx, error) {
if c.savePoint == nil {
return &tx{"_", c}, nil // save point is not supported
}

c.Lock()
defer c.Unlock()

id := "tx_" + strconv.Itoa(rand.Int())
_, err := c.tx.Exec(fmt.Sprintf(`SAVEPOINT %s`, id))
c.saves++
id := fmt.Sprintf("tx_%d", c.saves)
_, err := c.tx.Exec(c.savePoint.Create(id))
if err != nil {
return nil, err
}
return &tx{id, c}, nil
}

func (tx *tx) Commit() error {
if tx.conn.savePoint == nil {
return nil // save point is not supported
}

tx.conn.Lock()
defer tx.conn.Unlock()

_, err := tx.conn.tx.Exec(fmt.Sprintf(`RELEASE SAVEPOINT %s`, tx.id))
_, err := tx.conn.tx.Exec(tx.conn.savePoint.Release(tx.id))
return err
}

func (tx *tx) Rollback() error {
if tx.conn.savePoint == nil {
return nil // save point is not supported
}

tx.conn.Lock()
defer tx.conn.Unlock()

_, err := tx.conn.tx.Exec(fmt.Sprintf(`ROLLBACK TO SAVEPOINT %s`, tx.id))
_, err := tx.conn.tx.Exec(tx.conn.savePoint.Rollback(tx.id))
return err
}

Expand Down
6 changes: 0 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
module github.com/DATA-DOG/go-txdb

go 1.12

require (
github.com/go-sql-driver/mysql v1.4.1
github.com/lib/pq v1.1.1
google.golang.org/appengine v1.6.1 // indirect
)
20 changes: 0 additions & 20 deletions go.sum

This file was deleted.

34 changes: 34 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package txdb

import "fmt"

// SavePoint defines the syntax to create savepoints
// within transaction
type SavePoint interface {
Create(id string) string
Release(id string) string
Rollback(id string) string
}

type defaultSavePoint struct{}

func (dsp *defaultSavePoint) Create(id string) string {
return fmt.Sprintf("SAVEPOINT %s", id)
}
func (dsp *defaultSavePoint) Release(id string) string {
return fmt.Sprintf("RELEASE SAVEPOINT %s", id)
}
func (dsp *defaultSavePoint) Rollback(id string) string {
return fmt.Sprintf("ROLLBACK TO SAVEPOINT %s", id)
}

// SavePointOption allows to modify the logic for
// transaction save points. In such cases if your driver
// does not support it, use nil. If not compatible with default
// use custom.
func SavePointOption(savePoint SavePoint) func(*conn) error {
return func(c *conn) error {
c.savePoint = savePoint
return nil
}
}

0 comments on commit 6ca798a

Please sign in to comment.