Skip to content

Commit bf3c77d

Browse files
committed
refactor(plpgsql-deparser): remove console.log and add snapshot for hydrate-demo
- Remove all console.log statements from hydrate-demo test - Add snapshot test for modified big-function.sql output - Verify exact hydration stats (68 total, 20 assignments, 48 SQL expressions)
1 parent b530f17 commit bf3c77d

File tree

2 files changed

+179
-44
lines changed

2 files changed

+179
-44
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`hydrate demonstration with big-function.sql should parse, hydrate, modify, and deparse big-function.sql 1`] = `
4+
"DECLARE
5+
v_orders_scanned int := 888;
6+
v_orders_upserted int := 888;
7+
v_gross numeric := 888;
8+
v_discount numeric := 888;
9+
v_tax numeric := 888;
10+
v_net numeric := 888;
11+
v_avg numeric := 888;
12+
v_top_sku text := NULL;
13+
v_top_sku_qty bigint := 888;
14+
v_now timestamptz := clock_timestamp();
15+
v_jitter numeric := (random() - 0.5) * 0.02;
16+
v_discount_rate numeric := GREATEST(LEAST(p_discount_rate, 0.50), 0);
17+
v_tax_rate numeric := GREATEST(LEAST(p_tax_rate, 0.30), 0);
18+
v_min_total numeric := COALESCE(p_min_total, 0);
19+
v_sql text;
20+
v_rowcount int := 0;
21+
v_lock_key bigint := ('x' || substr(md5(p_org_id::text), 1, 16))::bit(64)::bigint;
22+
sqlstate CONSTANT text;
23+
sqlerrm CONSTANT text;
24+
BEGIN
25+
BEGIN
26+
IF p_org_id IS NULL OR p_user_id IS NULL THEN
27+
RAISE EXCEPTION 'p_org_id and p_user_id are required';
28+
END IF;
29+
IF p_from_ts > p_to_ts THEN
30+
RAISE EXCEPTION 'p_from_ts (%) must be <= p_to_ts (%)', p_from_ts, p_to_ts;
31+
END IF;
32+
IF p_max_rows < 1 OR p_max_rows > 10000 THEN
33+
RAISE EXCEPTION 'p_max_rows out of range: %', p_max_rows;
34+
END IF;
35+
IF p_round_to < 0 OR p_round_to > 6 THEN
36+
RAISE EXCEPTION 'p_round_to out of range: %', p_round_to;
37+
END IF;
38+
IF p_lock THEN
39+
PERFORM SELECT pg_advisory_xact_lock(v_lock_key);
40+
END IF;
41+
IF p_debug THEN
42+
RAISE NOTICE 'big_kitchen_sink start=% org=% user=% from=% to=% min_total=%', v_now, p_org_id, p_user_id, p_from_ts, p_to_ts, v_min_total;
43+
END IF;
44+
WITH base AS (
45+
SELECT
46+
o.id,
47+
o.total_amount::numeric AS total_amount,
48+
o.currency,
49+
o.created_at
50+
FROM app_public.app_order o
51+
WHERE o.org_id = p_org_id
52+
AND o.user_id = p_user_id
53+
AND o.created_at >= p_from_ts
54+
AND o.created_at < p_to_ts
55+
AND o.total_amount::numeric >= v_min_total
56+
AND o.currency = p_currency
57+
ORDER BY o.created_at DESC
58+
LIMIT p_max_rows
59+
),
60+
totals AS (
61+
SELECT
62+
count(*)::int AS orders_scanned,
63+
COALESCE(sum(total_amount), 0) AS gross_total,
64+
COALESCE(avg(total_amount), 0) AS avg_total
65+
FROM base
66+
)
67+
SELECT
68+
t.orders_scanned,
69+
t.gross_total,
70+
t.avg_total
71+
FROM totals t;
72+
IF p_apply_discount THEN
73+
v_discount_MODIFIED := round(v_gross * GREATEST(LEAST(v_discount_rate + v_jitter, 0.50), 0), p_round_to);
74+
ELSE
75+
v_discount := 0;
76+
END IF;
77+
v_tax_MODIFIED := round(GREATEST(v_gross - v_discount, 0) * v_tax_rate, p_round_to);
78+
v_net := round((v_gross - v_discount + v_tax) * power(10::numeric, 0), p_round_to);
79+
SELECT
80+
oi.sku,
81+
sum(oi.quantity)::bigint AS qty
82+
FROM app_public.order_item oi
83+
JOIN app_public.app_order o ON o.id = oi.order_id
84+
WHERE o.org_id = p_org_id
85+
AND o.user_id = p_user_id
86+
AND o.created_at >= p_from_ts
87+
AND o.created_at < p_to_ts
88+
AND o.currency = p_currency
89+
GROUP BY oi.sku
90+
ORDER BY qty DESC, oi.sku ASC
91+
LIMIT 1;
92+
INSERT INTO app_public.order_rollup (
93+
org_id,
94+
user_id,
95+
period_from,
96+
period_to,
97+
currency,
98+
orders_scanned,
99+
gross_total,
100+
discount_total,
101+
tax_total,
102+
net_total,
103+
avg_order_total,
104+
top_sku,
105+
top_sku_qty,
106+
note,
107+
updated_at
108+
)
109+
VALUES (
110+
p_org_id,
111+
p_user_id,
112+
p_from_ts,
113+
p_to_ts,
114+
p_currency,
115+
v_orders_scanned,
116+
v_gross,
117+
v_discount,
118+
v_tax,
119+
v_net,
120+
v_avg,
121+
v_top_sku,
122+
v_top_sku_qty,
123+
p_note,
124+
now()
125+
)
126+
ON CONFLICT (org_id, user_id, period_from, period_to, currency)
127+
DO UPDATE SET
128+
orders_scanned = EXCLUDED.orders_scanned,
129+
gross_total = EXCLUDED.gross_total,
130+
discount_total = EXCLUDED.discount_total,
131+
tax_total = EXCLUDED.tax_total,
132+
net_total = EXCLUDED.net_total,
133+
avg_order_total = EXCLUDED.avg_order_total,
134+
top_sku = EXCLUDED.top_sku,
135+
top_sku_qty = EXCLUDED.top_sku_qty,
136+
note = COALESCE(EXCLUDED.note, app_public.order_rollup.note),
137+
updated_at = now();
138+
GET DIAGNOSTICS v_rowcount = ;
139+
v_orders_upserted := v_rowcount;
140+
v_sql := format(
141+
'SELECT count(*)::int FROM %I.%I WHERE org_id = $1 AND created_at >= $2 AND created_at < $3',
142+
'app_public',
143+
'app_order'
144+
);
145+
EXECUTE v_sql INTO (unnamed row) USING p_org_id, p_from_ts, p_to_ts;
146+
IF p_debug THEN
147+
RAISE NOTICE 'dynamic count(app_order)=%', v_rowcount;
148+
END IF;
149+
org_id := p_org_id;
150+
user_id := p_user_id;
151+
period_from := p_from_ts;
152+
period_to := p_to_ts;
153+
orders_scanned := v_orders_scanned;
154+
orders_upserted := v_orders_upserted;
155+
gross_total := v_gross;
156+
discount_total := v_discount;
157+
tax_total := v_tax;
158+
net_total := v_net;
159+
avg_order_total := round(v_avg, p_round_to);
160+
top_sku := v_top_sku;
161+
top_sku_qty := v_top_sku_qty;
162+
message := format(
163+
'rollup ok: gross=%s discount=%s tax=%s net=%s (discount_rate=%s tax_rate=%s)',
164+
v_gross, v_discount, v_tax, v_net, v_discount_rate, v_tax_rate
165+
);
166+
RETURN NEXT;
167+
RETURN;
168+
END;
169+
RETURN;
170+
END"
171+
`;

packages/plpgsql-deparser/__tests__/hydrate-demo.test.ts

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,57 +14,26 @@ describe('hydrate demonstration with big-function.sql', () => {
1414

1515
const parsed = parsePlPgSQLSync(sql) as unknown as PLpgSQLParseResult;
1616

17-
console.log('\n=== HYDRATION STATS ===');
18-
const { ast: hydratedAst, errors, stats } = hydratePlpgsqlAst(parsed);
19-
console.log('Total expressions:', stats.totalExpressions);
20-
console.log('Parsed expressions:', stats.parsedExpressions);
21-
console.log('Assignment expressions:', stats.assignmentExpressions);
22-
console.log('SQL expressions:', stats.sqlExpressions);
23-
console.log('Failed expressions:', stats.failedExpressions);
24-
console.log('Raw expressions:', stats.rawExpressions);
17+
const { ast: hydratedAst, stats } = hydratePlpgsqlAst(parsed);
2518

26-
if (errors.length > 0) {
27-
console.log('\nErrors:', errors.slice(0, 5));
28-
}
29-
30-
console.log('\n=== SAMPLE HYDRATED EXPRESSIONS ===');
31-
const sampleExprs = collectHydratedExprs(hydratedAst, 5);
32-
sampleExprs.forEach((expr, i) => {
33-
console.log(`\n[${i + 1}] Kind: ${expr.kind}`);
34-
console.log(` Original: "${expr.original}"`);
35-
if (expr.kind === 'assign') {
36-
console.log(` Target: "${expr.target}"`);
37-
console.log(` Value: "${expr.value}"`);
38-
console.log(` Has targetExpr: ${!!expr.targetExpr}`);
39-
console.log(` Has valueExpr: ${!!expr.valueExpr}`);
40-
} else if (expr.kind === 'sql-expr') {
41-
console.log(` Has expr AST: ${!!expr.expr}`);
42-
}
43-
});
19+
expect(stats.totalExpressions).toBe(68);
20+
expect(stats.parsedExpressions).toBe(68);
21+
expect(stats.assignmentExpressions).toBe(20);
22+
expect(stats.sqlExpressions).toBe(48);
23+
expect(stats.failedExpressions).toBe(0);
24+
expect(stats.rawExpressions).toBe(0);
4425

45-
console.log('\n=== MODIFYING AST ===');
4626
const modifiedAst = modifyAst(JSON.parse(JSON.stringify(hydratedAst)));
4727

48-
console.log('\n=== DEHYDRATING MODIFIED AST ===');
4928
const dehydratedAst = dehydratePlpgsqlAst(modifiedAst);
5029

51-
console.log('\n=== DEPARSING DEHYDRATED AST ===');
5230
const deparsed = deparseSync(dehydratedAst);
5331

54-
console.log('\n=== VERIFICATION: Changes Applied ===');
5532
expect(deparsed).toContain('v_discount_MODIFIED');
5633
expect(deparsed).toContain('v_tax_MODIFIED');
5734
expect(deparsed).toContain('888');
5835

59-
console.log('Found v_discount_MODIFIED:', deparsed.includes('v_discount_MODIFIED'));
60-
console.log('Found v_tax_MODIFIED:', deparsed.includes('v_tax_MODIFIED'));
61-
console.log('Found 888 (modified default values):', deparsed.includes('888'));
62-
63-
console.log('\n=== DEPARSED OUTPUT (first 2000 chars) ===');
64-
console.log(deparsed.substring(0, 2000));
65-
66-
expect(stats.totalExpressions).toBeGreaterThan(0);
67-
expect(stats.parsedExpressions).toBeGreaterThan(0);
36+
expect(deparsed).toMatchSnapshot();
6837
});
6938
});
7039

@@ -112,27 +81,23 @@ function modifyAst(ast: any): any {
11281

11382
if (typeof query === 'object' && query.kind === 'assign') {
11483
if (query.target === 'v_discount' && assignModCount === 0) {
115-
console.log(` Modifying assignment target: "${query.target}" -> "v_discount_MODIFIED"`);
11684
query.target = 'v_discount_MODIFIED';
11785
assignModCount++;
11886
modCount++;
11987
}
12088
if (query.target === 'v_tax' && assignModCount === 1) {
121-
console.log(` Modifying assignment target: "${query.target}" -> "v_tax_MODIFIED"`);
12289
query.target = 'v_tax_MODIFIED';
12390
assignModCount++;
12491
modCount++;
12592
}
12693
if (query.value === '0' && modCount < 5) {
127-
console.log(` Modifying assignment value: "${query.value}" -> "999"`);
12894
query.value = '999';
12995
modCount++;
13096
}
13197
}
13298

13399
if (typeof query === 'object' && query.kind === 'sql-expr') {
134100
if (query.original === '0' && modCount < 8) {
135-
console.log(` Modifying sql-expr value: "${query.original}" -> "888"`);
136101
query.original = '888';
137102
modCount++;
138103
}
@@ -152,6 +117,5 @@ function modifyAst(ast: any): any {
152117
}
153118

154119
walk(ast);
155-
console.log(` Total modifications: ${modCount}`);
156120
return ast;
157121
}

0 commit comments

Comments
 (0)