forked from mystiq/dex
sqlite3 in memory databases do not support concurrent writes. Limit number of open connections to prevent race conditions.
114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
|
|
"github.com/go-gorp/gorp"
|
|
|
|
"github.com/coreos/dex/repo"
|
|
|
|
// Import database drivers
|
|
_ "github.com/lib/pq"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
type table struct {
|
|
name string
|
|
model interface{}
|
|
autoinc bool
|
|
pkey []string
|
|
|
|
// unique are non-primary key fields which should have uniqueness constraints.
|
|
unique []string
|
|
}
|
|
|
|
var (
|
|
tables []table
|
|
)
|
|
|
|
func register(t table) {
|
|
tables = append(tables, t)
|
|
}
|
|
|
|
type Config struct {
|
|
// Connection string in the format: <driver>://<username>:<password>@<host>:<port>/<database>
|
|
DSN string
|
|
// The maximum number of open connections to the database. The default is 0 (unlimited).
|
|
// For more details see: http://golang.org/pkg/database/sql/#DB.SetMaxOpenConns
|
|
MaxOpenConnections int
|
|
// The maximum number of connections in the idle connection pool. The default is 0 (unlimited).
|
|
// For more details see: http://golang.org/pkg/database/sql/#DB.SetMaxIdleConns
|
|
MaxIdleConnections int
|
|
}
|
|
|
|
func NewConnection(cfg Config) (*gorp.DbMap, error) {
|
|
u, err := url.Parse(cfg.DSN)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse DSN: %v", err)
|
|
}
|
|
var (
|
|
db *sql.DB
|
|
dialect gorp.Dialect
|
|
)
|
|
switch u.Scheme {
|
|
case "postgres":
|
|
db, err = sql.Open("postgres", cfg.DSN)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
db.SetMaxIdleConns(cfg.MaxIdleConnections)
|
|
db.SetMaxOpenConns(cfg.MaxOpenConnections)
|
|
dialect = gorp.PostgresDialect{}
|
|
case "sqlite3":
|
|
db, err = sql.Open("sqlite3", u.Host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if u.Host == ":memory:" {
|
|
// NOTE(ericchiang): sqlite3 coordinates concurrent clients through file locks.
|
|
// In memory databases do not support concurrent calls. Limit the number of
|
|
// open connections to 1.
|
|
//
|
|
// See: https://www.sqlite.org/faq.html#q5
|
|
db.SetMaxOpenConns(1)
|
|
}
|
|
dialect = gorp.SqliteDialect{}
|
|
default:
|
|
return nil, errors.New("unrecognized database driver")
|
|
}
|
|
|
|
dbm := gorp.DbMap{Db: db, Dialect: dialect}
|
|
|
|
for _, t := range tables {
|
|
tm := dbm.AddTableWithName(t.model, t.name).SetKeys(t.autoinc, t.pkey...)
|
|
for _, unique := range t.unique {
|
|
cm := tm.ColMap(unique)
|
|
if cm == nil {
|
|
return nil, fmt.Errorf("no such column: %q", unique)
|
|
}
|
|
cm.SetUnique(true)
|
|
}
|
|
}
|
|
return &dbm, nil
|
|
}
|
|
|
|
func TransactionFactory(conn *gorp.DbMap) repo.TransactionFactory {
|
|
return func() (repo.Transaction, error) {
|
|
return conn.Begin()
|
|
}
|
|
}
|
|
|
|
// NewMemDB creates a new in memory sqlite3 database.
|
|
func NewMemDB() *gorp.DbMap {
|
|
dbMap, err := NewConnection(Config{DSN: "sqlite3://:memory:"})
|
|
if err != nil {
|
|
panic("Failed to create in memory database: " + err.Error())
|
|
}
|
|
if _, err := MigrateToLatest(dbMap); err != nil {
|
|
panic("In memory database migration failed: " + err.Error())
|
|
}
|
|
return dbMap
|
|
}
|