Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions __fixtures__/generated/generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@
"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)",
"pretty/selects-14.sql": "SELECT\n id,\n name,\n email\nFROM users\nWHERE\n active = true",
"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",
"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",
"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",
"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",
"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",
"pretty/quoting-5.sql": "CREATE FUNCTION faker.boolean() RETURNS boolean AS $EOFCODE$\nBEGIN\n RETURN random() < 0.5;\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
"pretty/quoting-6.sql": "CREATE FUNCTION faker.\"boolean\"() RETURNS boolean AS $EOFCODE$\nBEGIN\n RETURN random() < 0.5;\nEND;\n$EOFCODE$ LANGUAGE plpgsql",
"pretty/quoting-7.sql": "CREATE DOMAIN origin AS text CHECK (value = pg_catalog.\"substring\"(value, '^(https?://[^/]*)'))",
"pretty/procedures-1.sql": "SELECT handle_insert('TYPE_A')",
"pretty/procedures-2.sql": "SELECT \"HandleInsert\"('TYPE_A', 'Region-1')",
"pretty/procedures-3.sql": "SELECT compute_score(42, TRUE)",
Expand Down Expand Up @@ -80,6 +87,15 @@
"pretty/misc-15.sql": "ALTER DEFAULT PRIVILEGES IN SCHEMA dashboard_jobs \n GRANT EXECUTE ON FUNCTIONS TO administrator",
"pretty/misc-16.sql": "GRANT EXECUTE ON FUNCTION dashboard_private.uuid_generate_seeded_uuid TO PUBLIC",
"pretty/misc-17.sql": "SELECT CAST(t.date AT TIME ZONE $$America/New_York$$ AS text)::date FROM tbl t",
"pretty/formatting-1.sql": "EXPLAIN (COSTS OFF) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'",
"pretty/formatting-2.sql": "EXPLAIN SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'",
"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')",
"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')",
"pretty/formatting-5.sql": "INSERT INTO objects.object (name, val, active, hash)\nVALUES ('name', 'val', FALSE, 'abcdefg')",
"pretty/formatting-6.sql": "INSERT INTO objects.object (name, val, active, hash)\nVALUES ('name', 'val', 'f'::boolean, 'abcdefg')",
"pretty/formatting-7.sql": "CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql",
"pretty/formatting-8.sql": "CREATE FUNCTION test_func2(IN p1 pos_int, IN p2 text) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql",
"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",
"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",
"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",
"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",
Expand Down
40 changes: 40 additions & 0 deletions __fixtures__/kitchen-sink/pretty/formatting.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- 1. EXPLAIN with COSTS OFF - tests optional keyword preservation
-- Some deparsers may omit optional keywords like COSTS OFF
-- This documents that EXPLAIN (COSTS OFF) may deparse differently than EXPLAIN alone
EXPLAIN (COSTS OFF) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA';

-- 2. EXPLAIN without COSTS (default behavior)
EXPLAIN SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA';

-- 3. Boolean literal formatting - INPUT FORM with 't'::boolean cast
-- The deparser may normalize this to TRUE
INSERT INTO objects.object (name, val, active, hash)
VALUES ('name', 'val', 't'::boolean, 'abcdefg'),
('name', 'val', 't'::boolean, 'abcdefg'),
('name', 'val', 't'::boolean, 'abcdefg');

-- 4. Boolean literal formatting - CANONICAL FORM with TRUE
INSERT INTO objects.object (name, val, active, hash)
VALUES ('name', 'val', TRUE, 'abcdefg'),
('name', 'val', TRUE, 'abcdefg'),
('name', 'val', TRUE, 'abcdefg');

-- 5. Boolean literal formatting - CANONICAL FORM with FALSE
INSERT INTO objects.object (name, val, active, hash)
VALUES ('name', 'val', FALSE, 'abcdefg');

-- 6. Boolean literal formatting - INPUT FORM with 'f'::boolean cast
-- The deparser may normalize this to FALSE
INSERT INTO objects.object (name, val, active, hash)
VALUES ('name', 'val', 'f'::boolean, 'abcdefg');

-- 7. Parenthesization / argument formatting with IN parameter mode
-- Tests formatting of function parameters with IN mode and custom types
-- Input form: ( IN p1 pos_int )
CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;

-- 8. Parenthesization with multiple IN parameters
CREATE FUNCTION test_func2(IN p1 pos_int, IN p2 text) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;

-- 9. Mixed parameter modes (IN, OUT, INOUT)
CREATE FUNCTION test_func3(IN p1 integer, OUT p2 text, INOUT p3 boolean) RETURNS record AS $$ BEGIN p2 := 'test'; END; $$ LANGUAGE plpgsql;
47 changes: 47 additions & 0 deletions __fixtures__/kitchen-sink/pretty/quoting.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- 1. Unquoted function name "float" (reserved keyword) - INPUT FORM
-- The deparser should quote this reserved keyword
CREATE FUNCTION faker.float(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $EOFCODE$
BEGIN
RETURN min + random() * (max - min);
END;
$EOFCODE$ LANGUAGE plpgsql;

-- 2. Quoted function name "float" - CANONICAL FORM (idempotence check)
CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $EOFCODE$
BEGIN
RETURN min + random() * (max - min);
END;
$EOFCODE$ LANGUAGE plpgsql;

-- 3. Unquoted function name "interval" (reserved keyword) - INPUT FORM
CREATE FUNCTION faker.interval(min int, max int) RETURNS interval AS $EOFCODE$
BEGIN
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
END;
$EOFCODE$ LANGUAGE plpgsql;

-- 4. Quoted function name "interval" - CANONICAL FORM (idempotence check)
CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $EOFCODE$
BEGIN
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
END;
$EOFCODE$ LANGUAGE plpgsql;

-- 5. Unquoted function name "boolean" (reserved keyword) - INPUT FORM
CREATE FUNCTION faker.boolean() RETURNS boolean AS $EOFCODE$
BEGIN
RETURN random() < 0.5;
END;
$EOFCODE$ LANGUAGE plpgsql;

-- 6. Quoted function name "boolean" - CANONICAL FORM (idempotence check)
CREATE FUNCTION faker."boolean"() RETURNS boolean AS $EOFCODE$
BEGIN
RETURN random() < 0.5;
END;
$EOFCODE$ LANGUAGE plpgsql;

-- 7. pg_catalog.substring with quoted identifier - CANONICAL FORM
-- Note: SUBSTRING(value FROM 'pattern') SQL syntax gets deparsed to pg_catalog."substring"(value, 'pattern')
-- The SQL syntax form cannot be tested here due to AST round-trip differences (COERCE_SQL_SYNTAX vs COERCE_EXPLICIT_CALL)
CREATE DOMAIN origin AS text CHECK (value = pg_catalog."substring"(value, '^(https?://[^/]*)'));
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`non-pretty: pretty/formatting-1.sql 1`] = `"EXPLAIN (COSTS OFF) SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'"`;

exports[`non-pretty: pretty/formatting-2.sql 1`] = `"EXPLAIN SELECT * FROM onek2 WHERE unique2 = 11 AND stringu1 = 'ATAAAA'"`;

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')"`;

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')"`;

exports[`non-pretty: pretty/formatting-5.sql 1`] = `"INSERT INTO objects.object (name, val, active, hash) VALUES ('name', 'val', false, 'abcdefg')"`;

exports[`non-pretty: pretty/formatting-6.sql 1`] = `"INSERT INTO objects.object (name, val, active, hash) VALUES ('name', 'val', CAST('f' AS boolean), 'abcdefg')"`;

exports[`non-pretty: pretty/formatting-7.sql 1`] = `"CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql"`;

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"`;

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"`;

exports[`pretty: pretty/formatting-1.sql 1`] = `
"EXPLAIN (COSTS OFF) SELECT *
FROM onek2
WHERE
unique2 = 11
AND stringu1 = 'ATAAAA'"
`;

exports[`pretty: pretty/formatting-2.sql 1`] = `
"EXPLAIN SELECT *
FROM onek2
WHERE
unique2 = 11
AND stringu1 = 'ATAAAA'"
`;

exports[`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')"
`;

exports[`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')"
`;

exports[`pretty: pretty/formatting-5.sql 1`] = `
"INSERT INTO objects.object (
name,
val,
active,
hash
) VALUES
('name', 'val', false, 'abcdefg')"
`;

exports[`pretty: pretty/formatting-6.sql 1`] = `
"INSERT INTO objects.object (
name,
val,
active,
hash
) VALUES
('name', 'val', CAST('f' AS boolean), 'abcdefg')"
`;

exports[`pretty: pretty/formatting-7.sql 1`] = `"CREATE FUNCTION test_func(IN p1 pos_int) RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql"`;

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"`;

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"`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`non-pretty: pretty/quoting-1.sql 1`] = `
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
BEGIN
RETURN min + random() * (max - min);
END;
$$ LANGUAGE plpgsql"
`;

exports[`non-pretty: pretty/quoting-2.sql 1`] = `
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
BEGIN
RETURN min + random() * (max - min);
END;
$$ LANGUAGE plpgsql"
`;

exports[`non-pretty: pretty/quoting-3.sql 1`] = `
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
BEGIN
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
END;
$$ LANGUAGE plpgsql"
`;

exports[`non-pretty: pretty/quoting-4.sql 1`] = `
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
BEGIN
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
END;
$$ LANGUAGE plpgsql"
`;

exports[`non-pretty: pretty/quoting-5.sql 1`] = `
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
BEGIN
RETURN random() < 0.5;
END;
$$ LANGUAGE plpgsql"
`;

exports[`non-pretty: pretty/quoting-6.sql 1`] = `
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
BEGIN
RETURN random() < 0.5;
END;
$$ LANGUAGE plpgsql"
`;

exports[`non-pretty: pretty/quoting-7.sql 1`] = `"CREATE DOMAIN origin AS text CHECK (value = pg_catalog."substring"(value, '^(https?://[^/]*)'))"`;

exports[`pretty: pretty/quoting-1.sql 1`] = `
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
BEGIN
RETURN min + random() * (max - min);
END;
$$ LANGUAGE plpgsql"
`;

exports[`pretty: pretty/quoting-2.sql 1`] = `
"CREATE FUNCTION faker."float"(min double precision DEFAULT 0, max double precision DEFAULT 100) RETURNS double precision AS $$
BEGIN
RETURN min + random() * (max - min);
END;
$$ LANGUAGE plpgsql"
`;

exports[`pretty: pretty/quoting-3.sql 1`] = `
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
BEGIN
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
END;
$$ LANGUAGE plpgsql"
`;

exports[`pretty: pretty/quoting-4.sql 1`] = `
"CREATE FUNCTION faker."interval"(min int, max int) RETURNS interval AS $$
BEGIN
RETURN make_interval(secs => (min + floor(random() * (max - min + 1)))::int);
END;
$$ LANGUAGE plpgsql"
`;

exports[`pretty: pretty/quoting-5.sql 1`] = `
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
BEGIN
RETURN random() < 0.5;
END;
$$ LANGUAGE plpgsql"
`;

exports[`pretty: pretty/quoting-6.sql 1`] = `
"CREATE FUNCTION faker."boolean"() RETURNS boolean AS $$
BEGIN
RETURN random() < 0.5;
END;
$$ LANGUAGE plpgsql"
`;

exports[`pretty: pretty/quoting-7.sql 1`] = `
"CREATE DOMAIN origin AS text
CHECK (value = pg_catalog."substring"(value, '^(https?://[^/]*)'))"
`;
14 changes: 14 additions & 0 deletions packages/deparser/__tests__/pretty/formatting-pretty.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { PrettyTest } from '../../test-utils/PrettyTest';
const prettyTest = new PrettyTest([
'pretty/formatting-1.sql',
'pretty/formatting-2.sql',
'pretty/formatting-3.sql',
'pretty/formatting-4.sql',
'pretty/formatting-5.sql',
'pretty/formatting-6.sql',
'pretty/formatting-7.sql',
'pretty/formatting-8.sql',
'pretty/formatting-9.sql',
]);

prettyTest.generateTests();
12 changes: 12 additions & 0 deletions packages/deparser/__tests__/pretty/quoting-pretty.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PrettyTest } from '../../test-utils/PrettyTest';
const prettyTest = new PrettyTest([
'pretty/quoting-1.sql',
'pretty/quoting-2.sql',
'pretty/quoting-3.sql',
'pretty/quoting-4.sql',
'pretty/quoting-5.sql',
'pretty/quoting-6.sql',
'pretty/quoting-7.sql',
]);

prettyTest.generateTests();