Skip to content

Commit 9444414

Browse files
Implement pretty formatting option for CREATE TABLE statements
- Add pretty option to DeparserOptions interface - Enhance SqlFormatter with proper indentation support - Modify CreateStmt to format with newlines and tabs when pretty=true - Create new pretty/ test folder with snapshot tests - Maintain backward compatibility (pretty=false by default) - All existing tests continue to pass Co-Authored-By: Dan Lynch <[email protected]>
1 parent e191d2f commit 9444414

File tree

5 files changed

+145
-4
lines changed

5 files changed

+145
-4
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
CREATE TABLE users (
3+
id SERIAL PRIMARY KEY,
4+
name TEXT NOT NULL,
5+
email TEXT UNIQUE
6+
);
7+
8+
CREATE TABLE products (
9+
id SERIAL PRIMARY KEY,
10+
name VARCHAR(255) NOT NULL,
11+
price DECIMAL(10,2) CHECK (price > 0),
12+
category_id INTEGER,
13+
description TEXT,
14+
created_at TIMESTAMP DEFAULT now(),
15+
updated_at TIMESTAMP,
16+
UNIQUE (name, category_id),
17+
FOREIGN KEY (category_id) REFERENCES categories(id)
18+
);
19+
20+
CREATE TABLE orders (
21+
id SERIAL PRIMARY KEY,
22+
subtotal DECIMAL(10,2) NOT NULL,
23+
tax_rate DECIMAL(5,4) DEFAULT 0.0825,
24+
tax_amount DECIMAL(10,2) GENERATED ALWAYS AS (subtotal * tax_rate) STORED,
25+
total DECIMAL(10,2) GENERATED ALWAYS AS (subtotal + tax_amount) STORED
26+
);
27+
28+
CREATE TABLE sales (
29+
id SERIAL,
30+
sale_date DATE NOT NULL,
31+
amount DECIMAL(10,2),
32+
region VARCHAR(50)
33+
) PARTITION BY RANGE (sale_date);
34+
35+
CREATE TEMPORARY TABLE temp_calculations (
36+
id INTEGER,
37+
value DECIMAL(15,5),
38+
result TEXT
39+
);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Pretty CREATE TABLE formatting should format basic CREATE TABLE with pretty option enabled 1`] = `
4+
"CREATE TABLE users (
5+
id serial PRIMARY KEY,
6+
name text NOT NULL,
7+
email text UNIQUE
8+
);"
9+
`;
10+
11+
exports[`Pretty CREATE TABLE formatting should format complex CREATE TABLE with pretty option enabled 1`] = `
12+
"CREATE TABLE orders (
13+
id serial PRIMARY KEY,
14+
user_id int NOT NULL,
15+
total numeric(10, 2) CHECK (total > 0),
16+
status varchar(20) DEFAULT 'pending',
17+
created_at pg_catalog.timestamp DEFAULT now(),
18+
FOREIGN KEY (user_id) REFERENCES users (id)
19+
);"
20+
`;
21+
22+
exports[`Pretty CREATE TABLE 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', created_at pg_catalog.timestamp DEFAULT now(), FOREIGN KEY (user_id) REFERENCES users (id));"`;
23+
24+
exports[`Pretty CREATE TABLE formatting should maintain single-line format when pretty option disabled 1`] = `"CREATE TABLE users (id serial PRIMARY KEY, name text NOT NULL, email text UNIQUE);"`;
25+
26+
exports[`Pretty CREATE TABLE formatting should use custom newline and tab characters in pretty mode 1`] = `
27+
"CREATE TABLE users (
28+
id serial PRIMARY KEY,
29+
name text NOT NULL,
30+
email text UNIQUE
31+
);"
32+
`;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { deparseSync } from '../../src';
2+
import { parse } from 'libpg-query';
3+
4+
describe('Pretty CREATE TABLE formatting', () => {
5+
const basicTableSql = `CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE);`;
6+
7+
const complexTableSql = `CREATE TABLE orders (
8+
id SERIAL PRIMARY KEY,
9+
user_id INTEGER NOT NULL,
10+
total DECIMAL(10,2) CHECK (total > 0),
11+
status VARCHAR(20) DEFAULT 'pending',
12+
created_at TIMESTAMP DEFAULT now(),
13+
FOREIGN KEY (user_id) REFERENCES users(id)
14+
);`;
15+
16+
it('should format basic CREATE TABLE with pretty option enabled', async () => {
17+
const parsed = await parse(basicTableSql);
18+
const result = deparseSync(parsed, { pretty: true });
19+
expect(result).toMatchSnapshot();
20+
});
21+
22+
it('should maintain single-line format when pretty option disabled', async () => {
23+
const parsed = await parse(basicTableSql);
24+
const result = deparseSync(parsed, { pretty: false });
25+
expect(result).toMatchSnapshot();
26+
});
27+
28+
it('should format complex CREATE TABLE with pretty option enabled', async () => {
29+
const parsed = await parse(complexTableSql);
30+
const result = deparseSync(parsed, { pretty: true });
31+
expect(result).toMatchSnapshot();
32+
});
33+
34+
it('should maintain single-line format for complex table when pretty disabled', async () => {
35+
const parsed = await parse(complexTableSql);
36+
const result = deparseSync(parsed, { pretty: false });
37+
expect(result).toMatchSnapshot();
38+
});
39+
40+
it('should use custom newline and tab characters in pretty mode', async () => {
41+
const parsed = await parse(basicTableSql);
42+
const result = deparseSync(parsed, {
43+
pretty: true,
44+
newline: '\r\n',
45+
tab: ' '
46+
});
47+
expect(result).toMatchSnapshot();
48+
});
49+
});

packages/deparser/src/deparser.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface DeparserOptions {
1212
functionDelimiter?: string; // Default: '$$'
1313
// Alternative delimiter when the default is found in the body
1414
functionDelimiterFallback?: string; // Default: '$EOFCODE$'
15+
pretty?: boolean; // Default: false
1516
}
1617

1718
// Type guards for better type safety
@@ -64,7 +65,7 @@ export class Deparser implements DeparserVisitor {
6465
private options: DeparserOptions;
6566

6667
constructor(tree: Node | Node[] | t.ParseResult, opts: DeparserOptions = {}) {
67-
this.formatter = new SqlFormatter(opts.newline, opts.tab);
68+
this.formatter = new SqlFormatter(opts.newline, opts.tab, opts.pretty);
6869

6970
// Set default options
7071
this.options = {
@@ -2139,7 +2140,15 @@ export class Deparser implements DeparserVisitor {
21392140
const elementStrs = elements.map(el => {
21402141
return this.deparse(el, context);
21412142
});
2142-
output.push(this.formatter.parens(elementStrs.join(', ')));
2143+
2144+
if (this.formatter.isPretty()) {
2145+
const formattedElements = elementStrs.map(el =>
2146+
this.formatter.indent(el)
2147+
).join(',' + this.formatter.newline());
2148+
output.push('(' + this.formatter.newline() + formattedElements + this.formatter.newline() + ')');
2149+
} else {
2150+
output.push(this.formatter.parens(elementStrs.join(', ')));
2151+
}
21432152
} else if (!node.partbound) {
21442153
output.push(this.formatter.parens(''));
21452154
}

packages/deparser/src/utils/sql-formatter.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
export class SqlFormatter {
22
private newlineChar: string;
33
private tabChar: string;
4+
private prettyMode: boolean;
45

5-
constructor(newlineChar: string = '\n', tabChar: string = ' ') {
6+
constructor(newlineChar: string = '\n', tabChar: string = ' ', prettyMode: boolean = false) {
67
this.newlineChar = newlineChar;
78
this.tabChar = tabChar;
9+
this.prettyMode = prettyMode;
810
}
911

1012
format(parts: string[], separator: string = ' '): string {
1113
return parts.filter(part => part !== null && part !== undefined && part !== '').join(separator);
1214
}
1315

1416
indent(text: string, count: number = 1): string {
15-
return text;
17+
if (!this.prettyMode) {
18+
return text;
19+
}
20+
const indentation = this.tabChar.repeat(count);
21+
return text.split(this.newlineChar).map(line =>
22+
line.trim() ? indentation + line : line
23+
).join(this.newlineChar);
1624
}
1725

1826
parens(content: string): string {
@@ -26,4 +34,8 @@ export class SqlFormatter {
2634
tab(): string {
2735
return this.tabChar;
2836
}
37+
38+
isPretty(): boolean {
39+
return this.prettyMode;
40+
}
2941
}

0 commit comments

Comments
 (0)