diff --git a/sqlite/database.go b/sqlite/database.go index 48f71dae01a90ab159626599bd0184fdac53f6d1..dee76afffdc0b9daf2726608df98f2e78ed08db8 100644 --- a/sqlite/database.go +++ b/sqlite/database.go @@ -25,31 +25,29 @@ import ( "database/sql" "errors" "fmt" + "sync" "sync/atomic" _ "github.com/mattn/go-sqlite3" // SQLite3 driver. "upper.io/db.v2" + "upper.io/db.v2/internal/sqladapter" "upper.io/db.v2/sqlbuilder" "upper.io/db.v2/sqlbuilder/exql" - "upper.io/db.v2/internal/sqladapter" ) -// Database represents a SQL database. -type Database interface { - db.Database - builder.Builder - - NewTransaction() (Tx, error) -} - // database is the actual implementation of Database type database struct { sqladapter.BaseDatabase // Leveraged by sqladapter - builder.Builder + db.SQLBuilder connURL db.ConnectionURL + txMu sync.Mutex } +var ( + _ = db.SQLDatabase(&database{}) +) + var ( fileOpenCount int32 errTooManyOpenFiles = errors.New(`Too many open database files.`) @@ -57,8 +55,7 @@ var ( ) var ( - _ = sqladapter.Database(&database{}) - _ = db.Database(&database{}) + _ = db.SQLDatabase(&database{}) ) // newDatabase binds *database with sqladapter and the SQL builer. @@ -69,18 +66,6 @@ func newDatabase(settings db.ConnectionURL) (*database, error) { return d, nil } -// Open stablishes a new connection to a SQL server. -func Open(settings db.ConnectionURL) (Database, error) { - d, err := newDatabase(settings) - if err != nil { - return nil, err - } - if err := d.Open(settings); err != nil { - return nil, err - } - return d, nil -} - // CleanUp cleans up the session. func (d *database) CleanUp() error { if atomic.AddInt32(&fileOpenCount, -1) < 0 { @@ -103,18 +88,18 @@ func (d *database) Open(connURL db.ConnectionURL) error { return d.open() } -// NewTransaction starts a transaction block. -func (d *database) NewTransaction() (Tx, error) { +// NewTx starts a transaction block. +func (d *database) NewTx() (db.SQLTx, error) { nTx, err := d.NewLocalTransaction() if err != nil { return nil, err } - return &tx{Tx: nTx}, nil + return &tx{DatabaseTx: nTx}, nil } // Collections returns a list of non-system tables from the database. func (d *database) Collections() (collections []string, err error) { - q := d.Builder.Select("tbl_name"). + q := d.Select("tbl_name"). From("sqlite_master"). Where("type = ?", "table") @@ -141,7 +126,7 @@ func (d *database) open() error { if err != nil { return err } - d.Builder = b + d.SQLBuilder = b openFn := func() error { openFiles := atomic.LoadInt32(&fileOpenCount) @@ -200,8 +185,14 @@ func (d *database) NewLocalCollection(name string) db.Collection { return newTable(d, name) } +// Tx creates a transaction and passes it to the given function, if if the +// function returns no error then the transaction is commited. +func (d *database) Tx(fn func(tx db.SQLTx) error) error { + return sqladapter.RunTx(d, fn) +} + // NewLocalTransaction allows sqladapter start a transaction block. -func (d *database) NewLocalTransaction() (sqladapter.Tx, error) { +func (d *database) NewLocalTransaction() (sqladapter.DatabaseTx, error) { clone, err := d.clone() if err != nil { return nil, err @@ -234,7 +225,7 @@ func (d *database) FindDatabaseName() (string, error) { // TableExists allows sqladapter check whether a table exists and returns an // error in case it doesn't. func (d *database) TableExists(name string) error { - q := d.Builder.Select("tbl_name"). + q := d.Select("tbl_name"). From("sqlite_master"). Where("type = 'table' AND tbl_name = ?", name) @@ -257,7 +248,7 @@ func (d *database) FindTablePrimaryKeys(tableName string) ([]string, error) { stmt := exql.RawSQL(fmt.Sprintf("PRAGMA TABLE_INFO('%s')", tableName)) - rows, err := d.Builder.Query(stmt) + rows, err := d.Query(stmt) if err != nil { return nil, err } diff --git a/sqlite/sqlite.go b/sqlite/sqlite.go index 56ee3f610b0f3df5107538491282e5eea2488de0..56515515d20fdf4c82b54238f52e542e5e8c9493 100644 --- a/sqlite/sqlite.go +++ b/sqlite/sqlite.go @@ -22,7 +22,11 @@ package sqlite // import "upper.io/db.v2/sqlite" import ( + "database/sql" "upper.io/db.v2" + + "upper.io/db.v2/internal/sqladapter" + "upper.io/db.v2/sqlbuilder" ) const sqlDriver = `sqlite` @@ -31,5 +35,69 @@ const sqlDriver = `sqlite` const Adapter = sqlDriver func init() { - db.Register(Adapter, &database{}) + db.RegisterSQLAdapter(Adapter, &db.SQLAdapterFuncMap{ + New: New, + NewTx: NewTx, + Open: Open, + }) +} + +// Open stablishes a new connection with the SQL server. +func Open(settings db.ConnectionURL) (db.SQLDatabase, error) { + d, err := newDatabase(settings) + if err != nil { + return nil, err + } + if err := d.Open(settings); err != nil { + return nil, err + } + return d, nil +} + +// NewTx returns a transaction session. +func NewTx(sqlTx *sql.Tx) (db.SQLTx, error) { + d, err := newDatabase(nil) + if err != nil { + return nil, err + } + + // Binding with sqladapter's logic. + d.BaseDatabase = sqladapter.NewBaseDatabase(d) + + // Binding with builder. + b, err := builder.New(d.BaseDatabase, template) + if err != nil { + return nil, err + } + d.SQLBuilder = b + + if err := d.BaseDatabase.BindTx(sqlTx); err != nil { + return nil, err + } + + newTx := sqladapter.NewTx(d) + return &tx{DatabaseTx: newTx}, nil +} + +// New wraps the given *sql.DB session and creates a new db session. +func New(sess *sql.DB) (db.SQLDatabase, error) { + d, err := newDatabase(nil) + if err != nil { + return nil, err + } + + // Binding with sqladapter's logic. + d.BaseDatabase = sqladapter.NewBaseDatabase(d) + + // Binding with builder. + b, err := builder.New(d.BaseDatabase, template) + if err != nil { + return nil, err + } + d.SQLBuilder = b + + if err := d.BaseDatabase.BindSession(sess); err != nil { + return nil, err + } + return d, nil } diff --git a/sqlite/template_test.go b/sqlite/template_test.go index 8e8d15d923143ac1edec395d1a98b1af1bbb3796..78040518defdad12f5ea79c819f9de10a9579e6b 100644 --- a/sqlite/template_test.go +++ b/sqlite/template_test.go @@ -9,7 +9,7 @@ import ( ) func TestTemplateSelect(t *testing.T) { - b := builder.NewBuilderWithTemplate(template) + b := builder.NewSQLBuilder(template) assert := assert.New(t) assert.Equal( @@ -147,7 +147,7 @@ func TestTemplateSelect(t *testing.T) { } func TestTemplateInsert(t *testing.T) { - b := builder.NewBuilderWithTemplate(template) + b := builder.NewSQLBuilder(template) assert := assert.New(t) assert.Equal( @@ -189,7 +189,7 @@ func TestTemplateInsert(t *testing.T) { } func TestTemplateUpdate(t *testing.T) { - b := builder.NewBuilderWithTemplate(template) + b := builder.NewSQLBuilder(template) assert := assert.New(t) assert.Equal( @@ -231,7 +231,7 @@ func TestTemplateUpdate(t *testing.T) { } func TestTemplateDelete(t *testing.T) { - b := builder.NewBuilderWithTemplate(template) + b := builder.NewSQLBuilder(template) assert := assert.New(t) assert.Equal( diff --git a/sqlite/tx.go b/sqlite/tx.go index 67e3984441bf65e118e63e79b5cca6d1bf5e396b..b26f01c4d738bc4d77fd206144c0146f773677a3 100644 --- a/sqlite/tx.go +++ b/sqlite/tx.go @@ -26,22 +26,10 @@ import ( "upper.io/db.v2/internal/sqladapter" ) -// Tx represents a transaction. -type Tx interface { - Database - - Commit() error - Rollback() error -} - type tx struct { - sqladapter.Tx + sqladapter.DatabaseTx } var ( - _ = db.Tx(&tx{}) + _ = db.SQLTx(&tx{}) ) - -func (t *tx) NewTransaction() (Tx, error) { - return t, db.ErrAlreadyWithinTransaction -}