Skip to content

Commit 6e2677c

Browse files
authored
chore: initial pretty printer (#546)
1 parent 2be26a5 commit 6e2677c

1,728 files changed

Lines changed: 492443 additions & 1527 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 912 additions & 826 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@ rust-version = "1.86.0"
1515
[workspace.dependencies]
1616
# supporting crates unrelated to postgres
1717
anyhow = "1.0.92"
18+
biome_console = "=0.5.7"
1819
biome_deserialize = "0.6.0"
1920
biome_deserialize_macros = "0.6.0"
21+
biome_diagnostics = "=0.5.7"
2022
biome_js_factory = "0.5.7"
2123
biome_js_formatter = "0.5.7"
2224
biome_js_syntax = "0.5.7"
23-
biome_rowan = "0.5.7"
24-
biome_string_case = "0.5.8"
25+
biome_json_parser = "=0.5.7"
26+
biome_parser = "=0.5.7"
27+
biome_rowan = "=0.5.7"
28+
biome_string_case = "=0.5.8"
29+
biome_text_edit = "=0.5.7"
30+
biome_text_size = "=0.5.7"
31+
biome_unicode_table = "=0.5.7"
2532
bpaf = { version = "0.9.15", features = ["derive"] }
2633
criterion = "0.5"
2734
crossbeam = "0.8.4"
@@ -44,7 +51,9 @@ slotmap = "1.0.7"
4451
smallvec = { version = "1.13.2", features = ["union", "const_new", "serde"] }
4552
strum = { version = "0.27.1", features = ["derive"] }
4653
# this will use tokio if available, otherwise async-std
54+
camino = "1.1.9"
4755
convert_case = "0.6.0"
56+
dir-test = "0.4.1"
4857
prost = "0.13.5"
4958
prost-reflect = "0.15.3"
5059
protox = "0.8.0"
@@ -79,6 +88,8 @@ pgls_lsp = { path = "./crates/pgls_lsp", version = "0.0.0" }
7988
pgls_markup = { path = "./crates/pgls_markup", version = "0.0.0" }
8089
pgls_matcher = { path = "./crates/pgls_matcher", version = "0.0.0" }
8190
pgls_plpgsql_check = { path = "./crates/pgls_plpgsql_check", version = "0.0.0" }
91+
pgls_pretty_print = { path = "./crates/pgls_pretty_print", version = "0.0.0" }
92+
pgls_pretty_print_codegen = { path = "./crates/pgls_pretty_print_codegen", version = "0.0.0" }
8293
pgls_query = { path = "./crates/pgls_query", version = "0.0.0" }
8394
pgls_query_ext = { path = "./crates/pgls_query_ext", version = "0.0.0" }
8495
pgls_query_macros = { path = "./crates/pgls_query_macros", version = "0.0.0" }

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ The following features are implemented:
2626
- Syntax Error Highlighting
2727
- Type-checking (via `EXPLAIN` error insights)
2828
- Linter, inspired by [Squawk](https://squawkhq.com)
29+
- Formatting
2930

3031
Our current focus is on refining and enhancing these core features while building a robust and easily accessible infrastructure. For future plans and opportunities to contribute, please check out the issues and discussions. Any contributions are welcome!
3132

crates/pgls_analyser/src/lint/safety/add_serial_column.rs

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@ impl LinterRule for AddSerialColumn {
4646

4747
if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
4848
for cmd in &stmt.cmds {
49-
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node {
50-
if cmd.subtype() == pgls_query::protobuf::AlterTableType::AtAddColumn {
51-
if let Some(pgls_query::NodeEnum::ColumnDef(col_def)) =
52-
&cmd.def.as_ref().and_then(|d| d.node.as_ref())
53-
{
54-
// Check for SERIAL types
55-
if let Some(type_name) = &col_def.type_name {
56-
let type_str = get_type_name(type_name);
57-
if is_serial_type(&type_str) {
58-
diagnostics.push(
49+
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node
50+
&& cmd.subtype() == pgls_query::protobuf::AlterTableType::AtAddColumn
51+
&& let Some(pgls_query::NodeEnum::ColumnDef(col_def)) =
52+
&cmd.def.as_ref().and_then(|d| d.node.as_ref())
53+
{
54+
// Check for SERIAL types
55+
if let Some(type_name) = &col_def.type_name {
56+
let type_str = get_type_name(type_name);
57+
if is_serial_type(&type_str) {
58+
diagnostics.push(
5959
LinterDiagnostic::new(
6060
rule_category!(),
6161
None,
@@ -66,26 +66,22 @@ impl LinterRule for AddSerialColumn {
6666
.detail(None, "SERIAL types require rewriting the entire table with an ACCESS EXCLUSIVE lock, blocking all reads and writes.")
6767
.note("SERIAL types cannot be added to existing tables without a full table rewrite. Consider using a non-serial type with a sequence instead."),
6868
);
69-
continue;
70-
}
71-
}
69+
continue;
70+
}
71+
}
7272

73-
// Check for GENERATED ALWAYS AS ... STORED
74-
let has_stored_generated =
75-
col_def.constraints.iter().any(|constraint| {
76-
if let Some(pgls_query::NodeEnum::Constraint(c)) =
77-
&constraint.node
78-
{
79-
c.contype()
80-
== pgls_query::protobuf::ConstrType::ConstrGenerated
81-
&& c.generated_when == "a" // 'a' = ALWAYS
82-
} else {
83-
false
84-
}
85-
});
73+
// Check for GENERATED ALWAYS AS ... STORED
74+
let has_stored_generated = col_def.constraints.iter().any(|constraint| {
75+
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node {
76+
c.contype() == pgls_query::protobuf::ConstrType::ConstrGenerated
77+
&& c.generated_when == "a" // 'a' = ALWAYS
78+
} else {
79+
false
80+
}
81+
});
8682

87-
if has_stored_generated {
88-
diagnostics.push(
83+
if has_stored_generated {
84+
diagnostics.push(
8985
LinterDiagnostic::new(
9086
rule_category!(),
9187
None,
@@ -96,8 +92,6 @@ impl LinterRule for AddSerialColumn {
9692
.detail(None, "GENERATED ... STORED columns require rewriting the entire table with an ACCESS EXCLUSIVE lock, blocking all reads and writes.")
9793
.note("GENERATED ... STORED columns cannot be added to existing tables without a full table rewrite."),
9894
);
99-
}
100-
}
10195
}
10296
}
10397
}

crates/pgls_analyser/src/lint/safety/adding_field_with_default.rs

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -50,31 +50,29 @@ impl LinterRule for AddingFieldWithDefault {
5050

5151
if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
5252
for cmd in &stmt.cmds {
53-
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node {
54-
if cmd.subtype() == pgls_query::protobuf::AlterTableType::AtAddColumn {
55-
if let Some(pgls_query::NodeEnum::ColumnDef(col_def)) =
56-
&cmd.def.as_ref().and_then(|d| d.node.as_ref())
57-
{
58-
let has_default = col_def.constraints.iter().any(|constraint| {
59-
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node
60-
{
61-
c.contype() == pgls_query::protobuf::ConstrType::ConstrDefault
62-
} else {
63-
false
64-
}
65-
});
53+
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node
54+
&& cmd.subtype() == pgls_query::protobuf::AlterTableType::AtAddColumn
55+
&& let Some(pgls_query::NodeEnum::ColumnDef(col_def)) =
56+
&cmd.def.as_ref().and_then(|d| d.node.as_ref())
57+
{
58+
let has_default = col_def.constraints.iter().any(|constraint| {
59+
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node {
60+
c.contype() == pgls_query::protobuf::ConstrType::ConstrDefault
61+
} else {
62+
false
63+
}
64+
});
6665

67-
let has_generated = col_def.constraints.iter().any(|constraint| {
68-
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node
69-
{
70-
c.contype() == pgls_query::protobuf::ConstrType::ConstrGenerated
71-
} else {
72-
false
73-
}
74-
});
66+
let has_generated = col_def.constraints.iter().any(|constraint| {
67+
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node {
68+
c.contype() == pgls_query::protobuf::ConstrType::ConstrGenerated
69+
} else {
70+
false
71+
}
72+
});
7573

76-
if has_generated {
77-
diagnostics.push(
74+
if has_generated {
75+
diagnostics.push(
7876
LinterDiagnostic::new(
7977
rule_category!(),
8078
None,
@@ -85,23 +83,26 @@ impl LinterRule for AddingFieldWithDefault {
8583
.detail(None, "This operation requires an ACCESS EXCLUSIVE lock and rewrites the entire table.")
8684
.note("Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead."),
8785
);
88-
} else if has_default {
89-
// For PG 11+, check if the default is volatile
90-
if pg_version.is_some_and(|v| v >= 11) {
91-
// Check if default is non-volatile
92-
let is_safe_default = col_def.constraints.iter().any(|constraint| {
93-
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node {
94-
if c.contype() == pgls_query::protobuf::ConstrType::ConstrDefault {
95-
if let Some(raw_expr) = &c.raw_expr {
96-
return is_safe_default_expr(&raw_expr.node.as_ref().map(|n| Box::new(n.clone())), ctx.schema_cache());
97-
}
98-
}
99-
}
100-
false
101-
});
86+
} else if has_default {
87+
// For PG 11+, check if the default is volatile
88+
if pg_version.is_some_and(|v| v >= 11) {
89+
// Check if default is non-volatile
90+
let is_safe_default = col_def.constraints.iter().any(|constraint| {
91+
if let Some(pgls_query::NodeEnum::Constraint(c)) = &constraint.node
92+
&& c.contype()
93+
== pgls_query::protobuf::ConstrType::ConstrDefault
94+
&& let Some(raw_expr) = &c.raw_expr
95+
{
96+
return is_safe_default_expr(
97+
&raw_expr.node.as_ref().map(|n| Box::new(n.clone())),
98+
ctx.schema_cache(),
99+
);
100+
}
101+
false
102+
});
102103

103-
if !is_safe_default {
104-
diagnostics.push(
104+
if !is_safe_default {
105+
diagnostics.push(
105106
LinterDiagnostic::new(
106107
rule_category!(),
107108
None,
@@ -112,10 +113,10 @@ impl LinterRule for AddingFieldWithDefault {
112113
.detail(None, "Even in PostgreSQL 11+, volatile default values require a full table rewrite.")
113114
.note("Add the column without a default, then set the default in a separate statement."),
114115
);
115-
}
116-
} else {
117-
// Pre PG 11, all defaults cause rewrites
118-
diagnostics.push(
116+
}
117+
} else {
118+
// Pre PG 11, all defaults cause rewrites
119+
diagnostics.push(
119120
LinterDiagnostic::new(
120121
rule_category!(),
121122
None,
@@ -126,8 +127,6 @@ impl LinterRule for AddingFieldWithDefault {
126127
.detail(None, "This operation requires an ACCESS EXCLUSIVE lock and rewrites the entire table.")
127128
.note("Add the column without a default, then set the default in a separate statement."),
128129
);
129-
}
130-
}
131130
}
132131
}
133132
}

crates/pgls_analyser/src/lint/safety/adding_foreign_key_constraint.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,10 @@ impl LinterRule for AddingForeignKeyConstraint {
5656
pgls_query::protobuf::AlterTableType::AtAddConstraint => {
5757
if let Some(pgls_query::NodeEnum::Constraint(constraint)) =
5858
cmd.def.as_ref().and_then(|d| d.node.as_ref())
59-
{
60-
if let Some(diagnostic) =
59+
&& let Some(diagnostic) =
6160
check_foreign_key_constraint(constraint, false)
62-
{
63-
diagnostics.push(diagnostic);
64-
}
61+
{
62+
diagnostics.push(diagnostic);
6563
}
6664
}
6765
pgls_query::protobuf::AlterTableType::AtAddColumn => {
@@ -72,12 +70,10 @@ impl LinterRule for AddingForeignKeyConstraint {
7270
for constraint in &col_def.constraints {
7371
if let Some(pgls_query::NodeEnum::Constraint(constr)) =
7472
&constraint.node
75-
{
76-
if let Some(diagnostic) =
73+
&& let Some(diagnostic) =
7774
check_foreign_key_constraint(constr, true)
78-
{
79-
diagnostics.push(diagnostic);
80-
}
75+
{
76+
diagnostics.push(diagnostic);
8177
}
8278
}
8379
}

crates/pgls_analyser/src/lint/safety/adding_not_null_field.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,17 @@ impl LinterRule for AddingNotNullField {
5858

5959
if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
6060
for cmd in &stmt.cmds {
61-
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node {
62-
if cmd.subtype() == pgls_query::protobuf::AlterTableType::AtSetNotNull {
63-
diagnostics.push(LinterDiagnostic::new(
61+
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node
62+
&& cmd.subtype() == pgls_query::protobuf::AlterTableType::AtSetNotNull
63+
{
64+
diagnostics.push(LinterDiagnostic::new(
6465
rule_category!(),
6566
None,
6667
markup! {
6768
"Setting a column NOT NULL blocks reads while the table is scanned."
6869
},
6970
).detail(None, "This operation requires an ACCESS EXCLUSIVE lock and a full table scan to verify all rows.")
7071
.note("Use a CHECK constraint with NOT VALID instead, then validate it in a separate transaction."));
71-
}
7272
}
7373
}
7474
}

crates/pgls_analyser/src/lint/safety/adding_primary_key_constraint.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,10 @@ impl LinterRule for AddingPrimaryKeyConstraint {
5454
pgls_query::protobuf::AlterTableType::AtAddConstraint => {
5555
if let Some(pgls_query::NodeEnum::Constraint(constraint)) =
5656
cmd.def.as_ref().and_then(|d| d.node.as_ref())
57-
{
58-
if let Some(diagnostic) =
57+
&& let Some(diagnostic) =
5958
check_for_primary_key_constraint(constraint)
60-
{
61-
diagnostics.push(diagnostic);
62-
}
59+
{
60+
diagnostics.push(diagnostic);
6361
}
6462
}
6563
// Check for ADD COLUMN with PRIMARY KEY
@@ -70,12 +68,10 @@ impl LinterRule for AddingPrimaryKeyConstraint {
7068
for constraint in &col_def.constraints {
7169
if let Some(pgls_query::NodeEnum::Constraint(constr)) =
7270
&constraint.node
73-
{
74-
if let Some(diagnostic) =
71+
&& let Some(diagnostic) =
7572
check_for_primary_key_constraint(constr)
76-
{
77-
diagnostics.push(diagnostic);
78-
}
73+
{
74+
diagnostics.push(diagnostic);
7975
}
8076
}
8177
}

0 commit comments

Comments
 (0)