Paste two pg_dump --schema-only outputs and get the added, removed and changed tables, columns, indexes, functions and policies.
To compare two Postgres schemas, paste both pg_dump --schema-only outputs, or CREATE and ALTER statements for the object types named below, and this page lists every table, column, constraint, index, view, function, trigger, policy and grant that was added, removed or changed. Each text is cut into statements by a tokenizer that knows single quotes, double-quoted names, $$ and $tag$ blocks, line comments and nested block comments, so a semicolon inside a function body never splits a statement. The statements are then applied in order and the objects that exist at the end are compared. That means a migration script works too: ADD COLUMN, DROP COLUMN, RENAME, ADD CONSTRAINT, ALTER COLUMN TYPE and DROP TABLE change the state the same way they do in Postgres. Objects are matched by name, and a function by name plus argument types, so an overload is a different function.
The page opens with two small dumps. The new one widens users.email from character varying(100) to character varying(255), adds a phone column, changes the orders foreign key from ON DELETE CASCADE to ON DELETE RESTRICT, adds the label refunded to the enum order_status, adds a column to an index, adds a condition to a function body, and enables row level security with one policy. The result is six differences: one added object (the policy) and five changed objects (the enum, the users table, the orders table, the index and the function). Two objects, the sequence and the view, are unchanged. The owner of users also differs, but OWNER TO is ignored by default, so it is not counted until you switch that option off. Each changed object shows one line per field, such as ~ column email: type: character varying(100) -> character varying(255).
The order of statements, columns and constraints is ignored, and so are upper and lower case, extra spaces, line breaks, comments, SET statements, SELECT pg_catalog.set_config, psql backslash lines and COPY data blocks. A name in double quotes equals the same name without quotes when no quotes are needed, the public. prefix is dropped, and type names are spelled out, so int4, int and integer are one type. A serial column equals the integer column, sequence and default that pg_dump writes for it. A primary key makes its columns not null. A constraint or index that is gone while one with another name and the same definition appears is listed with a possible rename note (an index is also listed as removed and added), because two snapshots cannot show whether it was a rename or a drop plus an add. A RENAME in a migration script is applied, including the column name inside the constraints and indexes that use it. OWNER TO is ignored by default; GRANT, REVOKE and COMMENT ON can be switched off. Loose expressions also drops ::type casts and the extra parentheses that pg_dump adds, so hand-written DDL such as CHECK (price > 0) matches CHECK ((price > (0)::numeric)). That option is off by default because a cast can be a real change.
This page does not connect to a database and does not write a migration script; it shows what differs, with the DDL of each side so you can write the change yourself. Column order is ignored, which matters only for code that inserts without naming columns. Function bodies in SQL and PL/pgSQL are compared as tokens; bodies in other languages are compared as written. Compared: extensions, schemas, types, domains, sequences, tables, indexes, views, materialized views, functions, procedures, triggers, policies, grants, owners and comments. Other DDL, such as statistics, collations, aggregates, rules, operators, casts, roles and publications, is only counted in a notice, not compared. One GRANT or REVOKE can name at most 2,000 object and role pairs, and a schema can hold up to 100,000 objects. Sequence current values are not schema and are ignored. A difference in a default that comes from a cast, for example 'x'::text against 'x', is reported unless Loose expressions is on. Each box takes up to 5,000,000 characters and 100,000 statements, and a large pair can take a second or two. To get a clean file from a database, pg_dump --schema-only writes the definitions without data; adding --no-owner and --no-privileges leaves out ownership and grants. An AI agent can open this page with both texts filled in: add #input= followed by the URL-encoded old schema, then &input2= and the URL-encoded new schema, to the address. Everything runs in this tab.
Run pg_dump --schema-only on each database, paste the first file into Original schema and the second into Changed schema. The page lists each object as added (+), removed (-) or changed (~), with one line per changed field, for example ~ constraint orders_user_id_fkey: on delete: cascade -> restrict.
Dumps taken by different roles contain different OWNER TO and GRANT lines. OWNER TO is ignored by default. Turn on Ignore GRANT and REVOKE to skip privileges too. Or dump with pg_dump --no-owner --no-privileges so the files never contain them.
No. Two dumps with the same tables in a different order, or the same table with columns declared in a different order, are reported as identical. Constraints, indexes and policies are matched by name, and functions by name plus argument types.
pg_dump writes CHECK ((price > (0)::numeric)) where a person writes CHECK (price > 0). The text differs by a cast and a pair of parentheses. Turn on Loose expressions to drop ::type casts and redundant parentheses before comparing; leave it off when comparing two pg_dump files.
Yes. Statements are applied in order, so ALTER TABLE ADD COLUMN, DROP COLUMN, RENAME TO, ADD CONSTRAINT and DROP TABLE give the same final state as in Postgres. Compare a migration script against a dump to check that the script produces the schema you expect.
No. The text is processed in your browser and not uploaded. Your two schemas are never saved; only the four option settings are kept on this device.