Skip to content

Commit b2fd75a

Browse files
rebased
1 parent 1a11556 commit b2fd75a

File tree

12 files changed

+172
-8
lines changed

12 files changed

+172
-8
lines changed

crates/pglt_analyser/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ mod tests {
9292
String::from_utf8(buffer).unwrap()
9393
}
9494

95-
const SQL: &str = r#"alter table test drop column id;"#;
96-
let rule_filter = RuleFilter::Rule("safety", "banDropColumn");
95+
const SQL: &str = r#"alter table test add column count int not null;"#;
96+
let rule_filter = RuleFilter::Rule("safety", "addingRequiredField");
9797

9898
let filter = AnalysisFilter {
9999
enabled_rules: Some(slice::from_ref(&rule_filter)),
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! Generated file, do not edit by hand, see `xtask/codegen`
22
33
use pglt_analyse::declare_lint_group;
4+
pub mod adding_required_field;
45
pub mod ban_drop_column;
56
pub mod ban_drop_not_null;
67
pub mod ban_drop_table;
7-
declare_lint_group! { pub Safety { name : "safety" , rules : [self :: ban_drop_column :: BanDropColumn , self :: ban_drop_not_null :: BanDropNotNull , self :: ban_drop_table :: BanDropTable ,] } }
8+
declare_lint_group! { pub Safety { name : "safety" , rules : [self :: adding_required_field :: AddingRequiredField , self :: ban_drop_column :: BanDropColumn , self :: ban_drop_not_null :: BanDropNotNull , self :: ban_drop_table :: BanDropTable ,] } }
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use pglt_analyse::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource};
2+
use pglt_console::markup;
3+
4+
declare_lint_rule! {
5+
/// Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required.
6+
///
7+
/// This will fail immediately upon running for any populated table. Furthermore, old application code that is unaware of this column will fail to INSERT to this table.
8+
///
9+
/// Make new columns optional initially by omitting the NOT NULL constraint until all existing data and application code has been updated. Once no NULL values are written to or persisted in the database, set it to NOT NULL.
10+
/// Alternatively, if using Postgres version 11 or later, add a DEFAULT value that is not volatile. This allows the column to keep its NOT NULL constraint.
11+
///
12+
/// ## Invalid
13+
/// alter table test add column count int not null;
14+
///
15+
/// ## Valid in Postgres >= 11
16+
/// alter table test add column count int not null default 0;
17+
pub AddingRequiredField {
18+
version: "next",
19+
name: "addingRequiredField",
20+
recommended: false,
21+
sources: &[RuleSource::Squawk("adding-required-field")],
22+
}
23+
}
24+
25+
impl Rule for AddingRequiredField {
26+
type Options = ();
27+
28+
fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
29+
let mut diagnostics = vec![];
30+
31+
if let pglt_query_ext::NodeEnum::AlterTableStmt(stmt) = ctx.stmt() {
32+
// We are currently lacking a way to check if a `AtAddColumn` subtype sets a
33+
// not null constraint – so we'll need to check the plain SQL.
34+
let plain_sql = ctx.stmt().to_ref().deparse().unwrap().to_ascii_lowercase();
35+
let is_nullable = !plain_sql.contains("not null");
36+
let has_set_default = plain_sql.contains("default");
37+
if is_nullable || has_set_default {
38+
return diagnostics;
39+
}
40+
41+
for cmd in &stmt.cmds {
42+
if let Some(pglt_query_ext::NodeEnum::AlterTableCmd(alter_table_cmd)) = &cmd.node {
43+
if alter_table_cmd.subtype()
44+
== pglt_query_ext::protobuf::AlterTableType::AtAddColumn
45+
{
46+
diagnostics.push(
47+
RuleDiagnostic::new(
48+
rule_category!(),
49+
None,
50+
markup! {
51+
"Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required."
52+
},
53+
)
54+
.detail(
55+
None,
56+
"Make new columns optional initially by omitting the NOT NULL constraint until all existing data and application code has been updated. Once no NULL values are written to or persisted in the database, set it to NOT NULL. Alternatively, if using Postgres version 11 or later, add a DEFAULT value that is not volatile. This allows the column to keep its NOT NULL constraint.
57+
",
58+
),
59+
);
60+
}
61+
}
62+
}
63+
}
64+
65+
diagnostics
66+
}
67+
}

crates/pglt_analyser/src/options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Generated file, do not edit by hand, see `xtask/codegen`
22
33
use crate::lint;
4+
pub type AddingRequiredField =
5+
<lint::safety::adding_required_field::AddingRequiredField as pglt_analyse::Rule>::Options;
46
pub type BanDropColumn =
57
<lint::safety::ban_drop_column::BanDropColumn as pglt_analyse::Rule>::Options;
68
pub type BanDropNotNull =
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- expect_only_lint/safety/addingRequiredField
2+
alter table test add column c int not null;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
source: crates/pglt_analyser/tests/rules_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```
7+
-- expect_only_lint/safety/addingRequiredField
8+
alter table test add column c int not null;
9+
```
10+
11+
# Diagnostics
12+
lint/safety/addingRequiredField ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
13+
14+
× Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required.
15+
16+
i Make new columns optional initially by omitting the NOT NULL constraint until all existing data and application code has been updated. Once no NULL values are written to or persisted in the database, set it to NOT NULL. Alternatively, if using Postgres version 11 or later, add a DEFAULT value that is not volatile. This allows the column to keep its NOT NULL constraint.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- expect_no_diagnostics
2+
alter table test
3+
add column c int not null default 0;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: crates/pglt_analyser/tests/rules_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```
7+
-- expect_no_diagnostics
8+
alter table test
9+
add column c int not null default 0;
10+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- expect_no_diagnostics
2+
alter table test
3+
add column c int;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: crates/pglt_analyser/tests/rules_tests.rs
3+
expression: snapshot
4+
---
5+
# Input
6+
```
7+
-- expect_no_diagnostics
8+
alter table test
9+
add column c int;
10+
```

0 commit comments

Comments
 (0)