pistachio

package module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Apr 21, 2026 License: MIT Imports: 12 Imported by: 0

README

pistachio

CI codecov

Declarative schema management tool for PostgreSQL. Define your desired schema in SQL and let pistachio figure out the diff.

demo

Installation

Homebrew
brew install winebarrel/pistachio/pistachio
Download binary

Download the latest binary from Releases.

Usage

Usage: pist <command> [flags]

Flags:
  -h, --help                   Show context-sensitive help.
  -c, --conn-string="postgres://postgres@localhost/postgres"
                               PostgreSQL connection string. See
                               https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
                               ($PIST_CONN_STR)
      --password=STRING        PostgreSQL password ($PIST_PASSWORD).
  -n, --schemas=public,...     Schemas to inspect and modify ($PGSCHEMAS).
  -m, --schema-map=KEY=VALUE;...
                               Schema name mapping (e.g. -m old=new).
  -I, --include=INCLUDE,...    Include only tables/views/enums matching the
                               pattern (wildcard: *, ?).
  -E, --exclude=EXCLUDE,...    Exclude tables/views/enums matching the pattern
                               (wildcard: *, ?).
      --version

Commands:
  apply <files> ... [flags]
    Apply schema changes to the database.

  plan <files> ... [flags]
    Print the schema diff SQL without applying it.

  dump [flags]
    Dump the current database schema as SQL.

Run "pist <command> --help" for more information on a command.
plan

Compare schema file(s) against the current database and print the SQL needed to reconcile them.

pist plan schema.sql

# Multiple files
pist plan tables.sql views.sql

# Include pre-SQL in the output
pist plan schema.sql --pre-sql-file pre.sql
apply

Apply the diff to the database.

pist apply schema.sql

# Multiple files
pist apply tables.sql views.sql

Use --pre-sql-file to run SQL before applying changes. Use --with-tx to wrap everything in a transaction.

pist apply schema.sql --pre-sql-file pre.sql --with-tx
dump

Dump the current database schema as SQL. Output can be used directly as a schema file.

pist dump
Schema name mapping

Use -m / --schema-map to remap schema names. This is useful when you want to manage a database whose schema name differs from the one used in your SQL files.

For example, to dump a staging schema as if it were public:

pist -n staging -m staging=public dump

You can also use it with plan and apply. The desired SQL files use the mapped name (public), while the generated SQL targets the real database schema (staging):

# schema.sql uses "public" as the schema name
pist -n staging -m staging=public plan schema.sql
pist -n staging -m staging=public apply schema.sql
Filtering tables/views/enums

Use -I / --include to include only matching tables/views/enums, or -E / --exclude to exclude them. Patterns support * and ? wildcards. Patterns match against object names only (not schema-qualified names).

# Dump only objects matching "user*"
pist -I 'user*' dump

# Plan changes excluding temporary tables
pist -E 'tmp_*' plan schema.sql

# Combine include and exclude
pist -I 'user*' -E 'user_tmp' apply schema.sql
Omit schema

Use --omit-schema to omit schema names from the dump output.

pist dump --omit-schema
# => CREATE TABLE users (...) instead of CREATE TABLE public.users (...)

pist dump --omit-schema --split ./schema/
# => ./schema/users.sql, ./schema/orders.sql, ...

When schema is omitted in SQL files, plan and apply use the schema specified by -n:

pist -n staging plan schema.sql   # schema-less SQL is treated as "staging"
pist -n staging apply schema.sql
Renaming objects

Use -- pist:rename-from <old_name> directives to rename objects instead of dropping and recreating them.

Tables, views, enums:

-- pist:rename-from public.old_status
CREATE TYPE public.new_status AS ENUM ('active', 'inactive');

-- pist:rename-from public.old_users
CREATE TABLE public.users (
    id integer NOT NULL,
    CONSTRAINT users_pkey PRIMARY KEY (id)
);

-- pist:rename-from public.old_view
CREATE VIEW public.new_view AS SELECT 1;

Columns, constraints, indexes (inside CREATE TABLE or before CREATE INDEX / ALTER TABLE ADD CONSTRAINT):

CREATE TABLE public.users (
    id integer NOT NULL,
    -- pist:rename-from name
    display_name text NOT NULL,
    CONSTRAINT users_pkey PRIMARY KEY (id),
    -- pist:rename-from users_name_key
    CONSTRAINT users_display_name_key UNIQUE (display_name)
);

-- pist:rename-from idx_users_name
CREATE INDEX idx_users_display_name ON public.users (display_name);

-- pist:rename-from fk_old_name
ALTER TABLE public.orders ADD CONSTRAINT fk_new_name FOREIGN KEY (user_id) REFERENCES public.users(id);

[!TIP] Rename directives that have already been applied are silently skipped, so you can safely leave them in your schema files until cleanup.

Split dump

Use --split to output each table/view/enum as a separate file in the specified directory.

pist dump --split ./schema/
# => ./schema/public.status.sql, ./schema/public.users.sql, ./schema/public.orders.sql, ...

Example

Create a schema file:

CREATE TYPE public.status AS ENUM ('active', 'inactive');

CREATE TABLE public.users (
    id integer NOT NULL,
    name text NOT NULL,
    status status NOT NULL,
    CONSTRAINT users_pkey PRIMARY KEY (id)
);

CREATE TABLE public.posts (
    id integer NOT NULL,
    user_id integer NOT NULL,
    title text NOT NULL,
    CONSTRAINT posts_pkey PRIMARY KEY (id)
);

CREATE INDEX idx_posts_user_id ON public.posts USING btree (user_id);

ALTER TABLE ONLY public.posts
    ADD CONSTRAINT posts_user_id_fkey
    FOREIGN KEY (user_id) REFERENCES users(id);

Preview and apply:

pist plan schema.sql   # review the diff
pist apply schema.sql  # apply it

Or split schema into multiple files and use them together:

pist dump --split ./schema/       # dump per table/view/enum
pist plan ./schema/*.sql          # review the diff
pist apply ./schema/*.sql         # apply it

[!NOTE] Unnamed constraints (e.g. id integer PRIMARY KEY, name text UNIQUE, col integer REFERENCES other(id)) are not tracked by pistachio because PostgreSQL auto-generates their names at creation time, making them unpredictable from the SQL file alone. Use explicit CONSTRAINT <name> clauses to ensure constraints are managed correctly.

Supported Objects

  • Enum types (CREATE TYPE ... AS ENUM, ALTER TYPE ... ADD VALUE)
  • Tables (including unlogged and partitioned tables)
  • Views
  • Columns (serial/bigserial/smallserial, identity, generated)
  • Constraints (primary key, unique, check, exclusion, foreign key)
  • Indexes (unique, partial, expression, hash, multi-column)
  • Comments (on tables, columns, views, types)
  • Renaming (tables, views, enums, columns, constraints, foreign keys, indexes via -- pist:rename-from directive)
  • Array, JSON, UUID, and other built-in types
  • Quoted identifiers

Development

docker compose up -d
make test

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ApplyOptions

type ApplyOptions struct {
	Files      []string `arg:"" help:"Path to the desired schema SQL file(s)."`
	PreSQLFile string   `type:"path" help:"Path to a SQL file to execute before applying changes."`
	WithTx     bool     `help:"Execute the pre-SQL and schema changes in a transaction."`
}

type Client

type Client struct {
	*Options
}

func NewClient

func NewClient(options *Options) *Client

func (*Client) Apply

func (client *Client) Apply(ctx context.Context, options *ApplyOptions, w io.Writer) error

func (*Client) Dump

func (client *Client) Dump(ctx context.Context, options *DumpOptions) (*DumpResult, error)

func (*Client) Plan

func (client *Client) Plan(ctx context.Context, options *PlanOptions) (string, error)

type DumpOptions

type DumpOptions struct {
	Split      string `help:"Output each table/view/enum as a separate file in the specified directory."`
	OmitSchema bool   `help:"Omit schema name from the dump output."`
}

type DumpResult added in v0.3.0

type DumpResult struct {
	Tables     *orderedmap.Map[string, *model.Table]
	Views      *orderedmap.Map[string, *model.View]
	Enums      *orderedmap.Map[string, *model.Enum]
	OmitSchema bool
}

func (*DumpResult) Files added in v0.3.0

func (r *DumpResult) Files() map[string]string

func (*DumpResult) String added in v0.3.0

func (r *DumpResult) String() string

type Options

type Options struct {
	ConnString string            `` /* 195-byte string literal not displayed */
	Password   string            `env:"PIST_PASSWORD" help:"PostgreSQL password."`
	Schemas    []string          `short:"n" env:"PGSCHEMAS" default:"public" help:"Schemas to inspect and modify."`
	SchemaMap  map[string]string `short:"m" help:"Schema name mapping (e.g. -m old=new)."`
	Include    []string          `short:"I" help:"Include only tables/views/enums matching the pattern (wildcard: *, ?)."`
	Exclude    []string          `short:"E" help:"Exclude tables/views/enums matching the pattern (wildcard: *, ?)."`
}

func (*Options) AfterApply added in v0.4.0

func (o *Options) AfterApply() error

func (*Options) MatchName added in v0.5.0

func (o *Options) MatchName(name string) bool

func (*Options) RemapSchema added in v0.4.0

func (o *Options) RemapSchema(schema string) string

func (*Options) ReverseRemapSchema added in v0.4.0

func (o *Options) ReverseRemapSchema(schema string) string

func (*Options) ValidatePatterns added in v0.5.0

func (o *Options) ValidatePatterns() error

func (*Options) ValidateSchemaMap added in v0.4.0

func (o *Options) ValidateSchemaMap() error

type PlanOptions

type PlanOptions struct {
	Files      []string `arg:"" help:"Path to the desired schema SQL file(s)."`
	PreSQLFile string   `type:"path" help:"Path to a SQL file to execute before applying changes."`
}

Directories

Path Synopsis
cmd
pist command
internal

Jump to

Keyboard shortcuts

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