Releases: nettrash/pgc
Releases · nettrash/pgc
v1.0.19
Immutable
release. Only release title and notes can be modified.
Bug fixes:
- Refactored comment-related query construction into dedicated
helper methods onDump:
build_tables_standalone_query,
build_regular_views_query,
build_materialized_views_query,
build_view_column_comments_query,
build_sequences_standalone_query,
and onTable:build_indexes_bulk_query. - Kept
pg_descriptionjoins scoped to relation comments by
filtering onpg_description.classoid = 'pg_class'::regclass
andpg_description.objsubid = 0to avoid catalog
collisions. - Normalized SQL formatting in the new helper query strings.
Comparer::compare_grantsno longer runs a duplicate
iter().find(...)overfrom_colsfor 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_columnsnow keys
serial_columnsby(schema, table, column)tuple
instead of aformat!("{}.{}.{}", ...)string that
was parsed back viasplitn(3, '.'). The string
form silently misparsed any identifier containing a
literal.(legal in PostgreSQL when quoted),
leaving the corresponding column unmarked.Comparer::compare_sequenceshad a dead
dropped_sequences: HashSet<String>that was
checked and updated on every iteration of a loop
overself.from.sequences. Sequences are unique by
(schema, name)so the dedupe could never fire;
the set and its per-iterationformat!/contains/
insertcalls are removed.Comparer::compare_routinesand
Comparer::compare_routines_and_viewsno longer
clone everyRoutine(each carries the full
source_codestring). 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_routinetook&mut selfand so
conflicted with any borrow intoself.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.scriptto coexist
with&Routineborrows out of the dump fields,
removing the per-emit clones. Same pattern as the
pre-existingSelf::emit_drop.
Performance:
Dump::processno longer materializes the entire
serialized dump as aStringbefore handing it to
the zip writer. The JSON payload is now streamed
intoZipWriterviaserde_json::to_writer
through a 256 KiBBufWriter, bounding peak
memory at the buffer plus zlib's internal state
rather than ~2x the uncompressed dump size. The
BufWriteris 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 asDump::write_to_file,
mirroring the existingDump::read_from_fileand
making the round-trip directly testable.
Tests:
- Added regression tests proving every new query
builder includes thepg_classclassoid 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,
andbuild_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_acldiffers (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 themark_serial_columnspath 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
Immutable
release. Only release title and notes can be modified.
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 thetoowner —
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
asymmetricfrom_owners/to_ownerslists that
model the effect ofALTER ... OWNER TO: the FROM
owner's implicit-owner row is stripped from the
FROM side (becauseALTER OWNERremoves it) and
the TO owner's implicit row is stripped from the
TO side (becauseALTER OWNERrecreates 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 emptyfrom_ownersbecauseALTER TABLE OWNER
does not touch column-level grants — explicit
column grants to a former owner persist and must
still be revoked underfullmode. FOREIGN TABLEACL parsing now uses the same
privilege set asTABLE(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 usesGRANT ... ON TABLErather
than the invalidGRANT ... ON FOREIGN TABLE
syntax (PostgreSQL has noON FOREIGN TABLEform
for GRANT/REVOKE — applying the previous diff
raisedsyntax 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 effectivefrom_acl, a
second compare run after applying the migration
emitted spuriousREVOKEstatements for those
auto-applied privileges. The foreign-table grant
path now mirrors the regular-table path: under
fullmode, new foreign tables use the FROM
default-table ACL as the effectivefrom_aclso
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 viaquote_identliterally 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=falseis 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
underuse_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 ofDump::filland
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::loadnow warns whenFROM_HOSTor
TO_HOSTis 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_schemascollapsed anif let Errfollowed 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_ownerssemantics are documented on
generate_column_grants_script(extending the
doc already added todiff_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
fullmode, 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
Immutable
release. Only release title and notes can be modified.
Bug fixes:
- PostgreSQL version agnostic handling of attstattarget
attribute. - Replaced double
.unwrap()in the dump serialization
path with proper error propagation so aserde_json
failure surfaces as a readable error instead of a
panic. compare_typesno longer emits a stray
-- Multirange … is created automaticallycomment for
new range types. PostgreSQL auto-creates the
companion multirange row whenCREATE 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_scriptand 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_COUNTconstant with a
compile-time arity guard against the outertry_join!
inDump::fill, plus a runtime warning when
--max-connections/MAX_CONNECTIONSis below the
number of parallel dump branches (pool starvation
would otherwise go unnoticed). Config::loadnow warns whenFROM_*andTO_*
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 fromARG PGC_VERSIONwired
through GitHub Actions fromapp/Cargo.toml— no
more hard-coded version skew on release.
UX / Docs:
- Fixed copy-pasted
--serverhelp text on--port
and--scheme; documented theSIMILAR TOmatching
behaviour of--scheme(e.g.public|app) and
expanded--grants-modeto describe ignore /
addonly / full explicitly. - Added explanatory comments at every
recreated_tables.insert()site documenting the
coupling withcompare_grantsdefault-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
Immutable
release. Only release title and notes can be modified.
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
Immutable
release. Only release title and notes can be modified.
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
v1.0.13
Immutable
release. Only release title and notes can be modified.
Features:
- Changed
coninhcounti16 -> i32 to support PG14+. - Added PostgreSQL 14–18 support:
- Virtual generated columns (
GENERATED ALWAYS AS (...) VIRTUAL). NOT ENFORCED/ENFORCEDconstraints 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 DISTINCTfor unique constraints and indexes.NO INHERITconstraint flag with ALTER support.- Table access method (
USING) withALTER TABLE SET ACCESS METHOD. - Column
STORAGEandCOMPRESSIONsettings with ALTER support. SECURITY INVOKERviews with ALTER SET/RESET support.- Unlogged sequences (
CREATE UNLOGGED SEQUENCE) with ALTER support. MAINTAINprivilege for table ACLs.- Range and multirange type support (
CREATE TYPE AS RANGE), including
pg_rangemetadata fetch (subtype, collation, opclass, canonical,
subdiff, multirange name) with PG13/PG14 compatibility.
- Virtual generated columns (
- 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 viapg_statistic_ext.
- Foreign tables: full support for
v1.0.12
Immutable
release. Only release title and notes can be modified.
Features:
- Fixed splitting arguments issue.
- Flag
use_drop/--use-dropcurrently affects all DROP statements; when disabled, DROP statements are commented out instead of executed. coninhcounti16 -> i32.- Fixed altering sequences with values.
v1.0.11
Immutable
release. Only release title and notes can be modified.
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
Immutable
release. Only release title and notes can be modified.
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.