Skip to content

Releases: nettrash/pgc

v1.0.19

03 May 12:18
Immutable release. Only release title and notes can be modified.
0be4a6a

Choose a tag to compare

Bug fixes:

  • Refactored comment-related query construction into dedicated
    helper methods on Dump:
    build_tables_standalone_query,
    build_regular_views_query,
    build_materialized_views_query,
    build_view_column_comments_query,
    build_sequences_standalone_query,
    and on Table: build_indexes_bulk_query.
  • Kept pg_description joins scoped to relation comments by
    filtering on pg_description.classoid = 'pg_class'::regclass
    and pg_description.objsubid = 0 to avoid catalog
    collisions.
  • Normalized SQL formatting in the new helper query strings.
  • Comparer::compare_grants no longer runs a duplicate
    iter().find(...) over from_cols for every TO column
    whose own ACL is empty. The first scan was discarded
    and the second was redone unconditionally; both are
    now replaced by a single per-table
    HashMap<&str, &[String]> lookup, which also reduces
    the column-grants emission from O(C^2) to O(C) on
    wide tables.
  • Comparer::mark_serial_columns now keys
    serial_columns by (schema, table, column) tuple
    instead of a format!("{}.{}.{}", ...) string that
    was parsed back via splitn(3, '.'). The string
    form silently misparsed any identifier containing a
    literal . (legal in PostgreSQL when quoted),
    leaving the corresponding column unmarked.
  • Comparer::compare_sequences had a dead
    dropped_sequences: HashSet<String> that was
    checked and updated on every iteration of a loop
    over self.from.sequences. Sequences are unique by
    (schema, name) so the dedupe could never fire;
    the set and its per-iteration format!/contains/
    insert calls are removed.
  • Comparer::compare_routines and
    Comparer::compare_routines_and_views no longer
    clone every Routine (each carries the full
    source_code string). The drop path now holds
    routines_to_drop: Vec<&Routine> borrowing into
    self.from.routines. The create/update path —
    previously forced to clone because
    process_target_routine took &mut self and so
    conflicted with any borrow into self.from /
    self.to — is unblocked by refactoring that
    method into a free associated function
    Self::emit_routine_diff(&mut script, use_drop, routine, from_routine). Disjoint-field split
    borrows now allow &mut self.script to coexist
    with &Routine borrows out of the dump fields,
    removing the per-emit clones. Same pattern as the
    pre-existing Self::emit_drop.

Performance:

  • Dump::process no longer materializes the entire
    serialized dump as a String before handing it to
    the zip writer. The JSON payload is now streamed
    into ZipWriter via serde_json::to_writer
    through a 256 KiB BufWriter, bounding peak
    memory at the buffer plus zlib's internal state
    rather than ~2x the uncompressed dump size. The
    BufWriter is required for speed: to_writer
    emits one write per JSON token, and feeding those
    straight into the deflate stream paid a per-call
    cost on every one (an early unbuffered version of
    this change made dumps roughly 10x slower). The
    write path is exposed as Dump::write_to_file,
    mirroring the existing Dump::read_from_file and
    making the round-trip directly testable.

Tests:

  • Added regression tests proving every new query
    builder includes the pg_class classoid filter:
    build_tables_standalone_query_filters_by_pg_class,
    build_regular_views_query_filters_by_pg_class,
    build_materialized_views_query_filters_by_pg_class,
    build_view_column_comments_query_filters_by_pg_class,
    build_sequences_standalone_query_filters_by_pg_class,
    and build_indexes_bulk_query_filters_by_pg_class.
  • write_to_file_round_trips_via_read_from_file
    builds a Dump with schemas, extensions, tables,
    views, sequences, and routines, writes it via the
    new streaming path, reads it back, and asserts
    every collection size and a few content fields
    match.
  • compare_column_grants_dispatches_per_column_acl_correctly
    builds a table with three columns whose effective
    from_acl differs (kept / revoked / newly
    granted) and verifies that the per-table
    column-ACL HashMap dispatches each column to the
    right diff outcome — guarding against off-by-one
    mistakes that single-column tests would miss.
  • mark_serial_columns_handles_dotted_identifier_names
    drives the mark_serial_columns path with a
    schema, table, and column name that all contain a
    literal ., asserting that the new tuple key
    still locates the target column. The pre-fix
    splitn(3, '.') parser would have failed on this
    input.

v1.0.18

25 Apr 20:31
Immutable release. Only release title and notes can be modified.
a1d0cda

Choose a tag to compare

Bug fixes:

  • Fixed grants for former owners after an owner change
    (two-stage fix). The original v1.0.17 behaviour
    filtered both old and new owners as implicit
    privilege holders, silently dropping explicit grants
    to the previous owner from the diff. The first
    attempt switched to filtering only the to owner —
    which exposed a non-idempotent oscillation: PG
    materialises an implicit-owner ACL row in
    pg_class.relacl (e.g. pgc_owner_from=arwdDxt/ pgc_owner_from) and treating it as a regular
    grantee made the comparer emit a long REVOKE on
    first run, then GRANT statements on every
    subsequent run. The fix splits owner filtering into
    asymmetric from_owners / to_owners lists that
    model the effect of ALTER ... OWNER TO: the FROM
    owner's implicit-owner row is stripped from the
    FROM side (because ALTER OWNER removes it) and
    the TO owner's implicit row is stripped from the
    TO side (because ALTER OWNER recreates it). When
    ownership does not change both lists hold the same
    single role, collapsing back to the original
    behaviour. Column ACLs (pg_attribute.attacl) pass
    an empty from_owners because ALTER TABLE OWNER
    does not touch column-level grants — explicit
    column grants to a former owner persist and must
    still be revoked under full mode.
  • FOREIGN TABLE ACL parsing now uses the same
    privilege set as TABLE (SELECT, INSERT, UPDATE,
    DELETE, TRUNCATE, REFERENCES, TRIGGER, MAINTAIN).
    Previously, foreign-table privileges were parsed
    against the default minimal set, which dropped
    legitimate privileges from the diff. Foreign-table
    grant emission now uses GRANT ... ON TABLE rather
    than the invalid GRANT ... ON FOREIGN TABLE
    syntax (PostgreSQL has no ON FOREIGN TABLE form
    for GRANT/REVOKE — applying the previous diff
    raised syntax error at or near "TABLE").
  • Foreign-table grants are now idempotent for newly
    created foreign tables. PostgreSQL applies default
    table privileges (pg_default_acl.defaclobjtype = 'r' covers foreign tables too) at CREATE time, so
    a TO-only foreign table inherits the FROM database's
    default ACL on the freshly created object. Without
    adjusting the comparer's effective from_acl, a
    second compare run after applying the migration
    emitted spurious REVOKE statements for those
    auto-applied privileges. The foreign-table grant
    path now mirrors the regular-table path: under
    full mode, new foreign tables use the FROM
    default-table ACL as the effective from_acl so
    any required revokes are emitted in the same diff
    run.
  • ACL parser now handles quoted role names with
    embedded =, /, or escaped "" quotes (e.g.
    "weird=name"=r/owner). The previous naive
    find('=') / find('/') split misparsed these and
    silently corrupted the grantee/grantor strings.
  • Dependency-scan needles in the comparer (used to
    decide drop/create order for views and routines)
    now strip quotes when matching against the
    quote-stripped haystack flavour. Dump fields
    populated via quote_ident literally embed " for
    mixed-case identifiers, and the previous code could
    not match a quoted needle against an unquoted
    reference in a definition body — silently losing
    real dependency edges.
  • --use-drop=false is now honoured uniformly for
    collations, text-search configurations, text-search
    dictionaries, casts, operators, default privileges,
    publications, subscriptions, foreign data wrappers,
    foreign servers, and user mappings. These eleven
    object kinds previously omitted the DROP entirely
    under use_drop=false; they now emit a commented
    DROP, matching the behaviour already in place for
    tables, views, types, enums, sequences, routines,
    rules, event triggers, and foreign tables.

Reliability:

  • current_setting('server_version_num') is now
    fetched once at the start of Dump::fill and
    threaded into the per-object-kind futures. Previously
    it was queried separately by the type and table
    branches, doubling the round-trip cost on every dump.
  • Unparseable ACL items are now logged with a
    Warning: line. Previously a silently-skipped
    grantee would simply disappear from the diff if a
    future PostgreSQL version introduced an aclitem
    syntax pgc did not yet recognise.
  • Config::load now warns when FROM_HOST or
    TO_HOST is empty. The same-target warning gated
    on both hosts being non-empty, so a config with
    missing host keys silently fell back to defaults.
  • get_schemas collapsed an if let Err followed by
    result.unwrap() into a single ?-chain, removing
    a redundant unwrap on a Result that was just
    pattern-matched.

Internals:

  • SEQUENCE ACL parsing explicitly documents that
    MAINTAIN ('m', PG17+) is filtered out — MAINTAIN is
    only valid on tables, views, materialised views,
    and foreign tables, never sequences.
  • to_owners semantics are documented on
    generate_column_grants_script (extending the
    doc already added to diff_acls): current owners
    are skipped because they have implicit access;
    former owners stay in the diff as ordinary grantees.

Tests:

  • New regression tests covering owner-change grant
    propagation, explicit grants to former owners under
    full mode, and the foreign-table privilege set.
  • Quoted-grantee ACL parsing tests for embedded =,
    /, escaped "", and quoted grantor.
  • Owner-unchanged with explicit owner grant in TO
    (must be filtered as implicit).
  • SEQUENCE with stray MAINTAIN bit (must be silently
    dropped, not emitted as an invalid GRANT).
  • Column-grant former-owner full-mode test where TO
    has no explicit column ACL (must still revoke
    from former owner without grants to the new owner).
  • Dependency-scan needle tests covering both
    quoted-against-unquoted and quoted-against-quoted
    references.

v1.0.17

23 Apr 07:48
Immutable release. Only release title and notes can be modified.
2db783e

Choose a tag to compare

Bug fixes:

  • PostgreSQL version agnostic handling of attstattarget
    attribute.
  • Replaced double .unwrap() in the dump serialization
    path with proper error propagation so a serde_json
    failure surfaces as a readable error instead of a
    panic.
  • compare_types no longer emits a stray
    -- Multirange … is created automatically comment for
    new range types. PostgreSQL auto-creates the
    companion multirange row when CREATE TYPE … AS RANGE
    runs, so the comparer now skips new-in-to
    multiranges in the CREATE branch (symmetric to the
    existing skip in the DROP branch). The skip is
    deliberately scoped to the new-type branch only — an
    existing multirange whose owner, comment, or ACL
    differs between dumps still reaches
    get_alter_script and emits the corresponding
    COMMENT ON TYPE / ALTER TYPE … OWNER TO. This
    was the leading cause of diffs looking "empty" on
    schemas that added a new range type.

Reliability:

  • Added FILL_SIBLING_BRANCH_COUNT constant with a
    compile-time arity guard against the outer try_join!
    in Dump::fill, plus a runtime warning when
    --max-connections / MAX_CONNECTIONS is below the
    number of parallel dump branches (pool starvation
    would otherwise go unnoticed).
  • Config::load now warns when FROM_* and TO_*
    resolve to the same host+port+database+schema, which
    almost always means a typo in the config file and
    silently produces an empty diff.

Tooling:

  • CI build job runs against both Rust 1.88.0 (floor)
    and 1.95.0 (matches the Dockerfile toolchain), so the
    shipped Docker image toolchain is CI-validated.
  • Dockerfile version / org.opencontainers.image.version
    labels are now filled from ARG PGC_VERSION wired
    through GitHub Actions from app/Cargo.toml — no
    more hard-coded version skew on release.

UX / Docs:

  • Fixed copy-pasted --server help text on --port
    and --scheme; documented the SIMILAR TO matching
    behaviour of --scheme (e.g. public|app) and
    expanded --grants-mode to describe ignore /
    addonly / full explicitly.
  • Added explanatory comments at every
    recreated_tables.insert() site documenting the
    coupling with compare_grants default-ACL handling
    for dropped-and-recreated tables.

Tests:

  • New buffer-ordering tests pin the emission order of
    the comparer's post-script buffers:
    sequence_post_script → type_post_script →
    enum_post_script → trigger_post_script.
  • Replaced .find().unwrap() assertions in the
    clear-script tests with a helper that panics with
    the full script, so ordering-test failures are
    actionable.
  • New multirange regression tests covering both sides
    of the fix:
    compare_types_multirange_not_created_independently
    (symptom fix) and
    compare_types_multirange_comment_change_still_emits_alter
    (guards against over-broad skipping — a metadata
    ALTER on an existing multirange must still emit).

v1.0.16

21 Apr 06:06
Immutable release. Only release title and notes can be modified.

Choose a tag to compare

            Performance:
                - Eliminated N+1 query pattern in the dump path. Per-table
                  metadata (columns, indexes, constraints, triggers, policies,
                  partition info, definitions) is now fetched via seven
                  schema-wide queries instead of seven queries per table,
                  collapsing `7 × N` round-trips into 7 on remote connections.
                - Prelower routine source code once per routine in O(n²)
                  dependency scans inside the comparer (three routine loops
                  plus the routine-and-view phase). Previously each inner
                  iteration re-lowercased the full source text.
                - Prelower view definitions once per view in the view-drop
                  dependency scan for the same reason.
                - Precompute foreign-key constraint definitions (lowercase
                  plus quote-stripped-lowercase) once across the FROM dump
                  before scanning dropped tables, removing repeated
                  `to_lowercase()` / `chars().filter()` work from the
                  O(T × T × C) inner loop.

            Bug fixes:
                - Config file values containing `=` (e.g. passwords with
                  base64 `=` padding) no longer cause `Invalid configuration
                  line` panics. The parser now splits on the first `=` only.
                - `Config::new` now has a non-panicking counterpart,
                  `Config::load(&str) -> Result<Config, String>`, which
                  `main.rs` uses to report malformed configs cleanly instead
                  of aborting with a Rust panic.

            Changes:
                - Default `MAX_CONNECTIONS` / `--max-connections` raised from
                  8 to 16. With the new bulk-query dump path the tool fires
                  up to ~18 concurrent introspection queries; the previous
                  default starved the pool on high-latency links.
                - README documentation for `MAX_CONNECTIONS` updated to
                  reflect the new bulk-query model.

v1.0.15

17 Apr 14:44
Immutable release. Only release title and notes can be modified.
44890b1

Choose a tag to compare

            Features:
                - UNLOGGED tables support with CREATE UNLOGGED TABLE and ALTER TABLE SET LOGGED/UNLOGGED.
                - Storage parameters (reloptions) support with WITH (...) and ALTER TABLE SET/RESET.
                - REPLICA IDENTITY support (DEFAULT, NOTHING, FULL) with ALTER TABLE.
                - FORCE ROW LEVEL SECURITY support with ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY.
                - Classical table inheritance (INHERITS) support.
                - Typed tables (OF type) support.
                - Per-column statistics target (SET STATISTICS) with ALTER support.
                - Function/procedure COST and ROWS clauses.
                - Function SUPPORT clause.
                - Function TRANSFORM FOR TYPE clause.
                - New object types with full CREATE/ALTER/DROP/compare support:
                    - Rules (`CREATE OR REPLACE RULE`, `DROP RULE`, comparison by definition hash).
                    - Collations (libc/ICU/default providers, locale, deterministic flag).
                    - Text search configurations (parser, token-to-dictionary mappings).
                    - Text search dictionaries (template and options).
                    - Foreign data wrappers (handler/validator functions, options).
                    - Foreign servers (type, version, FDW reference, options).
                    - User mappings (user-to-server with options).
                    - Publications (`FOR ALL TABLES` or per-table, publish operations).
                    - Subscriptions (connection string, publication list, enable/disable).
                    - Operators (procedure, left/right types, commutator, negator, HASHES/MERGES).
                    - Casts (function/binary-coercible/I-O, assignment/implicit context).
                    - Event triggers (ddl_command_start/end, sql_drop, table_rewrite, filter tags, enable states).
                - Default privileges comparison (`ALTER DEFAULT PRIVILEGES`) with per-role
                  and per-schema scoping, GRANT/REVOKE diff generation.
                - Column-level ACLs: per-column GRANT/REVOKE (SELECT, INSERT, UPDATE, REFERENCES).

            Bug fixes:
                - Removed `has_rules` from table hash computation. Rules are compared
                  independently; including them in the hash caused spurious view drops
                  when a rule was added or removed.
                - Added CASCADE to DROP TYPE statements. Dependent objects (e.g.
                  multirange types) could prevent type drops without it.
                - Fixed idempotency of grants for recreated objects. Tables that are
                  dropped and recreated during migration (e.g. partition key change)
                  now use the default privilege ACL as the effective source ACL in
                  `compare_grants`, so auto-applied grants are properly revoked in a
                  single diff run.
                - Fixed idempotency of grants for dropped and recreated views. Views
                  dropped as dependencies and then recreated now use the default
                  privilege ACL instead of an empty ACL, preventing persistent REVOKE
                  statements on subsequent runs.

v1.0.14

16 Apr 09:17
Immutable release. Only release title and notes can be modified.
b3647ab

Choose a tag to compare

Fixed multiline handling inside code blocks when comments are disabled.

v1.0.13

09 Apr 16:03
Immutable release. Only release title and notes can be modified.
d8362cb

Choose a tag to compare

Features:

  • Changed coninhcount i16 -> i32 to support PG14+.
  • Added PostgreSQL 14–18 support:
    • Virtual generated columns (GENERATED ALWAYS AS (...) VIRTUAL).
    • NOT ENFORCED / ENFORCED constraints with ALTER support.
    • Temporal primary key and unique constraints (WITHOUT OVERLAPS).
    • Exclusion constraints (contype 'x') support.
    • Named NOT NULL constraints (PG18 contype 'n').
    • NULLS NOT DISTINCT for unique constraints and indexes.
    • NO INHERIT constraint flag with ALTER support.
    • Table access method (USING) with ALTER TABLE SET ACCESS METHOD.
    • Column STORAGE and COMPRESSION settings with ALTER support.
    • SECURITY INVOKER views with ALTER SET/RESET support.
    • Unlogged sequences (CREATE UNLOGGED SEQUENCE) with ALTER support.
    • MAINTAIN privilege for table ACLs.
    • Range and multirange type support (CREATE TYPE AS RANGE), including
      pg_range metadata fetch (subtype, collation, opclass, canonical,
      subdiff, multirange name) with PG13/PG14 compatibility.
  • New object types:
    • Foreign tables: full support for CREATE FOREIGN TABLE, ALTER,
      DROP, column management, table/column options, and comparison.
    • Extended statistics: CREATE STATISTICS, ALTER, DROP, and
      comparison via pg_statistic_ext.

v1.0.12

08 Apr 18:32
Immutable release. Only release title and notes can be modified.
1e7d8f4

Choose a tag to compare

Features:

  • Fixed splitting arguments issue.
  • Flag use_drop / --use-drop currently affects all DROP statements; when disabled, DROP statements are commented out instead of executed.
  • coninhcount i16 -> i32.
  • Fixed altering sequences with values.

v1.0.11

07 Apr 10:18
Immutable release. Only release title and notes can be modified.
66ce523

Choose a tag to compare

Features:

  • Added --max-connections argument to set up a proper amount of connections.
  • Added string_extensions - to format statements in resulting diff file.
  • Fixed --use-comments argument.
  • Fixed CRLF endings.
  • Fix: new partition children created after recreated sub-partitioned parent.
  • Added clear command to produce sql files for removing objects.
  • Support --use-cascade argument for clear command.

v1.0.10

02 Apr 19:44
Immutable release. Only release title and notes can be modified.
fada173

Choose a tag to compare

Features:

  • Explicit begin...commit in resulting diff file.
  • Explicit quoting has been replaced with quote_ident
  • Support schema selection using SIMILAR TO instead of LIKE.
  • Added --use-comments parameter with default value true.
  • Fixed DDL inheritance logic.
  • Fixed incorrect detection of partition key column during type change.
  • Support GRANTS management with different behaviors.
  • Improved speed of execution.
  • Added execution time to output in seconds.
  • Fixed comparison of overloaded routines by matching functions/procedures using their argument signatures.