Skip to content

Commit 3810b13

Browse files
Extend pretty formatting to SELECT, constraints, and CREATE POLICY statements
- Add pretty formatting for SELECT statements with proper clause indentation - Add pretty formatting for constraint statements with multi-line support - Add pretty formatting for CREATE POLICY statements with clause formatting - Create comprehensive test fixtures for all new statement types - Add snapshot tests for SELECT, constraints, and CREATE POLICY formatting - Maintain backward compatibility with single-line formatting when pretty=false - All tests passing with proper snapshot validation Co-Authored-By: Dan Lynch <[email protected]>
1 parent 9444414 commit 3810b13

File tree

10 files changed

+545
-43
lines changed

10 files changed

+545
-43
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
CREATE TABLE orders (
2+
id SERIAL PRIMARY KEY,
3+
user_id INTEGER NOT NULL,
4+
total DECIMAL(10,2) CHECK (total > 0),
5+
status VARCHAR(20) DEFAULT 'pending',
6+
created_at TIMESTAMP DEFAULT now(),
7+
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
8+
CONSTRAINT unique_user_date UNIQUE (user_id, created_at),
9+
CONSTRAINT check_status CHECK (status IN ('pending', 'completed', 'cancelled'))
10+
);
11+
12+
ALTER TABLE products ADD CONSTRAINT fk_category
13+
FOREIGN KEY (category_id)
14+
REFERENCES categories(id)
15+
ON UPDATE CASCADE
16+
ON DELETE SET NULL
17+
DEFERRABLE INITIALLY DEFERRED;
18+
19+
ALTER TABLE products ADD CONSTRAINT check_price CHECK (price > 0);
20+
21+
ALTER TABLE users ADD CONSTRAINT unique_email UNIQUE (email);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
CREATE POLICY user_policy ON users FOR ALL TO authenticated_users USING (user_id = current_user_id());
2+
3+
CREATE POLICY admin_policy ON sensitive_data
4+
AS RESTRICTIVE
5+
FOR SELECT
6+
TO admin_role
7+
USING (department = current_user_department())
8+
WITH CHECK (approved = true);
9+
10+
CREATE POLICY complex_policy ON documents
11+
FOR UPDATE
12+
TO document_editors
13+
USING (
14+
owner_id = current_user_id() OR
15+
(shared = true AND permissions @> '{"edit": true}')
16+
)
17+
WITH CHECK (
18+
status != 'archived' AND
19+
last_modified > now() - interval '1 day'
20+
);
21+
22+
CREATE POLICY simple_policy ON posts FOR SELECT TO public USING (published = true);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
SELECT id, name, email FROM users WHERE active = true;
2+
3+
SELECT
4+
u.id,
5+
u.name,
6+
u.email,
7+
p.title as profile_title
8+
FROM users u
9+
JOIN profiles p ON u.id = p.user_id
10+
WHERE u.active = true
11+
AND u.created_at > '2023-01-01'
12+
GROUP BY u.id, u.name, u.email, p.title
13+
HAVING COUNT(*) > 1
14+
ORDER BY u.created_at DESC, u.name ASC
15+
LIMIT 10
16+
OFFSET 5;
17+
18+
SELECT id, name FROM users WHERE id IN (
19+
SELECT user_id FROM orders WHERE total > 100
20+
);
21+
22+
SELECT name FROM customers
23+
UNION ALL
24+
SELECT name FROM suppliers
25+
ORDER BY name;
26+
27+
SELECT name, email FROM users WHERE status = 'active';
28+
29+
SELECT u.name, o.total FROM users u, orders o WHERE u.id = o.user_id;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Pretty constraint formatting should format check constraint with pretty option enabled 1`] = `"ALTER TABLE products ADD CONSTRAINT check_price CHECK (price > 0);"`;
4+
5+
exports[`Pretty constraint formatting should format complex table with constraints with pretty option enabled 1`] = `
6+
"CREATE TABLE orders (
7+
id serial PRIMARY KEY,
8+
user_id int NOT NULL,
9+
total numeric(10, 2) CHECK (total > 0),
10+
status varchar(20) DEFAULT 'pending',
11+
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id)
12+
ON DELETE CASCADE
13+
);"
14+
`;
15+
16+
exports[`Pretty constraint formatting should format foreign key constraint with pretty option enabled 1`] = `
17+
"ALTER TABLE products ADD CONSTRAINT fk_category FOREIGN KEY (category_id) REFERENCES categories (id)
18+
ON UPDATE CASCADE
19+
ON DELETE SET NULL
20+
DEFERRABLE
21+
INITIALLY DEFERRED;"
22+
`;
23+
24+
exports[`Pretty constraint formatting should maintain single-line format for complex table when pretty disabled 1`] = `"CREATE TABLE orders (id serial PRIMARY KEY, user_id int NOT NULL, total numeric(10, 2) CHECK (total > 0), status varchar(20) DEFAULT 'pending', CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE);"`;
25+
26+
exports[`Pretty constraint formatting should maintain single-line format when pretty option disabled 1`] = `"ALTER TABLE products ADD CONSTRAINT fk_category FOREIGN KEY (category_id) REFERENCES categories (id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;"`;
27+
28+
exports[`Pretty constraint formatting should use custom newline and tab characters in pretty mode 1`] = `
29+
"ALTER TABLE products ADD CONSTRAINT fk_category FOREIGN KEY (category_id) REFERENCES categories (id)
30+
ON UPDATE CASCADE
31+
ON DELETE SET NULL
32+
DEFERRABLE
33+
INITIALLY DEFERRED;"
34+
`;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Pretty CREATE POLICY formatting should format basic CREATE POLICY with pretty option enabled 1`] = `
4+
"CREATEPOLICY"user_policy"ONusers
5+
AS PERMISSIVE
6+
FOR ALL
7+
TO authenticated_users
8+
USING (user_id = current_user_id());"
9+
`;
10+
11+
exports[`Pretty CREATE POLICY formatting should format complex CREATE POLICY with pretty option enabled 1`] = `
12+
"CREATEPOLICY"admin_policy"ONsensitive_data
13+
AS RESTRICTIVE
14+
FOR SELECT
15+
TO admin_role
16+
USING (department = current_user_department())
17+
WITH CHECK (approved = true);"
18+
`;
19+
20+
exports[`Pretty CREATE POLICY formatting should format simple CREATE POLICY with pretty option enabled 1`] = `
21+
"CREATEPOLICY"simple_policy"ONposts
22+
AS PERMISSIVE
23+
FOR SELECT
24+
TO public
25+
USING (published = true);"
26+
`;
27+
28+
exports[`Pretty CREATE POLICY formatting should maintain single-line format for complex policy when pretty disabled 1`] = `"CREATE POLICY "admin_policy" ON sensitive_data AS RESTRICTIVE FOR SELECT TO admin_role USING (department = current_user_department()) WITH CHECK (approved = true);"`;
29+
30+
exports[`Pretty CREATE POLICY formatting should maintain single-line format when pretty option disabled 1`] = `"CREATE POLICY "user_policy" ON users AS PERMISSIVE FOR ALL TO authenticated_users USING (user_id = current_user_id());"`;
31+
32+
exports[`Pretty CREATE POLICY formatting should use custom newline and tab characters in pretty mode 1`] = `
33+
"CREATEPOLICY"user_policy"ONusers
34+
AS PERMISSIVE
35+
FOR ALL
36+
TO authenticated_users
37+
USING (user_id = current_user_id());"
38+
`;
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Pretty SELECT formatting should format SELECT with UNION with pretty option enabled 1`] = `
4+
"SELECT
5+
name
6+
FROM
7+
customers
8+
UNION
9+
ALL
10+
SELECT
11+
name
12+
FROM
13+
suppliers
14+
ORDER BY
15+
name;"
16+
`;
17+
18+
exports[`Pretty SELECT formatting should format SELECT with subquery with pretty option enabled 1`] = `
19+
"SELECT
20+
21+
id,
22+
name
23+
FROM
24+
users
25+
WHERE
26+
id IN (SELECT
27+
user_id
28+
FROM
29+
orders
30+
WHERE
31+
total > 100);"
32+
`;
33+
34+
exports[`Pretty SELECT formatting should format basic SELECT with pretty option enabled 1`] = `
35+
"SELECT
36+
37+
id,
38+
name,
39+
email
40+
FROM
41+
users
42+
WHERE
43+
active = true;"
44+
`;
45+
46+
exports[`Pretty SELECT formatting should format complex SELECT with pretty option enabled 1`] = `
47+
"SELECT
48+
49+
u.id,
50+
u.name,
51+
u.email,
52+
p.title
53+
FROM
54+
users AS u JOIN profiles AS p ON u.id = p.user_id
55+
WHERE
56+
u.active = true AND u.created_at > '2023-01-01'
57+
GROUP BY
58+
59+
u.id,
60+
u.name,
61+
u.email,
62+
p.title
63+
HAVING
64+
count(*) > 1
65+
ORDER BY
66+
67+
u.created_at DESC,
68+
u.name ASC
69+
LIMIT
70+
10
71+
OFFSET
72+
5;"
73+
`;
74+
75+
exports[`Pretty SELECT formatting should maintain single-line format for complex SELECT when pretty disabled 1`] = `"SELECT u.id, u.name, u.email, p.title FROM users AS u JOIN profiles AS p ON u.id = p.user_id WHERE u.active = true AND u.created_at > '2023-01-01' GROUP BY u.id, u.name, u.email, p.title HAVING count(*) > 1 ORDER BY u.created_at DESC, u.name ASC LIMIT 10 OFFSET 5;"`;
76+
77+
exports[`Pretty SELECT formatting should maintain single-line format when pretty option disabled 1`] = `"SELECT id, name, email FROM users WHERE active = true;"`;
78+
79+
exports[`Pretty SELECT formatting should use custom newline and tab characters in pretty mode 1`] = `
80+
"SELECT
81+
82+
id,
83+
name,
84+
email
85+
FROM
86+
users
87+
WHERE
88+
active = true;"
89+
`;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { deparseSync } from '../../src';
2+
import { parse } from 'libpg-query';
3+
4+
describe('Pretty constraint formatting', () => {
5+
const foreignKeyConstraintSql = `ALTER TABLE products ADD CONSTRAINT fk_category FOREIGN KEY (category_id) REFERENCES categories(id) ON UPDATE CASCADE ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;`;
6+
7+
const checkConstraintSql = `ALTER TABLE products ADD CONSTRAINT check_price CHECK (price > 0);`;
8+
9+
const complexTableSql = `CREATE TABLE orders (
10+
id SERIAL PRIMARY KEY,
11+
user_id INTEGER NOT NULL,
12+
total DECIMAL(10,2) CHECK (total > 0),
13+
status VARCHAR(20) DEFAULT 'pending',
14+
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
15+
);`;
16+
17+
it('should format foreign key constraint with pretty option enabled', async () => {
18+
const parsed = await parse(foreignKeyConstraintSql);
19+
const result = deparseSync(parsed, { pretty: true });
20+
expect(result).toMatchSnapshot();
21+
});
22+
23+
it('should maintain single-line format when pretty option disabled', async () => {
24+
const parsed = await parse(foreignKeyConstraintSql);
25+
const result = deparseSync(parsed, { pretty: false });
26+
expect(result).toMatchSnapshot();
27+
});
28+
29+
it('should format check constraint with pretty option enabled', async () => {
30+
const parsed = await parse(checkConstraintSql);
31+
const result = deparseSync(parsed, { pretty: true });
32+
expect(result).toMatchSnapshot();
33+
});
34+
35+
it('should format complex table with constraints with pretty option enabled', async () => {
36+
const parsed = await parse(complexTableSql);
37+
const result = deparseSync(parsed, { pretty: true });
38+
expect(result).toMatchSnapshot();
39+
});
40+
41+
it('should maintain single-line format for complex table when pretty disabled', async () => {
42+
const parsed = await parse(complexTableSql);
43+
const result = deparseSync(parsed, { pretty: false });
44+
expect(result).toMatchSnapshot();
45+
});
46+
47+
it('should use custom newline and tab characters in pretty mode', async () => {
48+
const parsed = await parse(foreignKeyConstraintSql);
49+
const result = deparseSync(parsed, {
50+
pretty: true,
51+
newline: '\r\n',
52+
tab: ' '
53+
});
54+
expect(result).toMatchSnapshot();
55+
});
56+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { deparseSync } from '../../src';
2+
import { parse } from 'libpg-query';
3+
4+
describe('Pretty CREATE POLICY formatting', () => {
5+
const basicPolicySql = `CREATE POLICY user_policy ON users FOR ALL TO authenticated_users USING (user_id = current_user_id());`;
6+
7+
const complexPolicySql = `CREATE POLICY admin_policy ON sensitive_data AS RESTRICTIVE FOR SELECT TO admin_role USING (department = current_user_department()) WITH CHECK (approved = true);`;
8+
9+
const simplePolicySql = `CREATE POLICY simple_policy ON posts FOR SELECT TO public USING (published = true);`;
10+
11+
it('should format basic CREATE POLICY with pretty option enabled', async () => {
12+
const parsed = await parse(basicPolicySql);
13+
const result = deparseSync(parsed, { pretty: true });
14+
expect(result).toMatchSnapshot();
15+
});
16+
17+
it('should maintain single-line format when pretty option disabled', async () => {
18+
const parsed = await parse(basicPolicySql);
19+
const result = deparseSync(parsed, { pretty: false });
20+
expect(result).toMatchSnapshot();
21+
});
22+
23+
it('should format complex CREATE POLICY with pretty option enabled', async () => {
24+
const parsed = await parse(complexPolicySql);
25+
const result = deparseSync(parsed, { pretty: true });
26+
expect(result).toMatchSnapshot();
27+
});
28+
29+
it('should maintain single-line format for complex policy when pretty disabled', async () => {
30+
const parsed = await parse(complexPolicySql);
31+
const result = deparseSync(parsed, { pretty: false });
32+
expect(result).toMatchSnapshot();
33+
});
34+
35+
it('should format simple CREATE POLICY with pretty option enabled', async () => {
36+
const parsed = await parse(simplePolicySql);
37+
const result = deparseSync(parsed, { pretty: true });
38+
expect(result).toMatchSnapshot();
39+
});
40+
41+
it('should use custom newline and tab characters in pretty mode', async () => {
42+
const parsed = await parse(basicPolicySql);
43+
const result = deparseSync(parsed, {
44+
pretty: true,
45+
newline: '\r\n',
46+
tab: ' '
47+
});
48+
expect(result).toMatchSnapshot();
49+
});
50+
});

0 commit comments

Comments
 (0)