introspect

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package introspect reads a Postgres schema out of pg_catalog and returns the same *schema.Registry the DSL produces.

That symmetry is the point. A registry from here can be handed to migrate.Diff as the current state, which makes generating a migration and adopting an existing database the same machinery pointed in opposite directions (ADR-0014). It is also the only way to check the diff engine against a real database: render a schema to DDL, apply it, read it back, and the diff between what went in and what came out must be empty.

Why this is not in migrate

migrate does not connect to a database and says so in its own documentation: it produces files, and a runner applies them. This package does connect, so it is separate, and migrate stays a pure function over two data structures.

The connection

Everything here works through a sqlb.Executor, so a pool, a connection or a transaction the caller already holds all work, and reading a catalog uses the same handle as querying a table (ADR-0040).

What cannot be represented

The DSL is narrower than Postgres, and the failure that matters is dropping something quietly — a schema that looks complete, describes the database incorrectly, and produces a migration that reverses work nobody meant to reverse. So every construct this cannot express is collected into a Report rather than skipped. Read it. A Report with entries means the emitted schema is not the whole database.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Options

type Options struct {
	// Schema is the Postgres schema to read. Defaults to "public".
	Schema string

	// Module, when set, produces a module registry (schema.NewModule) and
	// strips the module's prefix from every table name it finds, so that a
	// database whose tables are called billing_invoices imports as a module
	// named billing holding a table called invoices.
	//
	// Tables without the prefix are left alone and reported, since a module
	// registry would silently rename them on the way back out.
	Module string
}

Options control what is read and how it is named.

type Report

type Report struct {
	Skipped []Skip
}

Report collects everything in the database that the DSL cannot express.

It exists because the dangerous failure here is a quiet one. A schema missing a construct still compiles, still validates, and still produces a migration — one that proposes undoing whatever it failed to see. ADR-0014 names silently dropping as the failure mode to watch for hardest, so nothing is dropped without an entry here.

An empty Report means the registry describes the database completely. A non-empty one means it does not, and the difference is yours to reconcile.

func Registry

func Registry(ctx context.Context, db sqlb.Executor, opts Options) (*schema.Registry, *Report, error)

Registry reads the database and returns the schema it describes, along with a Report of everything that could not be represented.

The registry is validated before it is returned: a registry that does not validate would produce DDL for a schema that cannot exist, and finding that out here beats finding it out from a migration.

Capabilities are not inferred. Nothing in a database says which columns should be filterable or exposed over REST, and guessing would publish columns nobody chose to publish — so everything imports with no capabilities at all and widening them is a deliberate, reviewable edit (ADR-0014).

func (*Report) Empty

func (r *Report) Empty() bool

Empty reports whether everything in the database was represented.

func (*Report) Err

func (r *Report) Err() error

Err returns an error describing everything skipped, or nil if nothing was.

It is offered rather than returned from Registry because whether an unrepresentable construct is fatal depends on what you are doing. Adopting a database wants to see the list and carry the remainder over by hand; round-tripping a schema this package generated wants any entry at all to be a failure.

func (*Report) String

func (r *Report) String() string

type Skip

type Skip struct {
	Table  string
	Object string // the constraint, index or column it concerns
	Reason string
	// Def is the definition Postgres reports, so the construct can be carried
	// across by hand without going back to the database to look it up.
	Def string
}

Skip is one construct that did not survive the import, where it was, and what to do about it.

Jump to

Keyboard shortcuts

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