dbkit

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 15, 2026 License: Apache-2.0 Imports: 15 Imported by: 3

README

dbkit

One database handle per engine, shared by every part of a Go program.

A program opens one *sql.DB for its site and hands it to everyone: its own stores, its accounts, its command line and its plugins. On SQLite that rule is what keeps the file safe. A second copy of the SQLite library keeps its own lock state, and code that opens and closes the database file drops SQLite's locks. Many connections through one copy of SQLite are safe.

This root module holds what every engine shares: the engine an address names, the error classes, the function list, the time type for SQLite columns, the snapshot interface, and the share, a capped and counted view of the one handle lent to one part of a program. It imports the standard library only. Each engine lives in its own nested module, so a program on PostgreSQL alone never builds a SQLite driver.

The guide is at https://docs.gopherium.org.

The import rule

Only dbkit imports a SQLite driver, and only dbkit opens a handle. Give your program both rules in its .golangci.yml. depguard refuses the drivers, and forbidigo refuses sql.Open and sql.OpenDB, which need no driver import to bypass every connection rule.

version: "2"

linters:
  enable:
    - depguard
    - forbidigo
  settings:
    depguard:
      rules:
        one-sqlite-driver:
          deny:
            - pkg: modernc.org/sqlite
              desc: open SQLite through github.com/gopherium/framework/dbkit/sqlite
            - pkg: modernc.org/libc
              desc: open SQLite through github.com/gopherium/framework/dbkit/sqlite
            - pkg: github.com/mattn/go-sqlite3
              desc: open SQLite through github.com/gopherium/framework/dbkit/sqlite
            - pkg: github.com/ncruces/go-sqlite3
              desc: open SQLite through github.com/gopherium/framework/dbkit/sqlite
            - pkg: github.com/glebarez/go-sqlite
              desc: open SQLite through github.com/gopherium/framework/dbkit/sqlite
            - pkg: turso.tech/database/tursogo
              desc: open SQLite through github.com/gopherium/framework/dbkit/sqlite
    forbidigo:
      analyze-types: true
      forbid:
        - pattern: ^sql\.Open(DB)?$
          pkg: ^database/sql$
          msg: open the database through dbkit

analyze-types makes forbidigo catch an aliased import of database/sql too.

License

Apache-2.0.

Documentation

Overview

Package dbkit names database engines, their error classes and shared types, and lends capped shares of one handle.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrBusy is the class of an error from a lock that was not free within the engine's wait.
	ErrBusy = errors.New("dbkit: the database is busy")
	// ErrUnique is the class of an error from a unique or primary key constraint.
	ErrUnique = errors.New("dbkit: a unique constraint failed")
	// ErrForeignKey is the class of an error from a foreign key constraint.
	ErrForeignKey = errors.New("dbkit: a foreign key constraint failed")
	// ErrNotNull is the class of an error from a not null constraint.
	ErrNotNull = errors.New("dbkit: a not null constraint failed")
	// ErrCheck is the class of an error from a check constraint.
	ErrCheck = errors.New("dbkit: a check constraint failed")
)
View Source
var (
	// ErrShareFull marks a statement that found no free slot before its deadline.
	ErrShareFull = errors.New("dbkit: no free slot in the share before the statement deadline")
	// ErrRefused marks a statement the observer refused.
	ErrRefused = errors.New("dbkit: the observer refused the statement")
	// ErrPlaceholder marks a SQLite statement with a question mark parameter, or a $N after a named parameter.
	ErrPlaceholder = errors.New("dbkit: write every parameter as $N")
)
View Source
var ErrAddress = errors.New("dbkit: refused database address")

ErrAddress marks every database address EngineOf and SQLitePath refuse.

Functions

func EscapeLike added in v0.2.0

func EscapeLike(term string) string

EscapeLike returns term with every LIKE pattern character escaped by a backslash.

func InTx added in v0.3.0

func InTx(ctx context.Context, db *sql.DB, body func(*sql.Tx) error) error

InTx runs body in a write transaction on db, rolling back when body does not return nil and committing otherwise.

func SQLitePath

func SQLitePath(address string) (string, error)

SQLitePath returns the file path of a SQLite address as written.

Types

type Checkpoint

type Checkpoint struct {
	// Busy reports that a reader or a writer stopped the checkpoint before it finished.
	Busy bool
	// LogFrames is the count of frames in the write-ahead log, or -1 when there is none.
	LogFrames int
	// CheckpointedFrames is the count of log frames written back into the database file, or -1 when there is none.
	CheckpointedFrames int
	// Took is how long the checkpoint ran.
	Took time.Duration
}

Checkpoint reports one write-ahead log checkpoint.

type Engine

type Engine int

Engine names the database engine an address selects.

const (
	// Postgres is PostgreSQL.
	Postgres Engine = iota + 1
	// SQLite is SQLite in one file.
	SQLite
)

func EngineOf

func EngineOf(address string) (Engine, error)

EngineOf returns the engine a database address names.

func (Engine) String

func (e Engine) String() string

String returns the engine's name.

type Function

type Function struct {
	// Name is the name SQL calls the function by, its ASCII letters matched in any case and every other letter exactly.
	Name string
	// Args is the count of arguments the function takes, or -1 for any count.
	Args int
	// Deterministic reports that the same arguments always give the same result.
	Deterministic bool
	// Call computes the result from the arguments, which stay valid only until it returns.
	Call func(args []driver.Value) (driver.Value, error)
}

Function is one Go function an engine registers for SQL to call.

func CaseFold

func CaseFold() Function

CaseFold returns casefold, a deterministic function of one argument that folds text by Unicode simple folding.

type FunctionList

type FunctionList struct {
	// contains filtered or unexported fields
}

FunctionList is a checked list of functions with no name repeated, its ASCII letters in any case.

func NewFunctionList

func NewFunctionList(fns ...Function) (*FunctionList, error)

NewFunctionList returns a list of fns, or an error naming the first function it refuses.

func (*FunctionList) All

func (l *FunctionList) All() []Function

All returns a copy of the functions in the list, or nil for a nil list.

type Kind

type Kind int

Kind names the call that sent a statement.

const (
	// KindExec is a call to Exec.
	KindExec Kind = iota + 1
	// KindQuery is a call to Query.
	KindQuery
	// KindQueryRow is a call to QueryRow.
	KindQueryRow
	// KindBegin is a call to Begin.
	KindBegin
)

func (Kind) String

func (k Kind) String() string

String returns the name of the call.

type Observer

type Observer func(ctx context.Context, s Statement) error

Observer sees every statement and transaction start of a share before the driver, and refuses one by an error.

type QueryBudget

type QueryBudget struct {
	// contains filtered or unexported fields
}

QueryBudget hands each request a fresh statement counter with the same limit.

func NewQueryBudget

func NewQueryBudget(limit int) (*QueryBudget, error)

NewQueryBudget returns a budget of limit statements per request.

func (*QueryBudget) Attach

func (b *QueryBudget) Attach(ctx context.Context) context.Context

Attach returns a copy of ctx that carries a fresh statement counter.

type QueryCount

type QueryCount struct {
	// Total is the number of statements of every share.
	Total int
	// ByID is the number of statements of each share, keyed by its ID.
	ByID map[string]int
	// PastBudget reports whether Total is above the limit of the budget.
	PastBudget bool
}

QueryCount is a copy of the statement counter of one request.

func QueriesIn

func QueriesIn(ctx context.Context) (QueryCount, bool)

QueriesIn returns a copy of the statement counter in ctx, or false when ctx carries none.

type Row

type Row struct {
	// contains filtered or unexported fields
}

Row is the result of QueryRow, holding its slot until Scan.

func (*Row) Err

func (r *Row) Err() error

Err returns the error of the query behind the row.

func (*Row) Scan

func (r *Row) Scan(dest ...any) error

Scan copies the first row into dest and closes the rows.

type Rows

type Rows struct {
	*sql.Rows
	// contains filtered or unexported fields
}

Rows is a *sql.Rows that gives its slot back exactly once, read within the statement timeout.

func (*Rows) Close

func (r *Rows) Close() error

Close closes the rows and gives the slot back, the first time only.

func (*Rows) Err

func (r *Rows) Err() error

Err returns the error met while reading, marked as a deadline error when the statement deadline stopped it.

func (*Rows) Next

func (r *Rows) Next() bool

Next prepares the next row for Scan and gives the slot back once the last result set ends.

func (*Rows) NextResultSet

func (r *Rows) NextResultSet() bool

NextResultSet moves to the next result set and gives the slot back when none follows.

func (*Rows) Scan

func (r *Rows) Scan(dest ...any) error

Scan copies the columns of the current row into dest, refusing a *sql.RawBytes destination.

type Share

type Share struct {
	// contains filtered or unexported fields
}

Share is a capped, timed and counted view of one database handle, lent to one part of an application.

func NewShare

func NewShare(db *sql.DB, engine Engine, opts ShareOptions) (*Share, error)

NewShare lends a capped share of an open handle. It opens nothing and sends nothing.

func (*Share) Begin

func (s *Share) Begin(ctx context.Context) (*Tx, error)

Begin starts a transaction that ends at the transaction timeout at the latest.

func (*Share) Engine

func (s *Share) Engine() Engine

Engine returns the engine of the share's handle.

func (*Share) Exec

func (s *Share) Exec(ctx context.Context, query string, args ...any) (sql.Result, error)

Exec runs a statement that returns no rows.

func (*Share) Query

func (s *Share) Query(ctx context.Context, query string, args ...any) (*Rows, error)

Query runs a query that returns rows, read within the statement timeout.

func (*Share) QueryRow

func (s *Share) QueryRow(ctx context.Context, query string, args ...any) *Row

QueryRow runs a query that returns at most one row, read within the statement timeout.

type ShareOptions

type ShareOptions struct {
	// ID names the share to the counter and the observer and is never parsed.
	ID string
	// Slots is how many statements, open result sets and transactions the share holds at once.
	Slots int
	// StatementTimeout is the deadline of one statement, its wait for a slot included.
	StatementTimeout time.Duration
	// TransactionTimeout is the deadline of one transaction from Begin to its end.
	TransactionTimeout time.Duration
	// Observer sees every statement and transaction start before the driver, and nil means none.
	Observer Observer
}

ShareOptions configures one share. Every number is required.

type Snapshot

type Snapshot struct {
	// Path is the file the copy was written to.
	Path string
	// Checkpoint is the write-ahead log checkpoint run after the copy.
	Checkpoint Checkpoint
}

Snapshot describes one finished copy of a database.

type Snapshotter

type Snapshotter interface {
	// Snapshot writes a consistent copy of the database to target and reports the checkpoint run after it.
	Snapshot(ctx context.Context, target string) (Snapshot, error)
}

Snapshotter writes a consistent copy of a database to a file.

type Statement

type Statement struct {
	// ID is the ID of the share.
	ID string
	// Kind is the call that sent the statement.
	Kind Kind
	// Query is the text as the caller wrote it, before any rewrite, and empty for Begin.
	Query string
	// Writes reports whether the text of the statement shows a write, always true for Begin.
	Writes bool
	// InTransaction reports whether the statement runs inside a transaction of the share.
	InTransaction bool
	// Count is the number of statements in the request so far, this one included and Begin never counted, 0 with none.
	Count int
	// PastBudget reports whether Count is above the budget of the request.
	PastBudget bool
}

Statement is what the observer sees before a statement or a transaction start reaches the driver.

type Time

type Time struct {
	time.Time
}

Time is a time.Time stored as INTEGER UTC microseconds since the Unix epoch.

func (*Time) Scan

func (t *Time) Scan(src any) error

Scan reads an int64 of microseconds since the Unix epoch into the time in UTC.

func (Time) Value

func (t Time) Value() (driver.Value, error)

Value returns the time as an int64 of microseconds since the Unix epoch, or an error for a time outside that range.

type Tx

type Tx struct {
	// contains filtered or unexported fields
}

Tx is a transaction of a share that holds one slot until Commit, Rollback or the end of its context.

func (*Tx) Commit

func (t *Tx) Commit() error

Commit commits the transaction and gives its slot back, or rolls it back once its context has ended.

func (*Tx) Exec

func (t *Tx) Exec(ctx context.Context, query string, args ...any) (sql.Result, error)

Exec runs a statement that returns no rows inside the transaction.

func (*Tx) Query

func (t *Tx) Query(ctx context.Context, query string, args ...any) (*Rows, error)

Query runs a query that returns rows inside the transaction, read within the statement timeout.

func (*Tx) QueryRow

func (t *Tx) QueryRow(ctx context.Context, query string, args ...any) *Row

QueryRow runs a query that returns at most one row inside the transaction, read within the statement timeout.

func (*Tx) Rollback

func (t *Tx) Rollback() error

Rollback rolls the transaction back and gives its slot back, the first time only.

Directories

Path Synopsis
postgres module
sqlite module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL