Skip to content

Commit aba85ef

Browse files
committed
feat: add pretty fixtures for quoting and formatting behaviors
Add test fixtures documenting deparser behavior for: - Quoted function names (float, interval, boolean) as reserved keywords - pg_catalog.substring with quoted identifier - EXPLAIN with optional COSTS OFF keyword - Boolean literal formatting ('t'::boolean vs TRUE) - Parenthesization/argument formatting with IN parameter mode Both input forms and canonical forms are included where applicable to test both the transformation and idempotence.
1 parent 7cbb962 commit aba85ef

File tree

7 files changed

+318
-0
lines changed

7 files changed

+318
-0
lines changed

__fixtures__/generated/generated.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@
5353
"pretty/selects-13.sql": "SELECT\n id,\n name\nFROM users\nWHERE\n id IN (SELECT\n user_id\nFROM orders\nWHERE\n total > 100)",
5454
"pretty/selects-14.sql": "SELECT\n id,\n name,\n email\nFROM users\nWHERE\n active = true",
5555
"pretty/selects-15.sql": "SELECT\n u.id,\n u.name,\n u.email,\n p.title\nFROM users AS u\nJOIN profiles AS p ON u.id = p.user_id\nWHERE\n u.active = true\n AND u.created_at > '2023-01-01'\nGROUP BY\n u.id,\n u.name,\n u.email,\n p.title\nHAVING\n count(*) > 1\nORDER BY\n u.created_at DESC,\n u.name ASC\nLIMIT 10\nOFFSET 5",
56+
"pretty/quoting-1.sql": "CREATE FUNCTION faker.float(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $EOFCODE$\nBEGIN\n RETURN min + random() * (max - min);\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
57+
"pretty/quoting-2.sql": "CREATE FUNCTION faker.\"float\"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $EOFCODE$\nBEGIN\n RETURN min + random() * (max - min);\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
58+
"pretty/quoting-3.sql": "CREATE FUNCTION faker.interval(min int, max int) RETURNS interval AS $EOFCODE$\nBEGIN\n RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
59+
"pretty/quoting-4.sql": "CREATE FUNCTION faker.\"interval\"(min int, max int) RETURNS interval AS $EOFCODE$\nBEGIN\n RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
60+
"pretty/quoting-5.sql": "CREATE FUNCTION faker.boolean() RETURNS boolean AS $EOFCODE$\nBEGIN\n RETURN random() < 0.5;\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
61+
"pretty/quoting-6.sql": "CREATE FUNCTION faker.\"boolean\"() RETURNS boolean AS $EOFCODE$\nBEGIN\n RETURN random() < 0.5;\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
62+
"pretty/quoting-7.sql": "CREATE DOMAIN origin AS text CHECK (value = pg_catalog.\"substring\"(value, '^(https?://[^/]*)'))",
5663
"pretty/procedures-1.sql": "SELECT handle_insert('TYPE_A')",
5764
"pretty/procedures-2.sql": "SELECT \"HandleInsert\"('TYPE_A', 'Region-1')",
5865
"pretty/procedures-3.sql": "SELECT compute_score(42, TRUE)",
@@ -80,6 +87,15 @@
8087
"pretty/misc-15.sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA dashboard_jobs \n GRANT EXECUTE ON FUNCTIONS TO administrator",
8188
"pretty/misc-16.sql": "GRANT EXECUTE ON FUNCTION dashboard_private.uuid_generate_seeded_uuid TO PUBLIC",
8289
"pretty/misc-17.sql": "SELECT CAST(t.date AT TIME ZONE $$America/New_York$$ AS text)::date FROM tbl t",
90+
"pretty/formatting-1.sql": "EXPLAIN (COSTS OFF) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'",
91+
"pretty/formatting-2.sql": "EXPLAIN SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'",
92+
"pretty/formatting-3.sql": "INSERT INTO objects.object (name, val, active, hash)\nVALUES ('name', 'val', 't'::boolean, 'abcdefg'),\n ('name', 'val', 't'::boolean, 'abcdefg'),\n ('name', 'val', 't'::boolean, 'abcdefg')",
93+
"pretty/formatting-4.sql": "INSERT INTO objects.object (name, val, active, hash)\nVALUES ('name', 'val', TRUE, 'abcdefg'),\n ('name', 'val', TRUE, 'abcdefg'),\n ('name', 'val', TRUE, 'abcdefg')",
94+
"pretty/formatting-5.sql": "INSERT INTO objects.object (name, val, active, hash)\nVALUES ('name', 'val', FALSE, 'abcdefg')",
95+
"pretty/formatting-6.sql": "INSERT INTO objects.object (name, val, active, hash)\nVALUES ('name', 'val', 'f'::boolean, 'abcdefg')",
96+
"pretty/formatting-7.sql": "CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql",
97+
"pretty/formatting-8.sql": "CREATE FUNCTION test_func2(IN p1 pos_int, IN p2 text) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql",
98+
"pretty/formatting-9.sql": "CREATE FUNCTION test_func3(IN p1 integer, OUT p2 text, INOUT p3 boolean) RETURNS record AS $$ BEGIN p2 := 'test'; END; $$ LANGUAGE plpgsql",
8399
"pretty/cte-1.sql": "WITH regional_sales AS (SELECT region, SUM(sales_amount) as total_sales FROM sales GROUP BY region) SELECT * FROM regional_sales",
84100
"pretty/cte-2.sql": "WITH regional_sales AS (SELECT region, SUM(sales_amount) as total_sales FROM sales GROUP BY region), top_regions AS (SELECT region FROM regional_sales WHERE total_sales > 1000000) SELECT * FROM top_regions",
85101
"pretty/cte-3.sql": "WITH RECURSIVE employee_hierarchy AS (SELECT id, name, manager_id, 1 as level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, e.manager_id, eh.level + 1 FROM employees e JOIN employee_hierarchy eh ON e.manager_id = eh.id) SELECT * FROM employee_hierarchy",
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
-- 1. EXPLAIN with COSTS OFF - tests optional keyword preservation
2+
-- Some deparsers may omit optional keywords like COSTS OFF
3+
-- This documents that EXPLAIN (COSTS OFF) may deparse differently than EXPLAIN alone
4+
EXPLAIN (COSTS OFF) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA';
5+
6+
-- 2. EXPLAIN without COSTS (default behavior)
7+
EXPLAIN SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA';
8+
9+
-- 3. Boolean literal formatting - INPUT FORM with 't'::boolean cast
10+
-- The deparser may normalize this to TRUE
11+
INSERT INTO objects.object (name, val, active, hash)
12+
VALUES ('name', 'val', 't'::boolean, 'abcdefg'),
13+
('name', 'val', 't'::boolean, 'abcdefg'),
14+
('name', 'val', 't'::boolean, 'abcdefg');
15+
16+
-- 4. Boolean literal formatting - CANONICAL FORM with TRUE
17+
INSERT INTO objects.object (name, val, active, hash)
18+
VALUES ('name', 'val', TRUE, 'abcdefg'),
19+
('name', 'val', TRUE, 'abcdefg'),
20+
('name', 'val', TRUE, 'abcdefg');
21+
22+
-- 5. Boolean literal formatting - CANONICAL FORM with FALSE
23+
INSERT INTO objects.object (name, val, active, hash)
24+
VALUES ('name', 'val', FALSE, 'abcdefg');
25+
26+
-- 6. Boolean literal formatting - INPUT FORM with 'f'::boolean cast
27+
-- The deparser may normalize this to FALSE
28+
INSERT INTO objects.object (name, val, active, hash)
29+
VALUES ('name', 'val', 'f'::boolean, 'abcdefg');
30+
31+
-- 7. Parenthesization / argument formatting with IN parameter mode
32+
-- Tests formatting of function parameters with IN mode and custom types
33+
-- Input form: ( IN p1 pos_int )
34+
CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;
35+
36+
-- 8. Parenthesization with multiple IN parameters
37+
CREATE FUNCTION test_func2(IN p1 pos_int, IN p2 text) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;
38+
39+
-- 9. Mixed parameter modes (IN, OUT, INOUT)
40+
CREATE FUNCTION test_func3(IN p1 integer, OUT p2 text, INOUT p3 boolean) RETURNS record AS $$ BEGIN p2 := 'test'; END; $$ LANGUAGE plpgsql;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
-- 1. Unquoted function name "float" (reserved keyword) - INPUT FORM
2+
-- The deparser should quote this reserved keyword
3+
CREATE FUNCTION faker.float(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $EOFCODE$
4+
BEGIN
5+
RETURN min + random() * (max - min);
6+
END;
7+
$EOFCODE$ LANGUAGE plpgsql;
8+
9+
-- 2. Quoted function name "float" - CANONICAL FORM (idempotence check)
10+
CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $EOFCODE$
11+
BEGIN
12+
RETURN min + random() * (max - min);
13+
END;
14+
$EOFCODE$ LANGUAGE plpgsql;
15+
16+
-- 3. Unquoted function name "interval" (reserved keyword) - INPUT FORM
17+
CREATE FUNCTION faker.interval(min int, max int) RETURNS interval AS $EOFCODE$
18+
BEGIN
19+
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
20+
END;
21+
$EOFCODE$ LANGUAGE plpgsql;
22+
23+
-- 4. Quoted function name "interval" - CANONICAL FORM (idempotence check)
24+
CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $EOFCODE$
25+
BEGIN
26+
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
27+
END;
28+
$EOFCODE$ LANGUAGE plpgsql;
29+
30+
-- 5. Unquoted function name "boolean" (reserved keyword) - INPUT FORM
31+
CREATE FUNCTION faker.boolean() RETURNS boolean AS $EOFCODE$
32+
BEGIN
33+
RETURN random() < 0.5;
34+
END;
35+
$EOFCODE$ LANGUAGE plpgsql;
36+
37+
-- 6. Quoted function name "boolean" - CANONICAL FORM (idempotence check)
38+
CREATE FUNCTION faker."boolean"() RETURNS boolean AS $EOFCODE$
39+
BEGIN
40+
RETURN random() < 0.5;
41+
END;
42+
$EOFCODE$ LANGUAGE plpgsql;
43+
44+
-- 7. pg_catalog.substring with quoted identifier - CANONICAL FORM
45+
-- Note: SUBSTRING(value FROM 'pattern') SQL syntax gets deparsed to pg_catalog."substring"(value, 'pattern')
46+
-- The SQL syntax form cannot be tested here due to AST round-trip differences (COERCE_SQL_SYNTAX vs COERCE_EXPLICIT_CALL)
47+
CREATE DOMAIN origin AS text CHECK (value = pg_catalog."substring"(value, '^(https?://[^/]*)'));
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`non-pretty: pretty/formatting-1.sql 1`] = `"EXPLAIN (COSTS OFF) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'"`;
4+
5+
exports[`non-pretty: pretty/formatting-2.sql 1`] = `"EXPLAIN SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'"`;
6+
7+
exports[`non-pretty: pretty/formatting-3.sql 1`] = `"INSERT INTO objects.object (name, val, active, hash) VALUES ('name', 'val', CAST('t' AS boolean), 'abcdefg'), ('name', 'val', CAST('t' AS boolean), 'abcdefg'), ('name', 'val', CAST('t' AS boolean), 'abcdefg')"`;
8+
9+
exports[`non-pretty: pretty/formatting-4.sql 1`] = `"INSERT INTO objects.object (name, val, active, hash) VALUES ('name', 'val', true, 'abcdefg'), ('name', 'val', true, 'abcdefg'), ('name', 'val', true, 'abcdefg')"`;
10+
11+
exports[`non-pretty: pretty/formatting-5.sql 1`] = `"INSERT INTO objects.object (name, val, active, hash) VALUES ('name', 'val', false, 'abcdefg')"`;
12+
13+
exports[`non-pretty: pretty/formatting-6.sql 1`] = `"INSERT INTO objects.object (name, val, active, hash) VALUES ('name', 'val', CAST('f' AS boolean), 'abcdefg')"`;
14+
15+
exports[`non-pretty: pretty/formatting-7.sql 1`] = `"CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql"`;
16+
17+
exports[`non-pretty: pretty/formatting-8.sql 1`] = `"CREATE FUNCTION test_func2(IN p1 pos_int, IN p2 text) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql"`;
18+
19+
exports[`non-pretty: pretty/formatting-9.sql 1`] = `"CREATE FUNCTION test_func3(IN p1 int, OUT p2 text, INOUT p3 boolean) RETURNS record AS $$ BEGIN p2 := 'test'; END; $$ LANGUAGE plpgsql"`;
20+
21+
exports[`pretty: pretty/formatting-1.sql 1`] = `
22+
"EXPLAIN (COSTS OFF) SELECT *
23+
FROM onek2
24+
WHERE
25+
unique2 = 11
26+
AND stringu1 = 'ATAAAA'"
27+
`;
28+
29+
exports[`pretty: pretty/formatting-2.sql 1`] = `
30+
"EXPLAIN SELECT *
31+
FROM onek2
32+
WHERE
33+
unique2 = 11
34+
AND stringu1 = 'ATAAAA'"
35+
`;
36+
37+
exports[`pretty: pretty/formatting-3.sql 1`] = `
38+
"INSERT INTO objects.object (
39+
name,
40+
val,
41+
active,
42+
hash
43+
) VALUES
44+
('name', 'val', CAST('t' AS boolean), 'abcdefg'),
45+
('name', 'val', CAST('t' AS boolean), 'abcdefg'),
46+
('name', 'val', CAST('t' AS boolean), 'abcdefg')"
47+
`;
48+
49+
exports[`pretty: pretty/formatting-4.sql 1`] = `
50+
"INSERT INTO objects.object (
51+
name,
52+
val,
53+
active,
54+
hash
55+
) VALUES
56+
('name', 'val', true, 'abcdefg'),
57+
('name', 'val', true, 'abcdefg'),
58+
('name', 'val', true, 'abcdefg')"
59+
`;
60+
61+
exports[`pretty: pretty/formatting-5.sql 1`] = `
62+
"INSERT INTO objects.object (
63+
name,
64+
val,
65+
active,
66+
hash
67+
) VALUES
68+
('name', 'val', false, 'abcdefg')"
69+
`;
70+
71+
exports[`pretty: pretty/formatting-6.sql 1`] = `
72+
"INSERT INTO objects.object (
73+
name,
74+
val,
75+
active,
76+
hash
77+
) VALUES
78+
('name', 'val', CAST('f' AS boolean), 'abcdefg')"
79+
`;
80+
81+
exports[`pretty: pretty/formatting-7.sql 1`] = `"CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql"`;
82+
83+
exports[`pretty: pretty/formatting-8.sql 1`] = `"CREATE FUNCTION test_func2(IN p1 pos_int, IN p2 text) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql"`;
84+
85+
exports[`pretty: pretty/formatting-9.sql 1`] = `"CREATE FUNCTION test_func3(IN p1 int, OUT p2 text, INOUT p3 boolean) RETURNS record AS $$ BEGIN p2 := 'test'; END; $$ LANGUAGE plpgsql"`;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`non-pretty: pretty/quoting-1.sql 1`] = `
4+
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
5+
BEGIN
6+
RETURN min + random() * (max - min);
7+
END;
8+
$$ LANGUAGE plpgsql"
9+
`;
10+
11+
exports[`non-pretty: pretty/quoting-2.sql 1`] = `
12+
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
13+
BEGIN
14+
RETURN min + random() * (max - min);
15+
END;
16+
$$ LANGUAGE plpgsql"
17+
`;
18+
19+
exports[`non-pretty: pretty/quoting-3.sql 1`] = `
20+
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
21+
BEGIN
22+
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
23+
END;
24+
$$ LANGUAGE plpgsql"
25+
`;
26+
27+
exports[`non-pretty: pretty/quoting-4.sql 1`] = `
28+
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
29+
BEGIN
30+
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
31+
END;
32+
$$ LANGUAGE plpgsql"
33+
`;
34+
35+
exports[`non-pretty: pretty/quoting-5.sql 1`] = `
36+
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
37+
BEGIN
38+
RETURN random() < 0.5;
39+
END;
40+
$$ LANGUAGE plpgsql"
41+
`;
42+
43+
exports[`non-pretty: pretty/quoting-6.sql 1`] = `
44+
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
45+
BEGIN
46+
RETURN random() < 0.5;
47+
END;
48+
$$ LANGUAGE plpgsql"
49+
`;
50+
51+
exports[`non-pretty: pretty/quoting-7.sql 1`] = `"CREATE DOMAIN origin AS text CHECK (value = pg_catalog."substring"(value, '^(https?://[^/]*)'))"`;
52+
53+
exports[`pretty: pretty/quoting-1.sql 1`] = `
54+
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
55+
BEGIN
56+
RETURN min + random() * (max - min);
57+
END;
58+
$$ LANGUAGE plpgsql"
59+
`;
60+
61+
exports[`pretty: pretty/quoting-2.sql 1`] = `
62+
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
63+
BEGIN
64+
RETURN min + random() * (max - min);
65+
END;
66+
$$ LANGUAGE plpgsql"
67+
`;
68+
69+
exports[`pretty: pretty/quoting-3.sql 1`] = `
70+
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
71+
BEGIN
72+
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
73+
END;
74+
$$ LANGUAGE plpgsql"
75+
`;
76+
77+
exports[`pretty: pretty/quoting-4.sql 1`] = `
78+
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
79+
BEGIN
80+
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
81+
END;
82+
$$ LANGUAGE plpgsql"
83+
`;
84+
85+
exports[`pretty: pretty/quoting-5.sql 1`] = `
86+
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
87+
BEGIN
88+
RETURN random() < 0.5;
89+
END;
90+
$$ LANGUAGE plpgsql"
91+
`;
92+
93+
exports[`pretty: pretty/quoting-6.sql 1`] = `
94+
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
95+
BEGIN
96+
RETURN random() < 0.5;
97+
END;
98+
$$ LANGUAGE plpgsql"
99+
`;
100+
101+
exports[`pretty: pretty/quoting-7.sql 1`] = `
102+
"CREATE DOMAIN origin AS text
103+
CHECK (value = pg_catalog."substring"(value, '^(https?://[^/]*)'))"
104+
`;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { PrettyTest } from '../../test-utils/PrettyTest';
2+
const prettyTest = new PrettyTest([
3+
'pretty/formatting-1.sql',
4+
'pretty/formatting-2.sql',
5+
'pretty/formatting-3.sql',
6+
'pretty/formatting-4.sql',
7+
'pretty/formatting-5.sql',
8+
'pretty/formatting-6.sql',
9+
'pretty/formatting-7.sql',
10+
'pretty/formatting-8.sql',
11+
'pretty/formatting-9.sql',
12+
]);
13+
14+
prettyTest.generateTests();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { PrettyTest } from '../../test-utils/PrettyTest';
2+
const prettyTest = new PrettyTest([
3+
'pretty/quoting-1.sql',
4+
'pretty/quoting-2.sql',
5+
'pretty/quoting-3.sql',
6+
'pretty/quoting-4.sql',
7+
'pretty/quoting-5.sql',
8+
'pretty/quoting-6.sql',
9+
'pretty/quoting-7.sql',
10+
]);
11+
12+
prettyTest.generateTests();

0 commit comments

Comments
 (0)