Help us shape the future of Go! Take the 2026 Go Developer Survey

sqldb

package
v0.1.0-rc.5 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 31 Imported by: 1

README

sqldb

The application's own SQLite databases inside a tinystore.Store, each in sql/<name>.db: its tables, joins, reports and transactions. The application writes every query; sqldb owns the file, its connections, the migrations and the values between Go and SQLite. A struct says what a row is, Table[T] what a struct cannot say, and Open checks the file the migrations made against them. The design and the measurements behind it are research's design/sqldb.md.

type Note struct {
	ID        uuid.UUID // text, 8-4-4-4-12
	AuthorID  int64
	Title     string
	Done      bool                 // INTEGER, 0 or 1
	Tags      sqldb.JSON[[]string] // TEXT, valid JSON
	Due       *sqldb.Date          // TEXT YYYY-MM-DD, or NULL
	CreatedAt time.Time            // INTEGER, unix milliseconds
}

var Notes = sqldb.Table[Note]("notes",
	sqldb.PrimaryKey("id"),
	sqldb.References("author_id", Users, sqldb.Cascade),
	sqldb.Default("done", false),
	sqldb.Index("author_id", "created_at"),
)

var Schema = sqldb.Schema(Users, Notes)

//go:embed migrations/*.sql
var migrations embed.FS

db, err := sqldb.Open(ctx, store, "app", migrations, Schema) // data/sql/app.db

note, err = sqldb.Insert(ctx, db, Notes, note)
note, found, err := sqldb.One[Note](ctx, db, `select * from notes where id = ?`, id)
mine, err := sqldb.All[Note](ctx, db, `select * from notes where author_id = ? order by created_at desc limit 50`, user)
count, err := sqldb.Scalar[int](ctx, db, `select count(*) from notes`)
_, err = db.Exec(ctx, `delete from notes where id = ? and author_id = ?`, id, user)

A test compares the schema with what the migrations make, and a tool writes the next migration from the difference:

func TestSchema(t *testing.T) {
	sqldbtest.CheckSchema(t, "app", Schema, "migrations")
}
$ go get -tool github.com/tinyshed/tinystore/cmd/tinystore
$ go tool tinystore migrate app new add_description
wrote internal/data/migrations/002_add_description.sql:

    ALTER TABLE notes ADD COLUMN description TEXT NOT NULL DEFAULT '';

Contracts

  • What a call's name starts with says where it runs. Exec, ExecOne, ExecAll, ExecScalar and Insert may write, and run on the file's one writer; One, All, Each and Scalar read, on readers that refuse to write: a write sent to one fails before writing anything, with tinystore.ErrInvalid naming the call to use. Every typed call takes a *DB, or the *Tx of DB.Tx or DB.View.

  • A read is one statement without a transaction, a statement being its own snapshot, on one of eight readers. Each connection keeps 128 compiled statements by their text, the least recently used closed first; a bound limit ? compiles every call, and limit cast(? as integer) does not.

  • One answers found: false for no row, ErrInvalid for two. Scalar reads one column of one row, for a query that always answers, as count(*) does, and no row is ErrInvalid. All holds its rows in the store's memory as it decodes them, and past 64 MiB of them is tinystore.ErrLimit; Each decodes a row at a time from one snapshot, which a break lets go and which lasts at most five seconds, since the write-ahead log grows with the oldest reader: an export longer than that pages by key.

  • A struct takes each column into the field of its name: CreatedAt is created_at, db:"name" names a column otherwise, db:"-" leaves a field out, and db:",generated" marks a column the database fills, read like any other and never written by Insert. An embedded struct's fields count as the embedding one's. A column no field takes is ErrInvalid; a field no column fills keeps its zero value. A T that is not such a struct takes the only column.

  • A value decodes into its field or says why, as ErrInvalid naming the column, the value and the field: column done: TEXT "yes" does not decode into Note.Done (bool). A read asks what SQLite returned, never what the table meant, since a column of an expression has no declared type:

    a field of type is the column which also checks and reads from
    string, named strings TEXT TEXT
    []byte BLOB BLOB, TEXT
    bool INTEGER IN (0, 1) INTEGER 0 or 1
    integers, named integers INTEGER INTEGER in the field's range
    float32, float64 REAL REAL, INTEGER
    time.Time INTEGER, unix milliseconds INTEGER; TEXT as SQLite's date functions or RFC 3339 spell it
    time.Duration INTEGER, milliseconds INTEGER
    uuid.UUID of github.com/google/uuid or github.com/gofrs/uuid TEXT, lower case TEXT; a 16-byte BLOB
    any other [N]byte BLOB length(…) = N a BLOB of N bytes
    sqldb.Date TEXT, YYYY-MM-DD IS date(…) TEXT
    sqldb.JSON[T] TEXT json_valid(…) TEXT, through encoding/json
    a type with its own Scan and Value what sqldb.Storage says what its Scan takes
    *T, sql.Null[T], sql.NullString and its kin as T, and NULL as T, and NULL
  • A parameter is written by its Go type, as its column would hold it: a time.Time as unix milliseconds, a known uuid as its text, a sqldb.JSON[T] as its JSON, a sqldb.Date as YYYY-MM-DD, a bool as 0 or 1, inside sql.Named too. A value SQLite would store otherwise is ErrInvalid before anything is written: a NaN, which a REAL column keeps as NULL, a uint64 past int64, a Date no calendar has.

  • A write returns once it is durable. Writes from many goroutines wait for the writer together and commit in one transaction, each in a savepoint, with one fsync: one that fails rolls back alone, one whose caller's context ends before its turn writes nothing, one that has started finishes with its group though its caller cancels. A deadline that passes while its statement runs ends the statement, since the application's SQL may never end, and SQLite then rolls back the whole transaction: the group fails with it, each write told so. A group holds the writer at most ten seconds counted from when it holds it, so a transaction holding the writer longer fails none of the writes behind it; a group whose commit fails answers sqldb.ErrOutcomeUnknown, and its caller reads back before writing again. A panic while an Exec form reads its rows rolls back its statement alone and goes on in the caller's goroutine.

  • Insert writes one row in one statement, grouped like any Exec: every column but the generated ones as its field holds it, false as 0 and nil as NULL, whatever a Default says. It returns the row as the file keeps it, a time to the millisecond in UTC, with what the database generated: the rowid the insert's result carries, and the generated columns a default fills, which it returns. Everything else is SQL.

  • Batch writes several statements as one, gathered before any runs and then committed with the writes beside them, in one savepoint of the group: all of them or none, a failure rolling back the batch alone, one fsync for the group. Building it runs nothing, so it may take its time; a statement that needs an earlier one's result says so in SQL, last_insert_rowid() or a key the program chose, and one that must read before it decides is a Tx. An engine that keeps its tables in the database adds its own writes, as a jobs queue's Enqueued adds a job that commits with the rows it is about.

  • Tx is a transaction of its own on the writer, run on the caller's goroutine: nil commits, an error rolls back, and a panic rolls back and goes on. A call on the DB inside its own Tx waits for the Tx, which waits for it, until the call's context ends; a write that has waited ten seconds for the writer is logged once, naming the transaction's caller. View reads several statements from one snapshot, held at most five seconds; a write inside it is ErrInvalid, and a Tx used after its function returned is tinystore.ErrClosed. A Tx holds the writer for itself and pays an fsync of its own, so transactions from many goroutines commit one at a time: what can be a Batch should be. Tx.Add writes another engine's change in the transaction, as a Batch adds one, told the outcome once the transaction ends; make the change before the transaction begins, since it holds memory that a transaction holding the writer must not wait for.

  • Copy copies a database no one in the store has open into a snapshot's directory, as the store's Snapshot copies an open one, without opening it: a backup of a whole directory takes it, and a migration that waits for its program still applies when the program opens it. It holds the name while it copies, so an Open meanwhile, or a copy of an open one, is tinystore.ErrInUse.

  • Tables named _tinystore_… are the store's: its migration history, and the queues a jobs store opened In the database keeps there. A schema leaves them out, and a table cannot be declared with such a name.

  • A declaration that cannot be a table panics when the program starts, as regexp.MustCompile does, naming the table: an option naming a column the struct lacks, a default of another type, a generated column nothing fills, a field sqldb cannot store, a custom type without Storage, a reference to a table keyed by several columns or of another storage, two primary keys or two indexes of one name. References points at its parent's one-column key, Unique is an index rather than a table constraint, an index is named <table>_<columns> unless NamedIndex names it, and Schema(...).SQL() prints every statement the migrations have to make, each table STRICT.

  • Migrations are the .sql files at the root of the fs.FS given or, when it holds none, in its one directory, applied in name order and all pending ones in one transaction, with foreign keys off and foreign_key_check before the commit, since rebuilding a parent with them on deletes its children. Each is kept by its checksum: an edited, renamed or missing one, a file that has applied more than the binary knows, or another engine's file refuses to open, ErrInvalid.

  • Nil migrations open the file as it is. Open(ctx, store, "app", nil, nil) makes an empty file when there is none, applies nothing and checks no history, for a script or a first try; with ApplyNone it opens only a file that is there. A later Open that carries migrations applies them from the first.

  • ApplyNone and Migrated apply nothing. Open(…, sqldb.ApplyNone()) opens only a file that applied every migration given, and makes none that is not there; db.Migrated(ctx, migrations) checks an open file the same way, since a database opens once a store. A migration the file has not applied is sqldb.ErrPending with ErrInvalid; an edited one refuses as Open refuses it. The server opens a data client's database so.

  • Query and ExecQuery read rows without a struct: the columns' names, even of no row, and each value as SQLite returned it, nil, an int64, a float64, a string or a []byte, held in the store's memory as All holds rows; ExecQuery runs on the writer, for a returning clause. A value is what the row keeps, whatever its column declares: a time held as text comes back as its text.

  • A statement SQLite refuses is ErrInvalid: a syntax error, a table, column or function the file does not have, a parameter out of range or one no argument fills; a value past SQLite's length is ErrLimit.

  • Open checks the file and never changes it. With a schema, it compares what SQLite describes of each declared table, its columns' storage, nullability and defaults that are values, its key, references and indexes, STRICT, and refuses a difference, ErrInvalid listing each. An expression, a CHECK or a DefaultSQL, never refuses a file; a table, trigger, view or index on an expression the schema does not declare is the migrations' own.

  • A constraint is a *sqldb.ConstraintError: its Kind, UniqueViolation, PrimaryKeyViolation, ForeignKeyViolation, CheckViolation or NotNullViolation, comes from SQLite's extended code, and Table and Constraint from its message when it names them. A unique or primary key is tinystore.ErrConflict, the others ErrInvalid.

  • Calls hold the store's memory. With Options.Memory, a call reserves 64 KiB and its arguments before it waits for a connection, and takes more only while it is free once it holds one, which it may otherwise wait for itself: a result past the free memory is ErrLimit. Inside Tx or View a call waits for no memory. No statement makes a string, blob or row longer than the whole budget: SQLite would allocate it before a call could count it, so it is ErrLimit at once, a length(printf(…)) whose answer is one number included.

  • An error names its database, sql "app", and a context's error is kept as it came. A name is [a-z0-9][a-z0-9_-]{0,63}; a second Open of a name in one store is tinystore.ErrInUse; after the store closes, every call is tinystore.ErrClosed.

The schema in a test

sqldbtest.CheckSchema(t, name, schema, dir) applies the migrations in dir to a new file through Open and compares it with the schema. A difference of structure fails the test, a line each, + for what the schema declares and the migrations do not make, - for the reverse:

sql "app": the schema declares what the migrations do not make:
  + notes.description TEXT NOT NULL DEFAULT ''
write it with: go tool tinystore migrate app new <what>

A CHECK or a default's expression spelled otherwise is logged as a line to read, never a failure. A test never writes. go tool tinystore migrate [name] new <what>, of the module cmd/tinystore, finds the test that checks the database by its name, runs it asking for the next migration, and prints what it wrote; a change the tool cannot decide, a rename or a new type, is a draft whose TODO keeps it from running until a person writes it. go tool tinystore migrate [name] says what differs, go tool tinystore schema [name] prints the schema's SQL.

Not in the first version

What design/sqldb.md leaves for later: Update of a whole row, Upsert, InsertAll and binding a struct's fields by name; a reference over several columns; building queries from pieces; the schema's manifest, a server and other languages; change notifications; search that knows Russian; decimal.

Documentation

Overview

Package sqldb gives an application its own SQLite databases inside a tinystore.Store. The application writes the SQL; a struct says what a row is, Table what a struct cannot say, and the engine owns the file, the connections, the migrations, the values and the transactions.

What a call's name starts with says where it runs: Exec, ExecOne, ExecAll, ExecScalar, ExecQuery and Insert may write and go to the file's one writer; One, All, Each, Scalar and Query read, on readers that refuse to write.

The package sqldbtest checks, in a program's tests, that a database's migrations make what its schema declares.

Index

Constants

This section is empty.

Variables

View Source
var ErrOutcomeUnknown = sqlite.ErrOutcomeUnknown

ErrOutcomeUnknown is a write whose group's commit failed: it may or may not be in the file, and its caller reads it back before writing again.

View Source
var ErrPending = sqlite.ErrPending

ErrPending is a migration the file has not applied, which ApplyNone and Migrated find and apply none of; it comes with tinystore.ErrInvalid.

Functions

func All

func All[T any](ctx context.Context, h Handle, query string, args ...any) ([]T, error)

All reads every row query returns, holding them in the store's memory as it decodes them. Past 64 MiB of them it is tinystore.ErrLimit, and Each reads them instead.

func Copy

func Copy(ctx context.Context, store *tinystore.Store, name, dir string) (tinystore.SnapshotFile, error)

Copy writes a consistent copy of the database name into dir, as the store's Snapshot copies a database that is open, while no one in this store has it open: a backup of a whole directory takes the databases no program opened too. Opening one instead would make it open for good, and a migration that waits for its program would then wait for the store to open again.

It holds the name while it copies, so an Open meanwhile is tinystore.ErrInUse, as is a copy of a database that is open.

func Each

func Each[T any](ctx context.Context, h Handle, query string, args ...any) iter.Seq2[T, error]

Each decodes the rows query returns a row at a time, from one snapshot held at most five seconds, which a break lets go; an export longer than that pages by key. An error ends the rows as their last element.

func ExecAll

func ExecAll[T any](ctx context.Context, h Handle, query string, args ...any) ([]T, error)

ExecAll runs a write on the writer and reads every row its returning clause gives, as All reads a query's.

func ExecOne

func ExecOne[T any](ctx context.Context, h Handle, query string, args ...any) (T, bool, error)

ExecOne runs a write on the writer and reads the one row its returning clause gives, as One reads a query's.

func ExecScalar

func ExecScalar[T any](ctx context.Context, h Handle, query string, args ...any) (T, error)

ExecScalar runs a write on the writer and reads the one column of the one row its returning clause gives, such as the id of insert … returning id.

func Insert

func Insert[T any](ctx context.Context, h Handle, table *TableDef[T], row T) (T, error)

Insert writes row into table in one statement, grouped like any Exec: every column but the generated ones as its field holds it, false as 0 and nil as NULL. It returns the row as the file keeps it, a time to the millisecond in UTC, with what the database generated: the rowid its result carries, and the generated columns the insert returns.

INSERT INTO notes (id, title, done) VALUES (?, ?, ?)

func One

func One[T any](ctx context.Context, h Handle, query string, args ...any) (T, bool, error)

One reads the one row query returns into T: a struct takes each column into the field of its name, and any other T takes the only column. found is false for no row; two are tinystore.ErrInvalid.

func Scalar

func Scalar[T any](ctx context.Context, h Handle, query string, args ...any) (T, error)

Scalar reads one column of one row, for a query that always answers, as count(*) does; a query that may find nothing asks One.

Types

type Action

type Action int

Action is what a reference does when the row it refers to is deleted.

const (
	Cascade Action = iota + 1
	SetNull
)

func (Action) String

func (a Action) String() string

type AnyTable

type AnyTable interface {
	// contains filtered or unexported methods
}

AnyTable is a table of any row type, as Schema and References take it.

type Batch

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

Batch is writes gathered before any of them runs, which then commit together or not at all: statements, and what another engine keeps in this database, such as a job that a jobs store opened with Options.In enqueues.

Nothing runs while the batch is built, so building it may take as long as it likes; what it cannot do is read what an earlier write of it wrote. A statement that depends on an earlier one says so in SQL, with last_insert_rowid() or a key the program chose; one that needs to read first and decide belongs in a Tx.

func (*Batch) Add

func (b *Batch) Add(change Change)

Add adds another engine's write to the batch; it runs after the statements.

func (*Batch) Exec

func (b *Batch) Exec(query string, args ...any)

Exec adds a statement to the batch; it runs when the batch does.

type Change

type Change interface {
	// Bytes is what the change holds, counted against the group's bound, or
	// why it cannot be written, which writes nothing of the batch.
	Bytes() (int, error)
	// Apply writes the change with the batch's writer. file is the database's,
	// which the change refuses when its tables are in another.
	Apply(ctx context.Context, file *sqlite.File, w sqlite.Writer) error
	// Done is told the batch's outcome once, nil when it is durable, whatever
	// became of it, so that the change lets go of what it holds.
	Done(err error)
}

Change is a write another engine keeps in this database, which a Batch runs beside its statements, in their savepoint. A program does not implement it: its methods name the store's own types. An engine gives one out, as a jobs queue's Enqueued does.

type ConstraintError

type ConstraintError struct {
	// Kind comes from SQLite's extended code.
	Kind ConstraintKind
	// Table and Constraint are empty when SQLite's message does not name them.
	Table        string
	Constraint   string
	ExtendedCode int
	Err          error
	// contains filtered or unexported fields
}

ConstraintError is a write that broke a constraint. A unique or primary key is tinystore.ErrConflict, the others tinystore.ErrInvalid.

SQLite's message becomes Table and Constraint when it names them:

UNIQUE constraint failed: users.email      Table users, Constraint email
CHECK constraint failed: length(title) > 0 Constraint length(title) > 0

func (*ConstraintError) Error

func (e *ConstraintError) Error() string

func (*ConstraintError) Unwrap

func (e *ConstraintError) Unwrap() []error

type ConstraintKind

type ConstraintKind int

ConstraintKind is the constraint a write broke.

const (
	UniqueViolation ConstraintKind = iota + 1
	PrimaryKeyViolation
	ForeignKeyViolation
	CheckViolation
	NotNullViolation
)

func (ConstraintKind) String

func (k ConstraintKind) String() string

type DB

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

func Open

func Open(ctx context.Context, store *tinystore.Store, name string, migrations fs.FS, schema *SchemaDef,
	options ...OpenOption,
) (*DB, error)

Open opens or creates sql/<name>.db inside the store, applies in one transaction every migration the file has not applied, then checks the file against schema, which may be nil:

001_users.sql   applied on the first run
002_posts.sql   applied the first time a binary carrying it opens the file

The migrations are the .sql files at the root of migrations or, when it has none, in its one directory, as an embed.FS of migrations/*.sql holds them. A migration changed after it was applied, a file that has applied more of them than the binary knows, another engine's file, or a file that does not match the schema refuses to open.

Nil migrations open the file as it is, making an empty one when there is none: nothing is applied and no history is checked, as a script or a first try wants. A later Open that carries migrations applies them from the first.

func (*DB) Batch

func (d *DB) Batch(ctx context.Context, build func(*Batch) error) (err error)

Batch runs what build adds in one savepoint of a grouped commit, as an Exec runs its one statement: they share an fsync with the writes beside them, and a batch whose statement or change fails rolls back alone. An error from build, an argument no column can hold or a change that cannot be written writes nothing.

A Tx holds the writer for itself and pays a commit of its own; a batch, whose writes are known before it runs, need not.

func (*DB) Close

func (d *DB) Close(ctx context.Context) error

Close lets the calls in flight finish and closes the file; cancellation stops waiting, not the cleanup. The store calls it: an application closes the store instead.

func (*DB) Exec

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

Exec runs one statement that may write on the file's one writer, and returns once it is durable.

Statements from many goroutines commit together, each in its savepoint of one transaction, with one fsync.

A statement that fails rolls back alone. One whose caller's context ends before its turn writes nothing. A group whose commit fails answers ErrOutcomeUnknown.

func (*DB) Migrated

func (d *DB) Migrated(ctx context.Context, migrations fs.FS) error

Migrated checks migrations against those the file applied, applying none: one it has not applied is ErrPending, and one changed after it was applied, or missing, is refused as Open refuses it. A database opens once in a store, so a program that opens it again checks with Migrated instead. Nil migrations check nothing, as Open opens with none.

func (*DB) SQLiteFile

func (d *DB) SQLiteFile() *sqlite.File

SQLiteFile is the database's file, for an engine that keeps its tables in it, as a jobs store opened with Options.In does. A program has no use for it.

func (*DB) Snapshot

func (d *DB) Snapshot(ctx context.Context, dir string) ([]tinystore.SnapshotFile, error)

Snapshot copies the database into dir while it keeps working.

func (*DB) Tx

func (d *DB) Tx(ctx context.Context, work func(tx *Tx) error) (err error)

Tx runs work in one writer transaction of its own, on the caller's goroutine: nil commits, an error rolls back, and a panic rolls back and goes on.

A call on the DB inside its own Tx waits for the Tx, which waits for it, until the call's context ends. sqldb logs a write that has waited ten seconds, naming the transaction.

func (*DB) View

func (d *DB) View(ctx context.Context, read func(tx *Tx) error) error

View runs read against one snapshot of the database, held at most five seconds, since the write-ahead log grows with the oldest reader. A write inside it is tinystore.ErrInvalid.

type Date

type Date struct {
	Year  int
	Month time.Month
	Day   int
}

Date is a day of the calendar without a time or a zone, kept as its YYYY-MM-DD text, which SQLite's date functions read and write.

func DateOf

func DateOf(t time.Time) Date

DateOf is the day t falls on in its own location.

func (Date) In

func (d Date) In(loc *time.Location) time.Time

In is the day's first moment in loc.

func (Date) String

func (d Date) String() string

type Handle

type Handle interface {
	// contains filtered or unexported methods
}

Handle is where a call runs: a *DB, or the *Tx that DB.Tx or DB.View gives.

type JSON

type JSON[T any] struct{ V T }

JSON holds V, which its column keeps as JSON text through encoding/json. A JSON holding a nil slice or map writes null, a valid JSON text; the column is NULL only through a pointer to the JSON.

func JSONOf

func JSONOf[T any](v T) JSON[T]

JSONOf wraps v, as a query's argument or a field's value.

type OpenOption

type OpenOption func(*tuning)

func ApplyNone

func ApplyNone() OpenOption

ApplyNone opens a file only if it has applied every migration given, and applies none, as a program that may change rows and not the schema opens one.

A migration the file has not applied is ErrPending, and a file that is not there is not made.

type Rows

type Rows struct {
	Columns []string
	Values  [][]any
}

Rows is what a query returns without a struct to take it: the columns' names, and each row's values as SQLite returned them, a column each.

A value is nil, an int64, a float64, a string or a []byte, what the row keeps whatever its column declares.

func ExecQuery

func ExecQuery(ctx context.Context, h Handle, query string, args ...any) (Rows, error)

ExecQuery runs a write on the writer and reads the rows its returning clause gives, as Query reads a query's. A write that returns none answers its columns alone, or none.

func Query

func Query(ctx context.Context, h Handle, query string, args ...any) (Rows, error)

Query reads every row query returns as SQLite returns it, for a program that has no struct for them, as the server has none for its clients. The rows are held in the store's memory as All holds them.

type SchemaDef

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

SchemaDef is a database's tables, in the order Schema was given them.

func Schema

func Schema(tables ...AnyTable) *SchemaDef

Schema is what the migrations of one database make, and what Open checks the file against. It panics, as Table does, on two tables of one name and on a reference to a table it does not hold.

func (*SchemaDef) SQL

func (s *SchemaDef) SQL() string

SQL is every statement the schema stands for, as its migrations have to make it.

type StorageClass

type StorageClass int

StorageClass is the STRICT column a custom type, or a uuid, is kept in.

const (
	Text StorageClass = iota + 1
	Integer
	Real
	Blob
)

func (StorageClass) String

func (s StorageClass) String() string

type TableDef

type TableDef[T any] struct {
	// contains filtered or unexported fields
}

TableDef is a table declared for the rows of T: what Schema prints, what Open checks the file against and what Insert writes. Table makes it.

func Table

func Table[T any](name string, options ...TableOption) *TableDef[T]

Table declares the table name for the rows of T, and panics, as regexp.MustCompile does, on a declaration that cannot be a table:

an option naming a column T lacks
a default of another type
a generated column nothing fills
a field sqldb cannot store
a custom type without Storage
a reference to a table keyed by several columns

type TableOption

type TableOption func(*table) error

TableOption says what a struct cannot: keys, indexes, references, defaults, checks and storage.

func Check

func Check(expression string) TableOption

Check is a condition every row keeps, as SQL spells it.

func Default

func Default(column string, value any) TableOption

Default is the value SQL gives column when an insert leaves it out, and what lets a later migration add a NOT NULL column to a table holding rows. Insert writes every field, so it never uses one. value is of the field's type, or of the type a pointer, a sql.Null or a sqldb.JSON holds.

func DefaultSQL

func DefaultSQL(column, expression string) TableOption

DefaultSQL is an expression SQL computes for column when an insert leaves it out, such as lower(hex(randomblob(4))).

func Index

func Index(columns ...string) TableOption

Index is an index named <table>_<columns>.

func NamedIndex

func NamedIndex(name string, columns ...string) TableOption

NamedIndex is an index a migration made under a name of its own.

func PrimaryKey

func PrimaryKey(columns ...string) TableOption

PrimaryKey is the table's key; a single INTEGER column is the rowid SQLite numbers new rows by.

func References

func References(column string, parent AnyTable, action ...Action) TableOption

References points column at the primary key of parent, which must be one column of the same storage; the action, when given, is ON DELETE's.

func Storage

func Storage(column string, class StorageClass) TableOption

Storage says how column keeps a type sqldb does not know, whose own Scan and Value convert it. It can also keep a uuid as its 16 bytes, which a raw query then passes as id[:].

func Unique

func Unique(columns ...string) TableOption

Unique is a unique index named <table>_<columns>, not a table constraint. A later migration can then drop it without rebuilding the table.

type Tx

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

Tx is one transaction of the database, given to the function DB.Tx or DB.View runs and used by the goroutine that runs it. Every typed call takes it where it takes a DB.

func (*Tx) Add

func (t *Tx) Add(ctx context.Context, change Change) error

Add writes another engine's change in the transaction, as a Batch's Add does after its statements: a job that a jobs store opened with Options.In enqueues, which then commits with the transaction's rows or not at all. The change is told the transaction's outcome once it ends, nil when it committed.

Make the change before the transaction begins. A change holds the memory its value needs, and a transaction holding the writer must not wait for memory that a write queued behind it holds.

func (*Tx) Exec

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

Exec runs one statement of the transaction.

Directories

Path Synopsis
internal
catalog
Package catalog reads what a SQLite file says its tables are, through its pragmas and the text sqlite_schema keeps, and says how two files differ: in structure, which sqldb's Open checks, and in the text of expressions, which a test reports.
Package catalog reads what a SQLite file says its tables are, through its pragmas and the text sqlite_schema keeps, and says how two files differ: in structure, which sqldb's Open checks, and in the text of expressions, which a test reports.
Package sqldbtest checks, in a program's own tests, that a database's migrations make what its schema declares, and writes the next migration when go tool tinystore asks it to.
Package sqldbtest checks, in a program's own tests, that a database's migrations make what its schema declares, and writes the next migration when go tool tinystore asks it to.

Jump to

Keyboard shortcuts

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