Files
coder/database/db.go
Kyle Carberry 025b55f7be chore: Initial database scaffolding (#2)
* chore: Initial database scaffolding

This implements migrations and code generation for interfacing with a PostgreSQL database.

A dependency is added for the "postgres" binary on the host, but that seems like an acceptable requirement considering it's our primary database.

An in-memory database object can be created for simple cross-OS and fast testing.

* Run tests in CI

* Use Docker instead of binaries on the host

* Skip database tests on non-Linux operating systems

* chore: Add golangci-lint and codecov

* Use consistent file names
2022-01-05 09:20:56 -06:00

76 lines
2.0 KiB
Go

// Package database connects to external services for stateful storage.
//
// Query functions are generated using sqlc.
//
// To modify the database schema:
// 1. Add a new migration using "create_migration.sh" in database/migrations/
// 2. Run "make database/generate" in the root to generate models.
// 3. Add/Edit queries in "query.sql" and run "make database/generate" to create Go code.
package database
import (
"context"
"database/sql"
"errors"
"golang.org/x/xerrors"
)
// Store contains all queryable database functions.
// It extends the generated interface to add transaction support.
type Store interface {
querier
InTx(context.Context, func(Store) error) error
}
// DBTX represents a database connection or transaction.
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
// New creates a new database store using a SQL database connection.
func New(sdb *sql.DB) Store {
return &sqlQuerier{
db: sdb,
sdb: sdb,
}
}
type sqlQuerier struct {
sdb *sql.DB
db DBTX
}
// InTx performs database operations inside a transaction.
func (q *sqlQuerier) InTx(ctx context.Context, fn func(Store) error) error {
if q.sdb == nil {
return nil
}
tx, err := q.sdb.Begin()
if err != nil {
return xerrors.Errorf("begin transaction: %w", err)
}
defer func() {
rerr := tx.Rollback()
if rerr == nil || errors.Is(rerr, sql.ErrTxDone) {
// no need to do anything, tx committed successfully
return
}
// couldn't roll back for some reason, extend returned error
err = xerrors.Errorf("defer (%s): %w", rerr.Error(), err)
}()
err = fn(&sqlQuerier{db: tx})
if err != nil {
return xerrors.Errorf("execute transaction: %w", err)
}
err = tx.Commit()
if err != nil {
return xerrors.Errorf("commit transaction: %w", err)
}
return nil
}