123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- // Copyright (c) 2012-present 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 sqlbuilder
-
- import (
- "context"
- "database/sql"
- "fmt"
- "sync"
-
- "upper.io/db.v3"
- )
-
- var (
- adapters map[string]*AdapterFuncMap
- adaptersMu sync.RWMutex
- )
-
- func init() {
- adapters = make(map[string]*AdapterFuncMap)
- }
-
- // Tx represents a transaction on a SQL database. A transaction is like a
- // regular Database except it has two extra methods: Commit and Rollback.
- //
- // A transaction needs to be committed (with Commit) to make changes permanent,
- // changes can be discarded before committing by rolling back (with Rollback).
- // After either committing or rolling back a transaction it can not longer be
- // used and it's automatically closed.
- type Tx interface {
- // All db.Database methods are available on transaction sessions. They will
- // run on the same transaction.
- db.Database
-
- // All SQLBuilder methods are available on transaction sessions. They will
- // run on the same transaction.
- SQLBuilder
-
- // db.Tx adds Commit and Rollback methods to the transaction.
- db.Tx
-
- // Context returns the context used as default for queries on this transaction.
- // If no context has been set, a default context.Background() is returned.
- Context() context.Context
-
- // WithContext returns a copy of the transaction that uses the given context
- // as default. Copies are safe to use concurrently but they're backed by the
- // same *sql.Tx, so any copy may commit or rollback the parent transaction.
- WithContext(context.Context) Tx
-
- // SetTxOptions sets the default TxOptions that is going to be used for new
- // transactions created in the session.
- SetTxOptions(sql.TxOptions)
-
- // TxOptions returns the defaultx TxOptions.
- TxOptions() *sql.TxOptions
- }
-
- // Database represents a SQL database.
- type Database interface {
- // All db.Database methods are available on this session.
- db.Database
-
- // All SQLBuilder methods are available on this session.
- SQLBuilder
-
- // NewTx creates and returns a transaction that runs on the given context.
- // If a nil context is given, then the transaction will use the session's
- // default context. The user is responsible for committing or rolling back
- // the session.
- NewTx(ctx context.Context) (Tx, error)
-
- // Tx creates a new transaction that is passed as argument to the fn
- // function. The fn function defines a transactional operation. If the fn
- // function returns nil, the transaction is committed, else the transaction
- // is rolled back. The transaction session is closed after the function
- // exits, regardless of the error value returned by fn.
- Tx(ctx context.Context, fn func(sess Tx) error) error
-
- // Context returns the context used as default for queries on this session
- // and for new transactions. If no context has been set, a default
- // context.Background() is returned.
- Context() context.Context
-
- // WithContext returns a copy of the session that uses the given context as
- // default. Copies are safe to use concurrently but they're backed by the
- // same *sql.DB. You may close a copy at any point but that won't close the
- // parent session.
- WithContext(context.Context) Database
-
- // SetTxOptions sets the default TxOptions that is going to be used for new
- // transactions created in the session.
- SetTxOptions(sql.TxOptions)
-
- // TxOptions returns the defaultx TxOptions.
- TxOptions() *sql.TxOptions
- }
-
- // AdapterFuncMap is a struct that defines a set of functions that adapters
- // need to provide.
- type AdapterFuncMap struct {
- New func(sqlDB *sql.DB) (Database, error)
- NewTx func(sqlTx *sql.Tx) (Tx, error)
- Open func(settings db.ConnectionURL) (Database, error)
- }
-
- // RegisterAdapter registers a SQL database adapter. This function must be
- // called from adapter packages upon initialization. RegisterAdapter calls
- // RegisterAdapter automatically.
- func RegisterAdapter(name string, adapter *AdapterFuncMap) {
- adaptersMu.Lock()
- defer adaptersMu.Unlock()
-
- if name == "" {
- panic(`Missing adapter name`)
- }
- if _, ok := adapters[name]; ok {
- panic(`db.RegisterAdapter() called twice for adapter: ` + name)
- }
- adapters[name] = adapter
-
- db.RegisterAdapter(name, &db.AdapterFuncMap{
- Open: func(settings db.ConnectionURL) (db.Database, error) {
- return adapter.Open(settings)
- },
- })
- }
-
- // adapter returns SQL database functions.
- func adapter(name string) AdapterFuncMap {
- adaptersMu.RLock()
- defer adaptersMu.RUnlock()
-
- if fn, ok := adapters[name]; ok {
- return *fn
- }
- return missingAdapter(name)
- }
-
- // Open opens a SQL database.
- func Open(adapterName string, settings db.ConnectionURL) (Database, error) {
- return adapter(adapterName).Open(settings)
- }
-
- // New wraps an active *sql.DB session and returns a SQLBuilder database. The
- // adapter needs to be imported to the blank namespace in order for it to be
- // used here.
- //
- // This method is internally used by upper-db to create a builder backed by the
- // given database. You may want to use your adapter's New function instead of
- // this one.
- func New(adapterName string, sqlDB *sql.DB) (Database, error) {
- return adapter(adapterName).New(sqlDB)
- }
-
- // NewTx wraps an active *sql.Tx transation and returns a SQLBuilder
- // transaction. The adapter needs to be imported to the blank namespace in
- // order for it to be used.
- //
- // This method is internally used by upper-db to create a builder backed by the
- // given transaction. You may want to use your adapter's NewTx function
- // instead of this one.
- func NewTx(adapterName string, sqlTx *sql.Tx) (Tx, error) {
- return adapter(adapterName).NewTx(sqlTx)
- }
-
- func missingAdapter(name string) AdapterFuncMap {
- err := fmt.Errorf("upper: Missing SQL adapter %q, forgot to import?", name)
- return AdapterFuncMap{
- New: func(*sql.DB) (Database, error) {
- return nil, err
- },
- NewTx: func(*sql.Tx) (Tx, error) {
- return nil, err
- },
- Open: func(db.ConnectionURL) (Database, error) {
- return nil, err
- },
- }
- }
|