Skip to content

Commit 90078fe

Browse files
authored
Merge pull request #4 from constructive-io/feat/cli-5.20
feat: pgpm 5.20 — linear SQL delta projection (--emit-sql) + acceptance lane
2 parents 0e3bf32 + d262bbf commit 90078fe

5 files changed

Lines changed: 95 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ concurrency:
1111
cancel-in-progress: true
1212

1313
env:
14-
PGPM_VERSION: 5.19.1
14+
PGPM_VERSION: 5.20.0
1515

1616
jobs:
1717
acceptance:

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,18 @@ deploys on top of `shop-app`.
103103

104104
```sh
105105
pgpm import sources/shop.v2.sql --pkg shop-v2 --out /tmp/v2mod
106-
pgpm diff packages/shop /tmp/v2mod/shop-v2 --emit-migration packages --pkg shop-v1-to-v2 --verify
106+
pgpm diff packages/shop /tmp/v2mod/shop-v2 --emit-migration packages --pkg shop-v1-to-v2 \
107+
--emit-sql sources/shop.v1-to-v2.sql --verify
107108
```
108109

110+
The delta is one model and every `--emit-*` flag is a projection of it: the
111+
pgpm module lands in `packages/shop-v1-to-v2`, and the same delta deparsed as
112+
a single linear SQL script lands in
113+
[`sources/shop.v1-to-v2.sql`](sources/shop.v1-to-v2.sql)
114+
(`--emit-bundle` would additionally produce a content-addressed
115+
`.bundle.tar.gz`). `pgpm diff --append-module <dir>` can instead append the
116+
delta as new changes into an existing module.
117+
109118
Identity-keyed semantic diff between the v1 module and the v2 schema. Tables
110119
are compared column-by-column and constraint-by-constraint, so the delta is
111120
`ALTER TABLE`, not a rebuild. The delta is emitted as a normal pgpm module you
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# shop-v1-to-v2 extension
2-
comment = 'shop-v1-to-v2 migration (generated by pgpm diff)'
2+
comment = 'shop-v1-to-v2 extension'
33
default_version = '0.0.1'
44
relocatable = false
55
superuser = false

scripts/acceptance.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ if [ -d packages/shop-v1-to-v2 ] && [ -d packages/shop ]; then
169169
$PSQL -d shop_v2_fresh -f sources/shop.v2.sql
170170

171171
assert_same_catalog shop_migrated shop_v2_fresh "v1 + migration vs v2 fresh"
172+
173+
# the --emit-sql projection of the same delta, applied as plain SQL
174+
if [ -f sources/shop.v1-to-v2.sql ]; then
175+
note "load v1 dump + linear delta SQL -> shop_sql_migrated"
176+
$PSQL -d postgres -c 'CREATE DATABASE shop_sql_migrated'
177+
$PSQL -d shop_sql_migrated -v ON_ERROR_STOP=1 -f sources/shop.v1.sql
178+
$PSQL -d shop_sql_migrated -v ON_ERROR_STOP=1 -f sources/shop.v1-to-v2.sql
179+
assert_same_catalog shop_sql_migrated shop_v2_fresh "linear SQL delta vs v2 fresh"
180+
else
181+
skip "linear SQL delta check (sources/shop.v1-to-v2.sql)"
182+
fi
172183
else
173184
skip "v1 -> v2 migration check (packages/shop-v1-to-v2)"
174185
fi

sources/shop.v1-to-v2.sql

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
DROP INDEX shop.idx_orders_placed_at;
2+
3+
CREATE TABLE shop.product_reviews (
4+
id uuid DEFAULT gen_random_uuid() NOT NULL,
5+
product_id uuid NOT NULL,
6+
customer_id uuid NOT NULL,
7+
rating int NOT NULL,
8+
body text,
9+
created_at timestamptz DEFAULT now() NOT NULL,
10+
CONSTRAINT product_reviews_pkey PRIMARY KEY (id),
11+
CONSTRAINT product_reviews_rating_check
12+
CHECK (
13+
rating >= 1
14+
AND rating <= 5
15+
)
16+
);
17+
18+
GRANT SELECT, INSERT ON shop.product_reviews TO app_user;
19+
20+
ALTER TABLE ONLY shop.product_reviews
21+
ADD CONSTRAINT product_reviews_product_id_fkey
22+
FOREIGN KEY(product_id)
23+
REFERENCES shop.products (id)
24+
ON DELETE CASCADE;
25+
26+
ALTER TABLE ONLY shop.product_reviews
27+
ADD CONSTRAINT product_reviews_customer_id_fkey
28+
FOREIGN KEY(customer_id)
29+
REFERENCES shop.customers (id);
30+
31+
COMMENT ON TABLE shop.product_reviews IS 'Customer product reviews, 1-5 stars.';
32+
33+
CREATE INDEX idx_product_reviews_product_id ON shop.product_reviews (product_id);
34+
35+
CREATE POLICY orders_insert_own
36+
ON shop.orders
37+
AS PERMISSIVE
38+
FOR INSERT
39+
TO PUBLIC
40+
WITH CHECK (
41+
customer_id = (current_setting('app.current_customer_id', true))::uuid
42+
);
43+
44+
ALTER TABLE shop.customers
45+
ADD COLUMN marketing_opt_in boolean
46+
DEFAULT false
47+
NOT NULL;
48+
49+
ALTER TABLE shop.customers
50+
DROP COLUMN phone RESTRICT;
51+
52+
ALTER TABLE shop.orders
53+
DROP CONSTRAINT orders_status_check RESTRICT;
54+
55+
ALTER TABLE shop.orders
56+
ADD CONSTRAINT orders_status_check
57+
CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled', 'refunded'));
58+
59+
COMMENT ON FUNCTION shop.order_total(p_order_id uuid) IS NULL;
60+
61+
DROP FUNCTION shop.order_total(uuid);
62+
63+
CREATE FUNCTION shop.order_total(
64+
p_order_id uuid
65+
) RETURNS int LANGUAGE sql STABLE AS $EOFCODE$
66+
SELECT COALESCE(SUM(quantity * unit_price_cents), 0)::integer
67+
FROM shop.order_items
68+
WHERE order_id = p_order_id
69+
AND quantity > 0;
70+
$EOFCODE$;
71+
72+
COMMENT ON FUNCTION shop.order_total(p_order_id uuid) IS 'Sum of line totals for an order, in cents.';

0 commit comments

Comments
 (0)