postgres

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package postgres is the PostgreSQL engine of blobfs: the PostgreSQL variant of the data package's variation points, the native-tier statements the variant runs, and the DDL of the two tables, exported as a migration set. A consumer selects the engine by importing this package, as it imports sqlate's postgres module for the dialect; there is no registry, no init, and no flag, and data.New builds the standard baseline when no engine is installed. The package imports only the data package, the root package, and sqlate, and names no driver.

The variant

Engine builds a Variant, which implements data.Variant over four native-tier statements with the store's standard baseline embedded, as the data.Variant contract requires, and overrides all three variation points:

  • The tree lock (lock_tree) is a transaction-scoped advisory lock, pg_advisory_xact_lock over the fixed key TreeLockKey, so two transactions that move directories run one after the other and their cycle checks cannot both pass. Serializes reports true.
  • Path resolution (resolve_path) walks every segment in one recursive statement that indexes a text[] parameter by depth: one round trip for any depth instead of one per segment. The segments bind as one parameter, which the driver encodes from the Go slice, so no name is spliced into the text. The walk is bounded by the segments.
  • The file hold (lock_file, and lock_file_at_version under data.AtVersion) is SELECT ... FOR NO KEY UPDATE, the row lock the baseline's self-assigning update takes and a delete waits on, taken without writing a row version: a hold leaves no dead tuple. A refused hold takes no lock, and the store classifies it as over the baseline.

The returning commands need no variant: under sqlate's postgres dialect, which renders RETURNING, the store runs each of them in the single-statement form from its standard-tier file. The keyset predicate needs none either: a consumer that registers sqlate's postgres.Patterns() in its catalog in place of query.Patterns() continues its pages by the row-value comparison, which PostgreSQL serves as an index condition.

Every statement file declares its tier as native and carries a port note: the engine feature it uses and what a port to another engine must provide. A consumer selects the engine at its composition root, over the one catalog and the dialect its store uses:

store, err := data.New(catalog, dialect, data.WithEngine(postgres.Engine))

data.New compiles the data package's statements once and hands the baseline it bound to Engine, which compiles only its own four. The store lists the variant's statements after its own, and its Verify prepares them in the same pass.

The conformance suite in package datatest proves the variant against the contract the baseline satisfies, comparing each outcome with the baseline's on the same database; this module's integration tier runs it under both forms of the returning commands, the single-statement form and the fallback.

The migration set

Migrations returns blobfs's migration set: the name Source, the history table Table, and two migrations, directory and file, embedded from the migrations directory. A consumer declares the set in migrate.New below its own, so blobfs's migrations are all applied before the consumer's migrations reference its tables.

The directory migration seeds the one root directory, the row with no parent, named /, and the id blobfs.RootID, and a partial unique index allows no second row without a parent. A check constraint states that a directory is named / exactly when it has no parent, so a root under another name or a non-root named / is refused.

The set ships no index on blobfs_file (directory_id, created_at). The unique constraint on (directory_id, name) orders a sort by name, and a sort by created_at without that index sorts the directory. A consumer that lists by creation time adds the index in its own migration set, for example CREATE INDEX ix_blobfs_file_directory_created ON blobfs_file (directory_id, created_at).

The set owns every object it creates, and every object's name starts with the set's name and an underscore: the tables blobfs_directory and blobfs_file, their constraints and indexes, and the history table blobfs_schema_version. Constraint and index names are public API, because a violation reaches a consumer as sqlate.ConstraintError.Constraint, and the data package maps blobfs's own constraints to its sentinel errors through the root package's constants. The scheme is blobfs_<kind>_<table>_<detail>, where kind is pk, fk, uq, cc, or ix (a plain index), table is the table name without its blobfs_ prefix, and detail names the referenced relation, the indexed columns, or the checked rule.

A released migration file never changes in text or name; the golden-hash test in this package pins each one. A second engine is a sub-module of its own with the same file names in its own migrations directory.

Index

Constants

View Source
const (
	// Source is the migration set's name and the prefix of every object
	// the set owns.
	Source = "blobfs"

	// Table is the history table the set's migrations are recorded in. It
	// is distinct from a consumer's own history table, so the two sets keep
	// separate version lines.
	Table = "blobfs_schema_version"
)
View Source
const TreeLockKey int64 = -8521165719926625175

TreeLockKey is the bigint key of the tree lock's advisory lock: the 64-bit FNV-1a hash of TreeLockName read as a signed integer, fixed for every install, since one install per database means one tree per database. A consumer that takes advisory locks of its own avoids it; this package's tests recompute it from the name.

View Source
const TreeLockName = "blobfs_directory.tree"

TreeLockName is the name the tree lock's key is derived from: the blobfs_ namespace and the table whose shape the lock serializes.

Variables

This section is empty.

Functions

func Engine

func Engine(catalog *query.Catalog, dialect sqlate.Dialect, base *data.Standard) (data.Variant, error)

Engine is the PostgreSQL engine for data.WithEngine: it compiles the variant's own four statements against catalog for dialect, binds them, and returns a *Variant over base, the baseline data.New compiled, so the data package's statements are compiled once. A consumer selects it at its composition root with data.New(catalog, dialect, data.WithEngine(Engine)). The catalog must carry the blobfs namespace, registered from data.Patterns(), because resolve_path returns the published directory columns. The variant's statements are not returning commands, so either form of the store's returning commands suits it. No I/O happens here.

func Migrations

func Migrations() (migrate.Set, error)

Migrations returns blobfs's whole migration set for PostgreSQL: the name Source, the history table Table, and the migrations in version order, read from the embedded migrations directory. A consumer declares it below its own set in migrate.New, so blobfs's migrations are all applied before the consumer's migrations reference its tables.

Types

type Variant

type Variant struct {
	*data.Standard
	// contains filtered or unexported fields
}

Variant is the PostgreSQL implementation of data.Variant, the variant Engine builds. It embeds the store's standard baseline, as the data.Variant contract requires, so a variation point this package does not override runs as the baseline does, and it binds the compiled native statements to their handles. It overrides all three: the tree lock, which the baseline cannot take; path resolution in one statement; and the file hold, taken without writing a row version. It holds no session; every method takes one.

func (*Variant) HoldFile

func (v *Variant) HoldFile(ctx context.Context, tx *sqlate.Tx, id string, version *int64) (bool, error)

HoldFile takes the file row's lock with lock_file, or lock_file_at_version when version is not nil: SELECT ... FOR NO KEY UPDATE, the lock the baseline's self-assigning update takes and delete_file waits on, without writing a row version. A row returned is a row held; no row, whether missing, deleting, or at another version, is false with no lock taken, and the store reads the row to classify, as it does over the baseline. See data.Variant.

func (*Variant) LockTree

func (v *Variant) LockTree(ctx context.Context, tx *sqlate.Tx) error

LockTree takes the transaction-scoped advisory lock under TreeLockKey in tx and returns once it is held. A transaction that holds it blocks every other LockTree until it commits or rolls back.

func (*Variant) ResolvePath

func (v *Variant) ResolvePath(ctx context.Context, sess sqlate.Session, startID string, segments []string) (blobfs.Directory, int, error)

ResolvePath walks segments below the directory with startID in one statement, the recursive query resolve_path, and returns the deepest directory reached and its depth, where the baseline reads the start and then one child per segment. The segments bind as one text[] parameter, encoded by the driver from the Go slice, so no name is ever spliced into the text; no segments bind as an empty array. No row means the start does not exist and is blobfs.ErrNotFound. See data.Variant.

func (*Variant) Serializes

func (*Variant) Serializes() bool

Serializes reports true: LockTree holds an engine lock to the end of the transaction.

func (*Variant) Statements

func (v *Variant) Statements() []query.Statement

Statements returns the variant's compiled inventory in name order, for a consumer that lists the SQL its program runs. The store's Statements appends it to the data package's own.

func (*Variant) Verify

func (v *Variant) Verify(ctx context.Context, sess sqlate.Session) error

Verify prepares the variant's statements against the schema the session reaches. The store's Verify runs it in the same pass as its own, so a startup Verify covers the variant's statements.

Jump to

Keyboard shortcuts

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