{"id":49064518,"url":"https://github.com/winebarrel/pistachio","last_synced_at":"2026-06-27T08:01:23.818Z","repository":{"id":339901044,"uuid":"1163021307","full_name":"winebarrel/pistachio","owner":"winebarrel","description":"pistachio is a declarative schema management tool for PostgreSQL.","archived":false,"fork":false,"pushed_at":"2026-06-24T03:58:04.000Z","size":1685,"stargazers_count":17,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T05:15:06.849Z","etag":null,"topics":["db-migration","declarative-migrations","golang","postgres","postgresql","schema-changes","schema-migrations"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/winebarrel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-21T01:22:56.000Z","updated_at":"2026-06-24T03:58:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/winebarrel/pistachio","commit_stats":null,"previous_names":["winebarrel/pistachio"],"tags_count":72,"template":false,"template_full_name":null,"purl":"pkg:github/winebarrel/pistachio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winebarrel%2Fpistachio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winebarrel%2Fpistachio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winebarrel%2Fpistachio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winebarrel%2Fpistachio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/winebarrel","download_url":"https://codeload.github.com/winebarrel/pistachio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/winebarrel%2Fpistachio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34845749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["db-migration","declarative-migrations","golang","postgres","postgresql","schema-changes","schema-migrations"],"created_at":"2026-04-20T04:06:28.709Z","updated_at":"2026-06-27T08:01:23.786Z","avatar_url":"https://github.com/winebarrel.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](https://github.com/user-attachments/assets/6d48635e-1d93-4b4b-a7fc-e8f888780575#gh-light-mode-only)\n![](https://github.com/user-attachments/assets/42250c31-d8cd-40a6-954d-77574e959a09#gh-dark-mode-only)\n\n[![CI](https://github.com/winebarrel/pistachio/actions/workflows/ci.yml/badge.svg)](https://github.com/winebarrel/pistachio/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/winebarrel/pistachio/branch/main/graph/badge.svg?token=lWmtTkDrbz)](https://codecov.io/gh/winebarrel/pistachio)\n\nDeclarative schema management tool for PostgreSQL. Define the desired schema in SQL; pistachio generates the DDL diff.\n\nSee also: [Getting Started Guide](getting-started.md)\n\n![](https://github.com/user-attachments/assets/f280336b-52c2-409e-a8ba-f9a2ec71f950)\n\n## Installation\n\n### Homebrew\n\n```bash\nbrew install winebarrel/pistachio/pistachio\n```\n\n### Download binary\n\nDownload the latest binary from [Releases](https://github.com/winebarrel/pistachio/releases).\n\n## Demo\n\nA demo image bundles PostgreSQL with a sample schema for trying `pista` without a local install:\n\n```bash\ndocker run --rm -it ghcr.io/winebarrel/pistachio-demo\n```\n\nThe container starts a shell in `/demo` with `pista` and `psql` preconfigured. Edit `desired.sql`, then run:\n\n```bash\npista plan  desired.sql     # show the DDL diff\npista apply desired.sql     # apply the changes\npista plan  desired.sql     # ...should now print \"No changes\"\npista dump                  # dump the current schema\n```\n\nThe source for the image is under [`demo/`](demo/).\n\n## Usage\n\n```\nUsage: pista \u003ccommand\u003e [flags]\n\nFlags:\n  -h, --help                  Show context-sensitive help.\n  -c, --conn-string=\"postgres://postgres@localhost/postgres\"\n                              PostgreSQL connection string. See\n                              https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING\n                              ($PISTA_CONN_STR)\n  -d, --dbname=STRING         PostgreSQL database name. Overrides the dbname in\n                              --conn-string ($PISTA_DBNAME).\n      --password=STRING       PostgreSQL password ($PISTA_PASSWORD).\n  -n, --schemas=public,...    Schemas to inspect and modify ($PISTA_SCHEMAS).\n  -m, --schema-map=KEY=VALUE;...\n                              Schema name mapping (e.g. -m old=new).\n      --version\n      --[no-]pager            Force paging via $PISTA_PAGER even when stdout is\n                              not a TTY. PISTA_PAGER must be set.\n\nCommands:\n  apply \u003cfiles\u003e ... [flags]\n    Apply schema changes to the database.\n\n  plan \u003cfiles\u003e ... [flags]\n    Print the schema diff SQL without applying it.\n\n  dump [flags]\n    Dump the current database schema as SQL.\n\nRun \"pista \u003ccommand\u003e --help\" for more information on a command.\n```\n\n### plan\n\nCompare schema file(s) against the current database and print the SQL needed to reconcile them.\n\n```bash\npista plan schema.sql\n\n# Multiple files\npista plan tables.sql views.sql\n\n# Include pre-SQL in the output\npista plan schema.sql --pre-sql \"SET statement_timeout = '5s';\"\npista plan schema.sql --pre-sql-file pre.sql\n```\n\n`--pre-sql` / `--pre-sql-file` are also available as `$PISTA_PRE_SQL` / `$PISTA_PRE_SQL_FILE`.\n\n### apply\n\nApply the diff to the database.\n\n```bash\npista apply schema.sql\n\n# Multiple files\npista apply tables.sql views.sql\n```\n\nUse `--pre-sql` or `--pre-sql-file` to run SQL before applying changes (mutually exclusive). Also available as `$PISTA_PRE_SQL` / `$PISTA_PRE_SQL_FILE`. Use `--with-tx` to wrap the apply in a transaction (also available as `$PISTA_WITH_TX`).\n\n```bash\n# Inline SQL\npista apply schema.sql --pre-sql \"SET statement_timeout = '5s';\" --with-tx\n\n# From file\npista apply schema.sql --pre-sql-file pre.sql --with-tx\n```\n\nTo apply `CONCURRENTLY` to individual indexes, either write `CREATE INDEX CONCURRENTLY` directly or use the `-- pista:concurrently` directive before the `CREATE INDEX` statement. Both are treated equivalently:\n\n```sql\n-- pista:concurrently\nCREATE INDEX idx_users_name ON public.users USING btree (name);\n\n-- Equivalent: inline CONCURRENTLY\nCREATE INDEX CONCURRENTLY idx_users_email ON public.users USING btree (email);\n\n-- This index will NOT use CONCURRENTLY\nCREATE INDEX idx_users_id ON public.users USING btree (id);\n```\n\nUse `--concurrently-pre-sql` (or `--concurrently-pre-sql-file`) to run SQL (typically `SET lock_timeout = '...'`) before any `CONCURRENTLY` index DDL. The SQL is emitted only when the plan contains `CREATE/DROP INDEX CONCURRENTLY`. Because `SET` is session-scoped and `CONCURRENTLY` runs outside a transaction, the value carries over to every subsequent `CONCURRENTLY` statement in the same `apply`. Also available as `$PISTA_CONCURRENTLY_PRE_SQL` / `$PISTA_CONCURRENTLY_PRE_SQL_FILE`.\n\n```bash\npista apply schema.sql --concurrently-pre-sql \"SET lock_timeout = '5s';\"\n```\n\nUse `--disable-index-concurrently` to ignore all `CONCURRENTLY` opt-ins (both inline and directive) and emit plain `CREATE INDEX` / `DROP INDEX` instead. This lets you keep the directives in your schema files while running a one-off plan/apply inside a transaction. Also available as `$PISTA_DISABLE_INDEX_CONCURRENTLY`.\n\n```bash\npista plan --disable-index-concurrently schema.sql\npista apply --disable-index-concurrently --with-tx schema.sql\n```\n\nUse `--force-index-concurrently` to apply `CONCURRENTLY` to every `CREATE INDEX` and `DROP INDEX` the diff emits, regardless of per-index directives. This also covers pure drops (indexes removed from the desired schema), which the directive cannot reach. Conflicts with `--disable-index-concurrently` and `--with-tx`. Also available as `$PISTA_FORCE_INDEX_CONCURRENTLY`.\n\n```bash\npista plan --force-index-concurrently schema.sql\npista apply --force-index-concurrently schema.sql\n```\n\n\u003e [!NOTE]\n\u003e When the generated diff includes `CREATE INDEX CONCURRENTLY` or `DROP INDEX CONCURRENTLY`, `--with-tx` cannot be used because `CONCURRENTLY` operations cannot run inside a transaction. If there are no index changes, `--with-tx` is allowed even when an index is opted into `CONCURRENTLY`. To run `apply` inside a transaction in spite of the opt-in, combine `--with-tx` with `--disable-index-concurrently`.\n\nUse `--bulk-alter` to combine consecutive `ALTER TABLE` actions on the same table into a single statement with comma-separated actions. This reduces metadata-lock churn and lets PostgreSQL plan the changes together. Foreign keys, `RENAME`, `VALIDATE CONSTRAINT`, RLS toggles, and skipped DROPs are kept as separate statements. Also available as `$PISTA_BULK_ALTER`.\n\n```bash\npista plan --bulk-alter schema.sql\npista apply --bulk-alter schema.sql\n```\n\n```sql\nALTER TABLE public.users\n  ADD COLUMN email text,\n  ALTER COLUMN name SET NOT NULL,\n  DROP COLUMN legacy,\n  ADD CONSTRAINT users_id_pos CHECK (id \u003e 0);\n```\n\nBy default, `plan` and `apply` do not drop tables, views, enums, domains, columns, constraints, foreign keys, or indexes. Use `--allow-drop` to enable dropping specific object types (`all`, `table`, `view`, `enum`, `domain`, `column`, `constraint`, `foreign_key`, `index`). Also available as `$PISTA_ALLOW_DROP`. `constraint` covers CHECK / UNIQUE / PRIMARY KEY / EXCLUSION; foreign keys are governed by `foreign_key` separately.\n\n```bash\n# Allow all drops\npista plan --allow-drop all schema.sql\n\n# Allow only column and table drops\npista apply --allow-drop column,table schema.sql\n```\n\nSuppressed drops are emitted as commented-out DDL prefixed with `-- skipped:`. The plan still reports `-- No changes` when the only diff would be a suppressed drop, since no executable DDL is generated:\n\n```sql\n-- Plan for schema public (1 table, 0 views, 0 enums, 0 domains)\n-- skipped: DROP TABLE public.legacy_users;\n-- No changes\n```\n\n\u003e [!NOTE]\n\u003e Only pure removals of constraints, foreign keys, and indexes (those absent from the desired schema) are governed by `--allow-drop=constraint` / `--allow-drop=foreign_key` / `--allow-drop=index`. Definition changes still execute regardless of `--allow-drop`: constraints and foreign keys as DROP + ADD, and indexes as DROP + CREATE, because PostgreSQL has no `ALTER CONSTRAINT` and no general `ALTER INDEX` form for definition changes.\n\u003e\n\u003e Foreign-key drops emitted because the owning table is being dropped follow the table-drop policy (not `foreign_key`): if the table drop is suppressed, the FK drop is suppressed too and surfaces as `-- skipped:` alongside the table.\n\n### Executing arbitrary SQL\n\nUse `-- pista:execute` to include non-managed SQL (functions, triggers, grants) in your schema files. The check SQL after the directive is evaluated during `apply`. When it returns `true` the statement is executed, otherwise skipped. A common pattern skips when an object already exists:\n\n```sql\n-- pista:execute SELECT to_regprocedure('public.my_func()') IS NULL\nCREATE OR REPLACE FUNCTION public.my_func() RETURNS void AS $$ ... $$ LANGUAGE plpgsql;\n```\n\nTo manage a function whose body changes over time, embed a version tag in `COMMENT ON FUNCTION` and execute only when the installed comment differs. Wrap the `CREATE` and `COMMENT` in a `DO` block so they are a single statement:\n\n```sql\n-- pista:execute SELECT obj_description(to_regprocedure('public.get_user_count()'), 'pg_proc') IS DISTINCT FROM 'v1'\nDO $do$ BEGIN\n  CREATE OR REPLACE FUNCTION public.get_user_count() RETURNS bigint AS $body$\n    SELECT count(*) FROM public.users;\n  $body$ LANGUAGE sql;\n  COMMENT ON FUNCTION public.get_user_count() IS 'v1';\nEND $do$;\n```\n\nWhen the body changes, update the tag in both places (e.g. `'v1'` -\u003e `'v2'`); the next `apply` will re-run.\n\nSee [Getting Started](getting-started.md) for details.\n\n### dump\n\nDump the current database schema as SQL. Output can be used directly as a schema file.\n\n```bash\npista dump\n```\n\n### Paging long output\n\nSet `$PISTA_PAGER` to forward `plan` / `apply` / `dump` output through an external command when stdout is a TTY. The command is interpreted by `sh -c`, so quoting and arguments work as in the shell. Pipes and redirects (`pista dump \u003e file.sql`, `pista dump | grep ...`) are unaffected; the pager runs only for interactive output. Use `--no-pager` to disable it for a single invocation, or `--pager` to force it on when stdout is not a TTY (e.g. when piping into another pager-aware tool). `PISTA_PAGER` must still be set for `--pager` to do anything.\n\n```bash\n# Page with less, keeping ANSI colors\nPISTA_PAGER='less -R' pista dump\n\n# Pipe through a syntax highlighter that supports SQL\nPISTA_PAGER='source-highlight -s sql -f esc | less -R' pista plan schema.sql\n\n# One-off override\npista --no-pager plan schema.sql\n\n# Force the pager even when stdout is not a TTY\nPISTA_PAGER='source-highlight -s sql -f esc' pista --pager dump\n```\n\n### Schema name mapping\n\nUse `-m` / `--schema-map` to remap schema names when the database schema name differs from the one used in your SQL files.\n\nFor example, to dump a `staging` schema as if it were `public`:\n\n```bash\npista -n staging -m staging=public dump\n```\n\nYou 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`):\n\n```bash\n# schema.sql uses \"public\" as the schema name\npista -n staging -m staging=public plan schema.sql\npista -n staging -m staging=public apply schema.sql\n```\n\n### Filtering objects\n\nUse `-I` / `--include` to include only matching objects by name, or `-E` / `--exclude` to exclude them. Patterns support `*` and `?` wildcards. Patterns match against object names only (not schema-qualified names). Also available as `$PISTA_INCLUDE` / `$PISTA_EXCLUDE` environment variables.\n\nUse `--enable` to restrict operations to specific object types, or `--disable` to exclude specific types. Valid types: `table`, `view`, `enum`, `domain`. Can be repeated. Also available as `$PISTA_ENABLE` / `$PISTA_DISABLE` environment variables.\n\nThese flags are available on the `dump`, `plan`, and `apply` subcommands.\n\n```bash\n# Dump only objects matching \"user*\"\npista dump -I 'user*'\n\n# Plan changes excluding temporary tables\npista plan -E 'tmp_*' schema.sql\n\n# Combine include and exclude\npista apply -I 'user*' -E 'user_tmp' schema.sql\n\n# Dump only enums\npista dump --enable enum\n\n# Dump only tables and views\npista dump --enable table,view\n\n# Dump everything except views\npista dump --disable view\n\n# Plan changes for enums only\npista plan --enable enum schema.sql\n\n# Using environment variables\nPISTA_ENABLE=enum pista dump\nPISTA_DISABLE=view pista dump\nPISTA_INCLUDE='user*' pista dump\nPISTA_EXCLUDE='tmp_*' pista plan schema.sql\n```\n\n\u003e [!NOTE]\n\u003e `--enable` takes precedence over `--disable`. When `--enable` is set, only the specified types are included regardless of `--disable`. These flags may exclude dependent objects (e.g. `--enable table` omits enums/domains that table columns may reference); use them for inspection (`dump`, `plan`), not `apply`.\n\n\u003e [!NOTE]\n\u003e When both a CLI flag and its corresponding environment variable are set, the CLI flag overrides the environment variable (values are not merged). For example, running `PISTA_EXCLUDE='tmp_*' pista plan -E 'foo_*' schema.sql` excludes only `foo_*`; `tmp_*` is ignored.\n\n### Omit schema\n\nUse `--omit-schema` to omit schema names from the dump output.\n\n```bash\npista dump --omit-schema\n# =\u003e CREATE TABLE users (...) instead of CREATE TABLE public.users (...)\n\npista dump --omit-schema --split ./schema/\n# -- Dump of schema public (2 tables, 0 views, 0 enums, 0 domains)\n# -- Wrote 2 file(s) to ./schema/\n# (writes ./schema/users.sql, ./schema/orders.sql, ...)\n```\n\nWhen schema is omitted in SQL files, `plan` and `apply` use the schema specified by `-n`:\n\n```bash\npista -n staging plan schema.sql   # schema-less SQL is treated as \"staging\"\npista -n staging apply schema.sql\n```\n\n### Renaming objects\n\nUse `-- pista:renamed-from \u003cold_name\u003e` directives to rename objects instead of dropping and recreating them.\n\n**Tables, views, enums:**\n\n```sql\n-- pista:renamed-from public.old_status\nCREATE TYPE public.new_status AS ENUM ('active', 'inactive');\n\n-- pista:renamed-from public.old_users\nCREATE TABLE public.users (\n    id integer NOT NULL,\n    CONSTRAINT users_pkey PRIMARY KEY (id)\n);\n\n-- pista:renamed-from public.old_view\nCREATE VIEW public.new_view AS SELECT 1;\n```\n\n**Columns, constraints, indexes** (inside `CREATE TABLE` or before `CREATE INDEX` / `ALTER TABLE ADD CONSTRAINT`):\n\n```sql\nCREATE TABLE public.users (\n    id integer NOT NULL,\n    -- pista:renamed-from name\n    display_name text NOT NULL,\n    CONSTRAINT users_pkey PRIMARY KEY (id),\n    -- pista:renamed-from users_name_key\n    CONSTRAINT users_display_name_key UNIQUE (display_name)\n);\n\n-- pista:renamed-from idx_users_name\nCREATE INDEX idx_users_display_name ON public.users (display_name);\n\n-- pista:renamed-from fk_old_name\nALTER TABLE public.orders ADD CONSTRAINT fk_new_name FOREIGN KEY (user_id) REFERENCES public.users(id);\n```\n\n\u003e [!TIP]\n\u003e Rename directives that have already been applied are silently skipped. Leave them in place until cleanup.\n\n#### Column rename caveats\n\nWhen a column is renamed, pistachio rewrites column references in same-table indexes, constraints, and foreign keys (including `EXCLUDE`, partial / expression / `INCLUDE` indexes) on the current side, so a single `ALTER TABLE ... RENAME COLUMN` is emitted without redundant `DROP/CREATE` on the dependents.\n\nThe desired-side SQL must use the new column name in those dependent definitions:\n\n```sql\nCREATE TABLE public.users (\n    id integer NOT NULL,\n    -- pista:renamed-from name\n    display_name text NOT NULL,\n    CONSTRAINT users_pkey PRIMARY KEY (id)\n);\n-- Reference the new column name here:\nCREATE INDEX idx_users_name ON public.users (display_name);\n```\n\nIf the desired side still references the old name, `pista plan` errors out at parse time with a message like `column name referenced in index idx_users_name does not exist on table public.users` (identifiers are quoted only when they aren't safe unquoted). All such unresolved references are reported in a single error.\n\nThe following references are not auto-rewritten and may produce a redundant `DROP/CREATE` on the first plan (the second run after applying the rename is clean):\n\n- View / materialized view definitions that `SELECT` the renamed column\n- Foreign keys in other tables whose `REFERENCES this_table(renamed_col)` points at the renamed column\n\n### Split dump\n\nUse `--split` to output each table/view/enum/domain as a separate file in the specified directory.\n\n```bash\npista dump --split ./schema/\n# -- Dump of schema public (3 tables, 0 views, 1 enum, 0 domains)\n# -- Wrote 4 file(s) to ./schema/\n# (writes ./schema/public.status.sql, ./schema/public.users.sql, ./schema/public.orders.sql, ...)\n```\n\n## Example\n\nCreate a schema file:\n\n```sql\nCREATE TYPE public.status AS ENUM ('active', 'inactive');\n\nCREATE TABLE public.users (\n    id integer NOT NULL,\n    name text NOT NULL,\n    status status NOT NULL,\n    CONSTRAINT users_pkey PRIMARY KEY (id)\n);\n\nCREATE TABLE public.posts (\n    id integer NOT NULL,\n    user_id integer NOT NULL,\n    title text NOT NULL,\n    CONSTRAINT posts_pkey PRIMARY KEY (id)\n);\n\nCREATE INDEX idx_posts_user_id ON public.posts USING btree (user_id);\n\nALTER TABLE ONLY public.posts\n    ADD CONSTRAINT posts_user_id_fkey\n    FOREIGN KEY (user_id) REFERENCES users(id);\n```\n\nPreview and apply:\n\n```bash\npista plan schema.sql                  # review the diff (drops suppressed by default)\npista plan --allow-drop all schema.sql # review the diff (with drops)\npista apply schema.sql                 # apply it\n```\n\nOr split the schema across multiple files:\n\n```bash\npista dump --split ./schema/       # dump per table/view/enum/domain\npista plan ./schema/*.sql          # review the diff\npista apply ./schema/*.sql         # apply it\n```\n\n\u003e [!NOTE]\n\u003e Unnamed constraints (e.g. `id integer PRIMARY KEY`, `name text UNIQUE`, `col integer REFERENCES other(id)`) are auto-named by pistachio following PostgreSQL's convention (`{table}_pkey`, `{table}_{col}_key`, `{table}_{col}_check`, `{table}_{col}_fkey`, `{table}_{col}_excl`). The auto-naming has two limitations:\n\u003e - When multiple constraints would generate the same name, PostgreSQL appends a numeric suffix (e.g. `_1`) that pistachio cannot predict.\n\u003e - PostgreSQL truncates identifier names to 63 bytes (NAMEDATALEN - 1). pistachio does not apply this truncation, so very long table/column names may produce mismatched constraint names.\n\u003e\n\u003e Use explicit `CONSTRAINT \u003cname\u003e` clauses to avoid these issues.\n\n## Supported Objects\n\n- Domain types (`CREATE DOMAIN`, `ALTER DOMAIN SET/DROP DEFAULT`, `SET/DROP NOT NULL`, `ADD/DROP CONSTRAINT`)\n- Enum types (`CREATE TYPE ... AS ENUM`, `ALTER TYPE ... ADD VALUE`)\n- Tables (including unlogged and partitioned tables)\n- Views\n- Materialized views\n- Columns (serial/bigserial/smallserial, identity, generated)\n- Constraints (primary key, unique, check, exclusion, foreign key)\n- Indexes (unique, partial, expression, hash, multi-column)\n- Comments (on tables, columns, views, types, domains)\n- Row-level security (`ALTER TABLE ... ENABLE/DISABLE/FORCE/NO FORCE ROW LEVEL SECURITY`, policies via `CREATE POLICY` / `ALTER POLICY` / `DROP POLICY`)\n- Renaming (tables, views, enums, domains, columns, constraints, foreign keys, indexes, policies via `-- pista:renamed-from` directive)\n- Array, JSON, UUID, and other built-in types\n- Quoted identifiers\n\n## Development\n\n```bash\ndocker compose up -d\nmake test\n```\n\n## Related projects\n\n- [myschema](https://github.com/winebarrel/myschema): declarative\n  schema management for MySQL.\n- [ridgepole](https://github.com/ridgepole/ridgepole): DB schema\n  management using a Rails DSL.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinebarrel%2Fpistachio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwinebarrel%2Fpistachio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinebarrel%2Fpistachio/lists"}