package datastore import ( "log" "os" "sync" "github.com/DavidHuie/gomigrate" "github.com/jmoiron/modl" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) // DB is the global database var DB = &modl.DbMap{Dialect: modl.PostgresDialect{}} // DBH is a modl.SqlExecutor interface to DB, the global database. It is better to // use DBH instead of DB because it prevents you from calling methods that could // not later be wrapped in a transaction. var DBH modl.SqlExecutor = DB var connectOnce sync.Once // Connect connects to the PostgreSQL database specified by the PG* environment // variables. It calls log.Fatal if it encounters an error. func Connect() { connectOnce.Do(func() { var err error setDBCredentialsFromFig() DB.Dbx, err = sqlx.Open("postgres", "timezone=UTC sslmode=disable") if err != nil { log.Fatal("Error connecting to PostgreSQL database (using PG* environment variables): ", err) } DB.TraceOn("[modl]", log.New(os.Stdout, "bactdb:", log.Lmicroseconds)) DB.Db = DB.Dbx.DB }) } // Create the database schema. It calls log.Fatal if it encounters an error. func Create(path string) { migrator, err := gomigrate.NewMigrator(DB.Dbx.DB, gomigrate.Postgres{}, path) if err != nil { log.Fatal("Error initializing migrations: ", err) } err = migrator.Migrate() if err != nil { log.Fatal("Error applying migrations: ", err) } } // Drop the database schema func Drop(path string) { migrator, err := gomigrate.NewMigrator(DB.Dbx.DB, gomigrate.Postgres{}, path) if err != nil { log.Fatal("Error initializing migrations: ", err) } err = migrator.RollbackAll() if err != nil && err != gomigrate.NoActiveMigrations { log.Fatal("Error rolling back migrations: ", err) } } // transact calls fn in a DB transaction. If dbh is a transaction, then it just calls // the function. Otherwise, it begins a transaction, rolling back on failure and // committing on success. func transact(dbh modl.SqlExecutor, fn func(fbh modl.SqlExecutor) error) error { var sharedTx bool tx, sharedTx := dbh.(*modl.Transaction) if !sharedTx { var err error tx, err = dbh.(*modl.DbMap).Begin() if err != nil { return err } defer func() { if err != nil { tx.Rollback() } }() } if err := fn(tx); err != nil { return err } if !sharedTx { if err := tx.Commit(); err != nil { return err } } return nil } func setDBCredentialsFromFig() { if figVal := os.Getenv("BACTDB_DB_1_PORT_5432_TCP_ADDR"); figVal != "" { err := os.Setenv("PGHOST", figVal) if err != nil { log.Print(err) } } }