From a3060d7d2f63e48b8110633317a378f81a7bbc1a Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 20 Dec 2025 20:50:14 -0500 Subject: [PATCH 1/3] linter: use annotate-snippets for lint error tests --- Cargo.lock | 1 + crates/squawk_linter/Cargo.toml | 1 + crates/squawk_linter/src/lib.rs | 2 +- .../src/rules/adding_field_with_default.rs | 88 ++--- .../rules/adding_foreign_key_constraint.rs | 42 +- .../src/rules/adding_not_null_field.rs | 21 +- .../rules/adding_primary_key_constraint.rs | 15 +- .../src/rules/adding_required_field.rs | 20 +- .../ban_alter_domain_with_add_constraint.rs | 11 +- .../squawk_linter/src/rules/ban_char_field.rs | 27 +- ...oncurrent_index_creation_in_transaction.rs | 62 ++- .../ban_create_domain_with_constraint.rs | 15 +- .../src/rules/ban_drop_column.rs | 8 +- .../src/rules/ban_drop_database.rs | 8 +- .../src/rules/ban_drop_not_null.rs | 8 +- .../squawk_linter/src/rules/ban_drop_table.rs | 8 +- .../src/rules/ban_truncate_cascade.rs | 11 +- .../src/rules/ban_uncommitted_transaction.rs | 210 ++++------ .../src/rules/changing_column_type.rs | 12 +- .../src/rules/constraint_missing_not_valid.rs | 87 +++-- .../src/rules/disallow_unique_constraint.rs | 49 +-- .../src/rules/prefer_bigint_over_int.rs | 19 +- .../src/rules/prefer_bigint_over_smallint.rs | 19 +- .../src/rules/prefer_identity.rs | 23 +- .../src/rules/prefer_robust_stmts.rs | 244 ++++++------ .../src/rules/prefer_text_field.rs | 32 +- .../src/rules/prefer_timestamptz.rs | 18 +- .../src/rules/renaming_column.rs | 8 +- .../squawk_linter/src/rules/renaming_table.rs | 8 +- .../require_concurrent_index_creation.rs | 33 +- .../require_concurrent_index_deletion.rs | 21 +- .../src/rules/require_timeout_settings.rs | 364 ++++++------------ ...ith_default__test__arbitrary_func_err.snap | 19 +- ...t__test__default_random_with_args_err.snap | 19 +- ...ith_default__test__default_uuid_error.snap | 19 +- ...__test__default_uuid_error_multi_stmt.snap | 19 +- ...ault__test__default_volatile_func_err.snap | 19 +- ...h_default__test__generated_stored_err.snap | 19 +- ...int__test__add_column_references_lock.snap | 10 + ...test__add_foreign_key_constraint_lock.snap | 10 + ..._field__test__regression_gh_issue_519.snap | 19 +- ...ng_not_null_field__test__set_not_null.snap | 19 +- ...y_constraint__test__plain_primary_key.snap | 19 +- ..._constraint__test__serial_primary_key.snap | 19 +- ...field__test__not_null_without_default.snap | 19 +- ...domain_with_add_constraint__test__err.snap | 15 +- ...__ban_char_field__test__all_the_types.snap | 165 +++----- ...ban_char_field__test__alter_table_err.snap | 31 +- ...char_field__test__array_char_type_err.snap | 31 +- ...an_char_field__test__case_insensitive.snap | 31 +- ...test__creating_table_with_char_errors.snap | 111 ++---- ...ent_index_creation_in_transaction_err.snap | 19 +- ...ate_domain_with_constraint__test__err.snap | 15 +- ...__test__err_with_multiple_constraints.snap | 15 +- ...er__rules__ban_drop_column__test__err.snap | 15 +- ...rop_database__test__ban_drop_database.snap | 37 +- ...__rules__ban_drop_not_null__test__err.snap | 15 +- ...ter__rules__ban_drop_table__test__err.snap | 37 +- ...ules__ban_truncate_cascade__test__err.snap | 19 +- ...anging_column_type__test__another_err.snap | 26 +- ...ules__changing_column_type__test__err.snap | 15 +- ...issing_not_valid__test__adding_fk_err.snap | 19 +- ...t__not_valid_validate_transaction_err.snap | 19 +- ...t__test__adding_unique_constraint_err.snap | 19 +- ...ique_constraint_inline_add_column_err.snap | 19 +- ...nstraint_inline_add_column_unique_err.snap | 19 +- ...es__prefer_bigint_over_int__test__err.snap | 157 +++----- ...refer_bigint_over_smallint__test__err.snap | 128 ++---- ...er__rules__prefer_identity__test__err.snap | 221 ++++------- ...prefer_identity__test__ok_when_quoted.snap | 66 +--- ...tmts__test__alter_column_set_not_null.snap | 15 +- ...ts__test__alter_table_drop_column_err.snap | 30 +- ...test__alter_table_drop_constraint_err.snap | 30 +- ...r_robust_stmts__test__alter_table_err.snap | 30 +- ...__test__create_index_concurrently_err.snap | 34 +- ..._robust_stmts__test__create_table_err.snap | 30 +- ..._create_table_with_on_commit_drop_err.snap | 30 +- ...temp_table_without_on_commit_drop_err.snap | 30 +- ..._test__disable_row_level_security_err.snap | 15 +- ...tmts__test__double_add_after_drop_err.snap | 15 +- ...er_robust_stmts__test__drop_index_err.snap | 30 +- ...__test__enable_row_level_security_err.snap | 15 +- ...vel_security_without_exists_check_err.snap | 15 +- ...eld__test__adding_column_non_text_err.snap | 35 +- ...eate_table_with_pgcatalog_varchar_err.snap | 35 +- ...__test__create_table_with_varchar_err.snap | 35 +- ...ield__test__increase_varchar_size_err.snap | 35 +- ..._test__alter_table_with_timestamp_err.snap | 65 +--- ...test__create_table_with_timestamp_err.snap | 65 +--- ...er__rules__renaming_column__test__err.snap | 15 +- ...ter__rules__renaming_table__test__err.snap | 15 +- ...st__adding_index_non_concurrently_err.snap | 34 +- ...__drop_index_missing_concurrently_err.snap | 34 +- ...g__test__begin_assume_transaction_err.snap | 49 +-- ...ion_nesting__test__begin_repeated_err.snap | 19 +- ...on_nesting__test__commit_repeated_err.snap | 19 +- ...commit_with_assume_in_transaction_err.snap | 19 +- ...llback_with_assume_in_transaction_err.snap | 19 +- .../src/rules/transaction_nesting.rs | 79 ++-- crates/squawk_linter/src/test_utils.rs | 113 ++++-- 100 files changed, 1479 insertions(+), 2513 deletions(-) create mode 100644 crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_column_references_lock.snap create mode 100644 crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_foreign_key_constraint_lock.snap diff --git a/Cargo.lock b/Cargo.lock index 3af00dd1..53c70369 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1901,6 +1901,7 @@ dependencies = [ name = "squawk-linter" version = "2.33.2" dependencies = [ + "annotate-snippets", "enum-iterator", "insta", "lazy_static", diff --git a/crates/squawk_linter/Cargo.toml b/crates/squawk_linter/Cargo.toml index 5206ebe8..af70b34f 100644 --- a/crates/squawk_linter/Cargo.toml +++ b/crates/squawk_linter/Cargo.toml @@ -20,6 +20,7 @@ insta.workspace = true enum-iterator.workspace = true line-index.workspace = true serde_plain.workspace = true +annotate-snippets.workspace = true [lints] diff --git a/crates/squawk_linter/src/lib.rs b/crates/squawk_linter/src/lib.rs index 6dd4509d..9da89fbe 100644 --- a/crates/squawk_linter/src/lib.rs +++ b/crates/squawk_linter/src/lib.rs @@ -469,7 +469,7 @@ impl Linter { errors: vec![], ignores: vec![], rules: rules.into(), - settings: LinterSettings::default(), + settings: Default::default(), } } } diff --git a/crates/squawk_linter/src/rules/adding_field_with_default.rs b/crates/squawk_linter/src/rules/adding_field_with_default.rs index 43e86839..69c73af4 100644 --- a/crates/squawk_linter/src/rules/adding_field_with_default.rs +++ b/crates/squawk_linter/src/rules/adding_field_with_default.rs @@ -123,10 +123,14 @@ pub(crate) fn adding_field_with_default(ctx: &mut Linter, parse: &Parse String { + crate::test_utils::lint_errors_with(sql, settings, Rule::AddingFieldWithDefault) + } #[test] fn docs_example_ok_post_pg_11() { @@ -135,9 +139,7 @@ mod test { ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -150,9 +152,7 @@ ALTER TABLE "core_recipe" ALTER COLUMN "foo" SET DEFAULT 10; -- remove nullability "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -161,9 +161,7 @@ ALTER TABLE "core_recipe" ALTER COLUMN "foo" SET DEFAULT 10; alter table t set logged, add column c integer default uuid(); "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingFieldWithDefault)); } #[test] @@ -172,9 +170,7 @@ alter table t set logged, add column c integer default uuid(); ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT uuid(); "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingFieldWithDefault)); } #[test] @@ -184,9 +180,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT uuid(); ALTER TABLE "core_recipe" ADD COLUMN "foo" boolean DEFAULT random(); "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingFieldWithDefault)); } #[test] @@ -196,9 +190,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" boolean DEFAULT random(); ALTER TABLE "core_recipe" ADD COLUMN "foo" boolean DEFAULT true; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -211,9 +203,7 @@ alter table t add column b bigint[] default cast(array[] as bigint[]); alter table t add column c text[] default array['foo', 'bar']::text[]; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -223,9 +213,7 @@ ALTER TABLE assessments ADD COLUMN statistics_last_updated_at timestamptz NOT NULL DEFAULT now() - interval '100 years'; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -235,9 +223,7 @@ ADD COLUMN statistics_last_updated_at timestamptz NOT NULL DEFAULT now() - inter ALTER TABLE "core_recipe" ADD COLUMN "foo" text DEFAULT 'some-str'; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -247,9 +233,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" text DEFAULT 'some-str'; ALTER TABLE "core_recipe" ADD COLUMN "foo" some_enum_type DEFAULT 'my-enum-variant'; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -259,9 +243,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" some_enum_type DEFAULT 'my-enum-varia ALTER TABLE "core_recipe" ADD COLUMN "foo" jsonb DEFAULT '{}'::jsonb; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -271,9 +253,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" jsonb DEFAULT '{}'::jsonb; ALTER TABLE "core_recipe" ADD COLUMN "foo" jsonb DEFAULT myjsonb(); "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingFieldWithDefault)); } #[test] @@ -283,9 +263,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" jsonb DEFAULT myjsonb(); ALTER TABLE "core_recipe" ADD COLUMN "foo" timestamptz DEFAULT now(123); "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingFieldWithDefault)); } #[test] @@ -295,9 +273,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" timestamptz DEFAULT now(123); ALTER TABLE "core_recipe" ADD COLUMN "foo" timestamptz DEFAULT now(); "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -306,9 +282,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" timestamptz DEFAULT now(); alter table t add column c timestamptz default current_timestamp; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -317,9 +291,7 @@ alter table t add column c timestamptz default current_timestamp; alter table account_metadata add column blah integer default 2 + 2; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(errors.is_empty()); - assert_debug_snapshot!(errors); + lint_ok(sql, Rule::AddingFieldWithDefault); } #[test] @@ -329,9 +301,7 @@ ALTER TABLE foo ADD COLUMN bar numeric GENERATED ALWAYS AS (bar + baz) STORED; "#; - let errors = lint(sql, Rule::AddingFieldWithDefault); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingFieldWithDefault)); } #[test] @@ -341,8 +311,12 @@ ADD COLUMN bar numeric GENERATED ALWAYS AS (bar + baz) STORED; ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10; "#; - let errors = lint_with_postgres_version(sql, Rule::AddingFieldWithDefault, "11"); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors_with( + sql, + LinterSettings { + pg_version: "11".parse().expect("Invalid PostgreSQL version"), + ..Default::default() + }, + )); } } diff --git a/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs b/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs index bad2c758..b9b8ba0b 100644 --- a/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs +++ b/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs @@ -78,8 +78,18 @@ pub(crate) fn adding_foreign_key_constraint(ctx: &mut Linter, parse: &Parse String { + crate::test_utils::lint_errors_with(sql, settings, Rule::AddingForeignKeyConstraint) + } #[test] fn create_table_with_foreign_key_constraint() { @@ -91,14 +101,13 @@ mod test { email TEXT, PRIMARY KEY(id), CONSTRAINT fk_user - FOREIGN KEY ("user_id") + FOREIGN KEY ("user_id") REFERENCES "user" ("id") ); COMMIT; "#; - let errors = lint(sql, Rule::AddingForeignKeyConstraint); - assert!(errors.is_empty()); + lint_ok(sql, Rule::AddingForeignKeyConstraint); } #[test] @@ -108,8 +117,13 @@ CREATE TABLE "emails" ("id" UUID NOT NULL, "user_id" UUID NOT NULL); ALTER TABLE "emails" ADD CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES "users" ("id"); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::AddingForeignKeyConstraint); - assert!(errors.is_empty()); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -121,8 +135,7 @@ ALTER TABLE "emails" ADD CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES COMMIT; "#; - let errors = lint(sql, Rule::AddingForeignKeyConstraint); - assert!(errors.is_empty()); + lint_ok(sql, Rule::AddingForeignKeyConstraint); } #[test] @@ -135,8 +148,7 @@ ALTER TABLE "email" VALIDATE CONSTRAINT "fk_user"; COMMIT; "#; - let errors = lint(sql, Rule::AddingForeignKeyConstraint); - assert!(errors.is_empty()); + lint_ok(sql, Rule::AddingForeignKeyConstraint); } #[test] @@ -148,9 +160,7 @@ ALTER TABLE "email" ADD CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES COMMIT; "#; - let errors = lint(sql, Rule::AddingForeignKeyConstraint); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].code, Rule::AddingForeignKeyConstraint); + assert_snapshot!(lint_errors(sql, Rule::AddingForeignKeyConstraint)); } #[test] @@ -161,8 +171,6 @@ ALTER TABLE "emails" ADD COLUMN "user_id" INT REFERENCES "user" ("id"); COMMIT; "#; - let errors = lint(sql, Rule::AddingForeignKeyConstraint); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].code, Rule::AddingForeignKeyConstraint); + assert_snapshot!(lint_errors(sql, Rule::AddingForeignKeyConstraint)); } } diff --git a/crates/squawk_linter/src/rules/adding_not_null_field.rs b/crates/squawk_linter/src/rules/adding_not_null_field.rs index 0519aa24..bec72e1d 100644 --- a/crates/squawk_linter/src/rules/adding_not_null_field.rs +++ b/crates/squawk_linter/src/rules/adding_not_null_field.rs @@ -31,19 +31,17 @@ pub(crate) fn adding_not_null_field(ctx: &mut Linter, parse: &Parse) #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::{lint_errors, lint_ok}; #[test] fn set_not_null() { let sql = r#" ALTER TABLE "core_recipe" ALTER COLUMN "foo" SET NOT NULL; "#; - let errors = lint(sql, Rule::AddingNotNullableField); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingNotNullableField)); } #[test] @@ -56,8 +54,7 @@ ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10 NOT NULL; ALTER TABLE "core_recipe" ALTER COLUMN "foo" DROP DEFAULT; COMMIT; "#; - let errors = lint(sql, Rule::AddingNotNullableField); - assert!(errors.is_empty()); + lint_ok(sql, Rule::AddingNotNullableField); } #[test] @@ -66,8 +63,7 @@ COMMIT; -- This won't work if the table is populated, but that error is caught by adding-required-field. ALTER TABLE "core_recipe" ADD COLUMN "foo" integer NOT NULL; "#; - let errors = lint(sql, Rule::AddingNotNullableField); - assert!(errors.is_empty()); + lint_ok(sql, Rule::AddingNotNullableField); } #[test] @@ -80,8 +76,7 @@ BEGIN; ALTER TABLE "core_recipe" ADD COLUMN "foo" integer NOT NULL DEFAULT 10; COMMIT; "#; - let errors = lint(sql, Rule::AddingNotNullableField); - assert!(errors.is_empty()); + lint_ok(sql, Rule::AddingNotNullableField); } #[test] @@ -93,8 +88,6 @@ ALTER TABLE my_table ALTER COLUMN my_column SET NOT NULL; UPDATE alembic_version SET version_num='b' WHERE alembic_version.version_num = 'a'; COMMIT; "#; - let errors = lint(sql, Rule::AddingNotNullableField); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::AddingNotNullableField)); } } diff --git a/crates/squawk_linter/src/rules/adding_primary_key_constraint.rs b/crates/squawk_linter/src/rules/adding_primary_key_constraint.rs index caf289c4..be779088 100644 --- a/crates/squawk_linter/src/rules/adding_primary_key_constraint.rs +++ b/crates/squawk_linter/src/rules/adding_primary_key_constraint.rs @@ -56,19 +56,17 @@ pub(crate) fn adding_primary_key_constraint(ctx: &mut Linter, parse: &Parse 0); "#; - let errors = lint(sql, Rule::BanAlterDomainWithAddConstraint); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanAlterDomainWithAddConstraint)); } #[test] @@ -50,7 +48,6 @@ mod test { ALTER DOMAIN domain_name_7 OWNER TO you; ALTER DOMAIN domain_name_8 SET SCHEMA foo; "#; - let errors = lint(sql, Rule::BanAlterDomainWithAddConstraint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::BanAlterDomainWithAddConstraint); } } diff --git a/crates/squawk_linter/src/rules/ban_char_field.rs b/crates/squawk_linter/src/rules/ban_char_field.rs index 6c8eb750..12b28b5d 100644 --- a/crates/squawk_linter/src/rules/ban_char_field.rs +++ b/crates/squawk_linter/src/rules/ban_char_field.rs @@ -90,11 +90,11 @@ pub(crate) fn ban_char_field(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::{ Rule, - test_utils::{fix_sql, lint}, + test_utils::{fix_sql, lint_errors, lint_ok}, }; fn fix(sql: &str) -> String { @@ -154,9 +154,7 @@ CREATE TABLE "core_bar" ( "delta" character NOT NULL ); "#; - let errors = lint(sql, Rule::BanCharField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCharField)); } #[test] @@ -168,8 +166,7 @@ CREATE TABLE "core_bar" ( "beta" text NOT NULL ); "#; - let errors = lint(sql, Rule::BanCharField); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::BanCharField); } #[test] @@ -192,9 +189,7 @@ create table t ( p char[] ); "#; - let errors = lint(sql, Rule::BanCharField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCharField)); } #[test] @@ -204,9 +199,7 @@ create table t ( a Char ); "#; - let errors = lint(sql, Rule::BanCharField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCharField)); } #[test] @@ -216,9 +209,7 @@ create table t ( a char[] ); "#; - let errors = lint(sql, Rule::BanCharField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCharField)); } #[test] @@ -226,8 +217,6 @@ create table t ( let sql = r#" alter table t add column c char; "#; - let errors = lint(sql, Rule::BanCharField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCharField)); } } diff --git a/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs b/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs index aea8caea..14ff7f97 100644 --- a/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs +++ b/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs @@ -42,12 +42,26 @@ pub(crate) fn ban_concurrent_index_creation_in_transaction( #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; - use crate::{ - Rule, - test_utils::{lint, lint_with_assume_in_transaction}, - }; + use crate::{LinterSettings, Rule}; + use crate::test_utils::{lint_ok, lint_errors}; + + fn lint_ok_with(sql: &str, settings: LinterSettings) { + crate::test_utils::lint_ok_with( + sql, + settings, + Rule::BanConcurrentIndexCreationInTransaction, + ); + } + + fn lint_errors_with(sql: &str, settings: LinterSettings) { + crate::test_utils::lint_errors_with( + sql, + settings, + Rule::BanConcurrentIndexCreationInTransaction, + ); + } #[test] fn ban_concurrent_index_creation_in_transaction_err() { @@ -57,9 +71,7 @@ mod test { CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); COMMIT; "#; - let errors = lint(sql, Rule::BanConcurrentIndexCreationInTransaction); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanConcurrentIndexCreationInTransaction)); } #[test] @@ -68,8 +80,7 @@ mod test { -- run outside a transaction CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); "#; - let errors = lint(sql, Rule::BanConcurrentIndexCreationInTransaction); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::BanConcurrentIndexCreationInTransaction); } #[test] @@ -79,10 +90,13 @@ mod test { CREATE UNIQUE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); ALTER TABLE "table_name" ADD CONSTRAINT "field_name_id" UNIQUE USING INDEX "field_name_idx"; "#; - let errors = - lint_with_assume_in_transaction(sql, Rule::BanConcurrentIndexCreationInTransaction); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + lint_errors_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -91,9 +105,13 @@ mod test { -- run index creation in a standalone migration CREATE UNIQUE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); "#; - let errors = - lint_with_assume_in_transaction(sql, Rule::BanConcurrentIndexCreationInTransaction); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -105,8 +123,12 @@ mod test { BEGIN; ALTER TABLE "table_name" ADD CONSTRAINT "field_name_id" UNIQUE USING INDEX "field_name_idx"; "#; - let errors = - lint_with_assume_in_transaction(sql, Rule::BanConcurrentIndexCreationInTransaction); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } } diff --git a/crates/squawk_linter/src/rules/ban_create_domain_with_constraint.rs b/crates/squawk_linter/src/rules/ban_create_domain_with_constraint.rs index 144999b6..cba4823a 100644 --- a/crates/squawk_linter/src/rules/ban_create_domain_with_constraint.rs +++ b/crates/squawk_linter/src/rules/ban_create_domain_with_constraint.rs @@ -35,19 +35,17 @@ pub(crate) fn ban_create_domain_with_constraint(ctx: &mut Linter, parse: &Parse< #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::{lint_errors, lint_ok}; #[test] fn err() { let sql = r#" CREATE DOMAIN domain_name_3 AS NUMERIC(15,5) CHECK (value > 0); "#; - let errors = lint(sql, Rule::BanCreateDomainWithConstraint); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCreateDomainWithConstraint)); } #[test] @@ -56,9 +54,7 @@ CREATE DOMAIN domain_name_3 AS NUMERIC(15,5) CHECK (value > 0); let sql = r#" create domain d as t check (value > 0) not null; "#; - let errors = lint(sql, Rule::BanCreateDomainWithConstraint); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanCreateDomainWithConstraint)); } #[test] @@ -68,7 +64,6 @@ create domain d as t check (value > 0) not null; CREATE DOMAIN domain_name_1 AS TEXT; CREATE DOMAIN domain_name_2 AS CHARACTER VARYING; "#; - let errors = lint(sql, Rule::BanCreateDomainWithConstraint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::BanCreateDomainWithConstraint); } } diff --git a/crates/squawk_linter/src/rules/ban_drop_column.rs b/crates/squawk_linter/src/rules/ban_drop_column.rs index 12619e16..0b6bf474 100644 --- a/crates/squawk_linter/src/rules/ban_drop_column.rs +++ b/crates/squawk_linter/src/rules/ban_drop_column.rs @@ -24,18 +24,16 @@ pub(crate) fn ban_drop_column(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn err() { let sql = r#" ALTER TABLE "bar_tbl" DROP COLUMN "foo_col" CASCADE; "#; - let errors = lint(sql, Rule::BanDropColumn); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanDropColumn)); } } diff --git a/crates/squawk_linter/src/rules/ban_drop_database.rs b/crates/squawk_linter/src/rules/ban_drop_database.rs index 5b6b78a4..83744546 100644 --- a/crates/squawk_linter/src/rules/ban_drop_database.rs +++ b/crates/squawk_linter/src/rules/ban_drop_database.rs @@ -21,10 +21,10 @@ pub(crate) fn ban_drop_database(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn ban_drop_database() { @@ -33,8 +33,6 @@ mod test { DROP DATABASE IF EXISTS "table_name"; DROP DATABASE IF EXISTS "table_name" "#; - let errors = lint(sql, Rule::BanDropDatabase); - assert!(!errors.is_empty()); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanDropDatabase)); } } diff --git a/crates/squawk_linter/src/rules/ban_drop_not_null.rs b/crates/squawk_linter/src/rules/ban_drop_not_null.rs index 941ba119..07274442 100644 --- a/crates/squawk_linter/src/rules/ban_drop_not_null.rs +++ b/crates/squawk_linter/src/rules/ban_drop_not_null.rs @@ -28,18 +28,16 @@ pub(crate) fn ban_drop_not_null(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn err() { let sql = r#" ALTER TABLE "bar_tbl" ALTER COLUMN "foo_col" DROP NOT NULL; "#; - let errors = lint(sql, Rule::BanDropNotNull); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanDropNotNull)); } } diff --git a/crates/squawk_linter/src/rules/ban_drop_table.rs b/crates/squawk_linter/src/rules/ban_drop_table.rs index de06b167..250e3ea5 100644 --- a/crates/squawk_linter/src/rules/ban_drop_table.rs +++ b/crates/squawk_linter/src/rules/ban_drop_table.rs @@ -20,10 +20,10 @@ pub(crate) fn ban_drop_table(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn err() { @@ -32,8 +32,6 @@ DROP TABLE "table_name"; DROP TABLE IF EXISTS "table_name"; DROP TABLE IF EXISTS "table_name" "#; - let errors = lint(sql, Rule::BanDropTable); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanDropTable)); } } diff --git a/crates/squawk_linter/src/rules/ban_truncate_cascade.rs b/crates/squawk_linter/src/rules/ban_truncate_cascade.rs index 44ce1976..a3c64fb2 100644 --- a/crates/squawk_linter/src/rules/ban_truncate_cascade.rs +++ b/crates/squawk_linter/src/rules/ban_truncate_cascade.rs @@ -22,19 +22,17 @@ pub(crate) fn ban_truncate_cascade(ctx: &mut Linter, parse: &Parse) #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::{lint_errors, lint_ok}; #[test] fn err() { let sql = r#" truncate a, b, c cascade; "#; - let errors = lint(sql, Rule::BanTruncateCascade); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::BanTruncateCascade)); } #[test] @@ -43,7 +41,6 @@ mod test { truncate a, b, c; truncate a; "#; - let errors = lint(sql, Rule::BanTruncateCascade); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::BanTruncateCascade); } } diff --git a/crates/squawk_linter/src/rules/ban_uncommitted_transaction.rs b/crates/squawk_linter/src/rules/ban_uncommitted_transaction.rs index a1007d56..2f29b9f4 100644 --- a/crates/squawk_linter/src/rules/ban_uncommitted_transaction.rs +++ b/crates/squawk_linter/src/rules/ban_uncommitted_transaction.rs @@ -39,10 +39,12 @@ pub(crate) fn ban_uncommitted_transaction(ctx: &mut Linter, parse: &Parse) #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn err() { @@ -41,9 +41,7 @@ BEGIN; ALTER TABLE "core_recipe" ALTER COLUMN "edits" TYPE text USING "edits"::text; COMMIT; "#; - let errors = lint(sql, Rule::ChangingColumnType); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::ChangingColumnType)); } #[test] @@ -57,8 +55,6 @@ ALTER TABLE "core_recipe" ALTER COLUMN "foo" TYPE varchar(255) USING "foo"::varc ALTER TABLE "core_recipe" ALTER COLUMN "foo" TYPE text USING "foo"::text; COMMIT; "#; - let errors = lint(sql, Rule::ChangingColumnType); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::ChangingColumnType)); } } diff --git a/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs b/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs index 0cb7c7ef..96e84b7d 100644 --- a/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs +++ b/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs @@ -146,12 +146,18 @@ pub(crate) fn constraint_missing_not_valid(ctx: &mut Linter, parse: &Parse= 0); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::ConstraintMissingNotValid); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + lint_errors_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -238,8 +250,7 @@ ALTER TABLE "accounts" ADD CONSTRAINT "positive_balance" CHECK ("balance" >= 0); ALTER TABLE "accounts" ADD CONSTRAINT "positive_balance" CHECK ("balance" >= 0) NOT VALID; ALTER TABLE accounts VALIDATE CONSTRAINT positive_balance; "#; - let errors = lint(sql, Rule::ConstraintMissingNotValid); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::ConstraintMissingNotValid); } #[test] @@ -253,8 +264,7 @@ CREATE TABLE "core_foo" ( ALTER TABLE "core_foo" ADD CONSTRAINT "age_restriction" CHECK ("age" >= 25); COMMIT; "#; - let errors = lint(sql, Rule::ConstraintMissingNotValid); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::ConstraintMissingNotValid); } #[test] @@ -266,8 +276,13 @@ CREATE TABLE "core_foo" ( ); ALTER TABLE "core_foo" ADD CONSTRAINT "age_restriction" CHECK ("age" >= 25); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::ConstraintMissingNotValid); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -279,8 +294,13 @@ CREATE TABLE "core_foo" ( ); ALTER TABLE "core_foo" ADD CONSTRAINT "age_restriction" CHECK ("age" >= 25); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::ConstraintMissingNotValid); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -288,7 +308,6 @@ ALTER TABLE "core_foo" ADD CONSTRAINT "age_restriction" CHECK ("age" >= 25); let sql = r#" ALTER TABLE "app_email" ADD CONSTRAINT "email_uniq" UNIQUE USING INDEX "email_idx"; "#; - let errors = lint(sql, Rule::ConstraintMissingNotValid); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::ConstraintMissingNotValid); } } diff --git a/crates/squawk_linter/src/rules/disallow_unique_constraint.rs b/crates/squawk_linter/src/rules/disallow_unique_constraint.rs index c556f093..f16fa5a0 100644 --- a/crates/squawk_linter/src/rules/disallow_unique_constraint.rs +++ b/crates/squawk_linter/src/rules/disallow_unique_constraint.rs @@ -68,21 +68,25 @@ pub(crate) fn disallow_unique_constraint(ctx: &mut Linter, parse: &Parse String { + crate::test_utils::lint_errors_with(sql, settings, Rule::DisallowedUniqueConstraint) + } #[test] fn adding_unique_constraint_err() { let sql = r#" ALTER TABLE table_name ADD CONSTRAINT field_name_constraint UNIQUE (field_name); "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::DisallowedUniqueConstraint)); } #[test] @@ -90,8 +94,7 @@ ALTER TABLE table_name ADD CONSTRAINT field_name_constraint UNIQUE (field_name); let sql = r#" ALTER TABLE table_name DROP CONSTRAINT field_name_constraint; "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::DisallowedUniqueConstraint); } #[test] @@ -101,8 +104,7 @@ CREATE UNIQUE INDEX CONCURRENTLY dist_id_temp_idx ON distributors (dist_id); ALTER TABLE distributors DROP CONSTRAINT distributors_pkey, ADD CONSTRAINT distributors_pkey PRIMARY KEY USING INDEX dist_id_temp_idx; "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::DisallowedUniqueConstraint); } #[test] @@ -112,8 +114,7 @@ CREATE UNIQUE INDEX CONCURRENTLY "legacy_questiongrouppg_mongo_id_1f8f47d9_uniq_ ON "legacy_questiongrouppg" ("mongo_id"); ALTER TABLE "legacy_questiongrouppg" ADD CONSTRAINT "legacy_questiongrouppg_mongo_id_1f8f47d9_uniq" UNIQUE USING INDEX "legacy_questiongrouppg_mongo_id_1f8f47d9_uniq_idx"; "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::DisallowedUniqueConstraint); } #[test] @@ -127,8 +128,7 @@ CREATE TABLE products ( ALTER TABLE products ADD CONSTRAINT sku_constraint UNIQUE (sku); COMMIT; "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::DisallowedUniqueConstraint); } #[test] @@ -140,8 +140,13 @@ CREATE TABLE products ( ); ALTER TABLE products ADD CONSTRAINT sku_constraint UNIQUE (sku); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::DisallowedUniqueConstraint); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -149,9 +154,7 @@ ALTER TABLE products ADD CONSTRAINT sku_constraint UNIQUE (sku); let sql = r#" ALTER TABLE foo ADD COLUMN bar text CONSTRAINT foo_bar_unique UNIQUE; "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::DisallowedUniqueConstraint)); } #[test] @@ -159,8 +162,6 @@ ALTER TABLE foo ADD COLUMN bar text CONSTRAINT foo_bar_unique UNIQUE; let sql = r#" ALTER TABLE foo ADD COLUMN bar text UNIQUE; "#; - let errors = lint(sql, Rule::DisallowedUniqueConstraint); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::DisallowedUniqueConstraint)); } } diff --git a/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs b/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs index 27c1e42a..4582e02b 100644 --- a/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs +++ b/crates/squawk_linter/src/rules/prefer_bigint_over_int.rs @@ -66,11 +66,11 @@ pub(crate) fn prefer_bigint_over_int(ctx: &mut Linter, parse: &Parse #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::{ Rule, - test_utils::{fix_sql, lint}, + test_utils::{fix_sql, lint_errors, lint_ok}, }; fn fix(sql: &str) -> String { @@ -133,17 +133,7 @@ create table users ( id serial4 ); "#; - let errors = lint(sql, Rule::PreferBigintOverInt); - assert_ne!(errors.len(), 0); - assert_eq!(errors.len(), 5); - assert_eq!( - errors - .iter() - .filter(|x| x.code == Rule::PreferBigintOverInt) - .count(), - 5 - ); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferBigintOverInt)); } #[test] @@ -174,7 +164,6 @@ create table users ( id serial2 ); "#; - let errors = lint(sql, Rule::PreferBigintOverInt); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferBigintOverInt); } } diff --git a/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs b/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs index 91da67d3..d4bc6dcc 100644 --- a/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs +++ b/crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs @@ -64,11 +64,11 @@ pub(crate) fn prefer_bigint_over_smallint(ctx: &mut Linter, parse: &Parse String { @@ -125,17 +125,7 @@ create table users ( id serial2 ); "#; - let errors = lint(sql, Rule::PreferBigintOverSmallint); - assert_ne!(errors.len(), 0); - assert_eq!(errors.len(), 4); - assert_eq!( - errors - .iter() - .filter(|x| x.code == Rule::PreferBigintOverSmallint) - .count(), - 4 - ); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferBigintOverSmallint)); } #[test] @@ -166,7 +156,6 @@ create table users ( id serial4 ); "#; - let errors = lint(sql, Rule::PreferBigintOverSmallint); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferBigintOverSmallint); } } diff --git a/crates/squawk_linter/src/rules/prefer_identity.rs b/crates/squawk_linter/src/rules/prefer_identity.rs index 60ce0a3c..ef5bd76a 100644 --- a/crates/squawk_linter/src/rules/prefer_identity.rs +++ b/crates/squawk_linter/src/rules/prefer_identity.rs @@ -65,11 +65,11 @@ pub(crate) fn prefer_identity(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::{ Rule, - test_utils::{fix_sql, lint}, + test_utils::{fix_sql, lint_errors, lint_ok}, }; fn fix(sql: &str) -> String { @@ -117,17 +117,7 @@ create table users ( id BIGSERIAL ); "#; - let errors = lint(sql, Rule::PreferIdentity); - assert_ne!(errors.len(), 0); - assert_eq!(errors.len(), 7); - assert_eq!( - errors - .iter() - .filter(|x| x.code == Rule::PreferIdentity) - .count(), - 7 - ); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferIdentity)); } #[test] @@ -140,9 +130,7 @@ create table users ( id "bigserial" ); "#; - let errors = lint(sql, Rule::PreferIdentity); - assert_eq!(errors.len(), 2); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferIdentity)); } #[test] @@ -155,7 +143,6 @@ create table users ( id bigint generated always as identity primary key ); "#; - let errors = lint(sql, Rule::PreferIdentity); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferIdentity); } } diff --git a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs index f1a1db99..560a1dfa 100644 --- a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs +++ b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs @@ -231,15 +231,28 @@ pub(crate) fn prefer_robust_stmts(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; - use crate::{ - Rule, - test_utils::{fix_sql, lint, lint_with_assume_in_transaction}, - }; + use crate::{LinterSettings, Rule}; fn fix(sql: &str) -> String { - fix_sql(sql, Rule::PreferRobustStmts) + crate::test_utils::fix_sql(sql, Rule::PreferRobustStmts) + } + + fn lint_errors(sql: &str) -> String { + crate::test_utils::lint_errors(sql, Rule::PreferRobustStmts) + } + + fn lint_ok(sql: &str) { + crate::test_utils::lint_ok(sql, Rule::PreferRobustStmts) + } + + fn lint_ok_with(sql: &str, settings: LinterSettings) { + crate::test_utils::lint_ok_with(sql, settings, Rule::PreferRobustStmts); + } + + fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { + crate::test_utils::lint_errors_with(sql, settings, Rule::PreferRobustStmts) } #[test] @@ -336,8 +349,7 @@ ALTER TABLE users DROP CONSTRAINT pk_users; ALTER TABLE "app_email" DROP CONSTRAINT IF EXISTS "email_uniq"; ALTER TABLE "app_email" ADD CONSTRAINT "email_uniq" UNIQUE USING INDEX "email_idx"; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -346,8 +358,7 @@ ALTER TABLE "app_email" ADD CONSTRAINT "email_uniq" UNIQUE USING INDEX "email_id select 1; -- so we don't skip checking DROP INDEX CONCURRENTLY IF EXISTS "email_idx"; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -357,8 +368,7 @@ ALTER TABLE "app_email" DROP CONSTRAINT IF EXISTS "fk_user"; ALTER TABLE "app_email" ADD CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES "app_user" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID; ALTER TABLE "app_email" VALIDATE CONSTRAINT "fk_user"; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -368,8 +378,7 @@ BEGIN; ALTER TABLE "core_foo" ADD COLUMN "answer_id" integer NULL; COMMIT; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -378,8 +387,7 @@ COMMIT; select 1; -- so we don't skip checking ALTER TABLE "core_foo" ADD COLUMN IF NOT EXISTS "answer_id" integer NULL; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -388,8 +396,7 @@ ALTER TABLE "core_foo" ADD COLUMN IF NOT EXISTS "answer_id" integer NULL; select 1; -- so we don't skip checking CREATE INDEX CONCURRENTLY IF NOT EXISTS "core_foo_idx" ON "core_foo" ("answer_id"); "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -402,8 +409,7 @@ CREATE TABLE "core_bar" ( ); COMMIT; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -414,8 +420,7 @@ CREATE TABLE IF NOT EXISTS "core_bar" ( "bravo" text NOT NULL ); "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -428,8 +433,7 @@ DROP TABLE "core_bar"; DROP TYPE foo; COMMIT; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -439,8 +443,7 @@ COMMIT; select 1; -- so we don't skip checking SELECT 1; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -450,8 +453,7 @@ SELECT 1; select 1; -- so we don't skip checking INSERT INTO tbl VALUES (a); "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -461,8 +463,7 @@ INSERT INTO tbl VALUES (a); select 1; -- so we don't skip checking ALTER TABLE "core_foo" DROP CONSTRAINT IF EXISTS "core_foo_idx"; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -471,8 +472,13 @@ ALTER TABLE "core_foo" DROP CONSTRAINT IF EXISTS "core_foo_idx"; select 1; -- so we don't skip checking ALTER TABLE "core_foo" ADD COLUMN "answer_id" integer NULL; "#; - let errors = lint_with_assume_in_transaction(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -482,8 +488,13 @@ DROP INDEX "core_bar_foo_id_idx"; DROP TABLE "core_bar"; DROP TYPE foo; "#; - let errors = lint_with_assume_in_transaction(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -494,8 +505,13 @@ CREATE TABLE "core_bar" ( "bravo" text NOT NULL ); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -505,8 +521,13 @@ CREATE TABLE "core_bar" ( let sql = r#" CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -515,8 +536,13 @@ CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] @@ -534,41 +560,43 @@ ALTER TABLE "D" DROP CONSTRAINT "UQ_468cad3743146a81c94b0b114ac"; COMMIT; "#; - let errors = lint_with_assume_in_transaction(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } #[test] fn alter_table_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking ALTER TABLE "core_foo" ADD COLUMN "answer_id" integer NULL; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn create_index_concurrently_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking CREATE INDEX CONCURRENTLY "core_foo_idx" ON "core_foo" ("answer_id"); - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn alter_table_drop_column_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking alter table t drop column c cascade; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] @@ -577,30 +605,27 @@ alter table t drop column c cascade; select 1; -- so we don't skip checking alter table t drop column if exists c cascade; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] fn create_table_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking CREATE TABLE "core_bar" ( "id" serial NOT NULL PRIMARY KEY, "bravo" text NOT NULL); - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn alter_table_drop_constraint_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking ALTER TABLE "core_foo" DROP CONSTRAINT "core_foo_idx"; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] @@ -609,76 +634,69 @@ ALTER TABLE "core_foo" DROP CONSTRAINT "core_foo_idx"; select 1; -- so we don't skip checking CREATE INDEX CONCURRENTLY ON "table_name" ("field_name"); "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] fn enable_row_level_security_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" CREATE TABLE IF NOT EXISTS test(); ALTER TABLE IF EXISTS test ENABLE ROW LEVEL SECURITY; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn enable_row_level_security_without_exists_check_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" CREATE TABLE IF NOT EXISTS test(); ALTER TABLE test ENABLE ROW LEVEL SECURITY; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn disable_row_level_security_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" CREATE TABLE IF NOT EXISTS test(); ALTER TABLE IF EXISTS test DISABLE ROW LEVEL SECURITY; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn double_add_after_drop_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" ALTER TABLE "app_email" DROP CONSTRAINT IF EXISTS "email_uniq"; ALTER TABLE "app_email" ADD CONSTRAINT "email_uniq" UNIQUE USING INDEX "email_idx"; -- this second add constraint should error because it's not robust ALTER TABLE "app_email" ADD CONSTRAINT "email_uniq" UNIQUE USING INDEX "email_idx"; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn alter_column_set_not_null() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking alter table t alter column c set not null; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn drop_index_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking DROP INDEX CONCURRENTLY "email_idx"; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] @@ -687,8 +705,7 @@ DROP INDEX CONCURRENTLY "email_idx"; select 1; -- so we don't skip checking CREATE TEMP TABLE test_table (id int) ON COMMIT DROP; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] @@ -697,29 +714,26 @@ CREATE TEMP TABLE test_table (id int) ON COMMIT DROP; select 1; -- so we don't skip checking CREATE TEMPORARY TABLE test_table (id int) ON COMMIT DROP; "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_eq!(errors.len(), 0); + lint_ok(sql); } #[test] fn create_temp_table_without_on_commit_drop_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking CREATE TEMP TABLE test_table (id int); - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } #[test] fn create_table_with_on_commit_drop_err() { - let sql = r#" + assert_snapshot!(lint_errors( + r#" select 1; -- so we don't skip checking CREATE TABLE test_table (id int) ON COMMIT DROP; - "#; - let errors = lint(sql, Rule::PreferRobustStmts); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + "# + )); } } diff --git a/crates/squawk_linter/src/rules/prefer_text_field.rs b/crates/squawk_linter/src/rules/prefer_text_field.rs index 0cb6364c..ea4547f0 100644 --- a/crates/squawk_linter/src/rules/prefer_text_field.rs +++ b/crates/squawk_linter/src/rules/prefer_text_field.rs @@ -93,11 +93,11 @@ pub(crate) fn prefer_text_field(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::{ Rule, - test_utils::{fix_sql, lint}, + test_utils::{fix_sql, lint_errors, lint_ok}, }; fn fix(sql: &str) -> String { @@ -116,9 +116,7 @@ BEGIN; ALTER TABLE "core_foo" ALTER COLUMN "kind" TYPE varchar(1000) USING "kind"::varchar(1000); COMMIT; "#; - let errors = lint(sql, Rule::PreferTextField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferTextField)); } #[test] @@ -129,27 +127,23 @@ BEGIN; -- Create model Bar -- CREATE TABLE "core_bar" ( - "id" serial NOT NULL PRIMARY KEY, + "id" serial NOT NULL PRIMARY KEY, "alpha" varchar(100) NOT NULL ); COMMIT; "#; - let errors = lint(sql, Rule::PreferTextField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferTextField)); } #[test] fn create_table_with_pgcatalog_varchar_err() { let sql = r#" create table t ( - "id" serial NOT NULL PRIMARY KEY, + "id" serial NOT NULL PRIMARY KEY, "alpha" pg_catalog.varchar(100) NOT NULL ); "#; - let errors = lint(sql, Rule::PreferTextField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferTextField)); } #[test] @@ -159,9 +153,7 @@ BEGIN; ALTER TABLE "foo_table" ADD COLUMN "foo_column" varchar(256) NULL; COMMIT; "#; - let errors = lint(sql, Rule::PreferTextField); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferTextField)); } #[test] @@ -169,8 +161,7 @@ COMMIT; let sql = r#" CREATE TABLE IF NOT EXISTS foo_table(bar_col varchar); "#; - let errors = lint(sql, Rule::PreferTextField); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferTextField); } #[test] @@ -181,7 +172,7 @@ BEGIN; -- Create model Bar -- CREATE TABLE "core_bar" ( - "id" serial NOT NULL PRIMARY KEY, + "id" serial NOT NULL PRIMARY KEY, "bravo" text NOT NULL ); -- @@ -190,8 +181,7 @@ CREATE TABLE "core_bar" ( ALTER TABLE "core_bar" ADD CONSTRAINT "text_size" CHECK (LENGTH("bravo") <= 100); COMMIT; "#; - let errors = lint(sql, Rule::PreferTextField); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferTextField); } #[test] diff --git a/crates/squawk_linter/src/rules/prefer_timestamptz.rs b/crates/squawk_linter/src/rules/prefer_timestamptz.rs index 90512d19..b974dbe4 100644 --- a/crates/squawk_linter/src/rules/prefer_timestamptz.rs +++ b/crates/squawk_linter/src/rules/prefer_timestamptz.rs @@ -84,10 +84,10 @@ pub(crate) fn prefer_timestamptz(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::{fix_sql, lint}; + use crate::test_utils::{fix_sql, lint_errors, lint_ok}; fn fix(sql: &str) -> String { fix_sql(sql, Rule::PreferTimestampTz) @@ -161,9 +161,7 @@ create table app.accounts created_ts timestamp without time zone ); "#; - let errors = lint(sql, Rule::PreferTimestampTz); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferTimestampTz)); } #[test] @@ -174,9 +172,7 @@ alter table app.users alter table app.accounts alter column created_ts type timestamp without time zone; "#; - let errors = lint(sql, Rule::PreferTimestampTz); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::PreferTimestampTz)); } #[test] @@ -191,8 +187,7 @@ create table app.accounts created_ts timestamp with time zone ); "#; - let errors = lint(sql, Rule::PreferTimestampTz); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferTimestampTz); } #[test] @@ -207,7 +202,6 @@ create table app.accounts created_ts timestamp with time zone ); "#; - let errors = lint(sql, Rule::PreferTimestampTz); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::PreferTimestampTz); } } diff --git a/crates/squawk_linter/src/rules/renaming_column.rs b/crates/squawk_linter/src/rules/renaming_column.rs index 49a58ee8..0445f697 100644 --- a/crates/squawk_linter/src/rules/renaming_column.rs +++ b/crates/squawk_linter/src/rules/renaming_column.rs @@ -24,18 +24,16 @@ pub(crate) fn renaming_column(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn err() { let sql = r#" ALTER TABLE "table_name" RENAME COLUMN "column_name" TO "new_column_name"; "#; - let errors = lint(sql, Rule::RenamingColumn); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::RenamingColumn)); } } diff --git a/crates/squawk_linter/src/rules/renaming_table.rs b/crates/squawk_linter/src/rules/renaming_table.rs index 07e21bb6..3677373d 100644 --- a/crates/squawk_linter/src/rules/renaming_table.rs +++ b/crates/squawk_linter/src/rules/renaming_table.rs @@ -24,18 +24,16 @@ pub(crate) fn renaming_table(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; use crate::Rule; - use crate::test_utils::lint; + use crate::test_utils::lint_errors; #[test] fn err() { let sql = r#" ALTER TABLE "table_name" RENAME TO "new_table_name"; "#; - let errors = lint(sql, Rule::RenamingTable); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::RenamingTable)); } } diff --git a/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs b/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs index 3fd5df05..a8c1475d 100644 --- a/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs +++ b/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs @@ -46,13 +46,21 @@ pub(crate) fn require_concurrent_index_creation(ctx: &mut Linter, parse: &Parse< #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::{ - Rule, - test_utils::{fix_sql, lint, lint_with_assume_in_transaction}, + LinterSettings, Rule, + test_utils::{fix_sql, lint_ok, lint_errors}, }; + fn lint_ok_with(sql: &str, settings: LinterSettings) { + crate::test_utils::lint_ok_with(sql, settings, Rule::RequireConcurrentIndexCreation); + } + + fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { + crate::test_utils::lint_errors_with(sql, settings, Rule::RequireConcurrentIndexCreation) + } + fn fix(sql: &str) -> String { fix_sql(sql, Rule::RequireConcurrentIndexCreation) } @@ -81,9 +89,7 @@ CREATE INDEX ON t (a); -- instead of CREATE INDEX "field_name_idx" ON "table_name" ("field_name"); "#; - let errors = lint(sql, Rule::RequireConcurrentIndexCreation); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::RequireConcurrentIndexCreation)); } #[test] @@ -92,8 +98,7 @@ CREATE INDEX "field_name_idx" ON "table_name" ("field_name"); -- use CONCURRENTLY CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); "#; - let errors = lint(sql, Rule::RequireConcurrentIndexCreation); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireConcurrentIndexCreation); } #[test] @@ -107,8 +112,7 @@ CREATE TABLE "core_foo" ( CREATE INDEX "core_foo_tenant_id_4d397ef9" ON "core_foo" ("tenant_id"); COMMIT; "#; - let errors = lint(sql, Rule::RequireConcurrentIndexCreation); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireConcurrentIndexCreation); } #[test] @@ -120,7 +124,12 @@ CREATE TABLE "core_foo" ( ); CREATE INDEX "core_foo_tenant_id_4d397ef9" ON "core_foo" ("tenant_id"); "#; - let errors = lint_with_assume_in_transaction(sql, Rule::RequireConcurrentIndexCreation); - assert_eq!(errors.len(), 0); + lint_ok_with( + sql, + LinterSettings { + assume_in_transaction: true, + ..Default::default() + }, + ); } } diff --git a/crates/squawk_linter/src/rules/require_concurrent_index_deletion.rs b/crates/squawk_linter/src/rules/require_concurrent_index_deletion.rs index 257f8a56..e48e65c8 100644 --- a/crates/squawk_linter/src/rules/require_concurrent_index_deletion.rs +++ b/crates/squawk_linter/src/rules/require_concurrent_index_deletion.rs @@ -31,11 +31,11 @@ pub(crate) fn require_concurrent_index_deletion(ctx: &mut Linter, parse: &Parse< #[cfg(test)] mod test { - use insta::{assert_debug_snapshot, assert_snapshot}; + use insta::assert_snapshot; use crate::{ Rule, - test_utils::{fix_sql, lint}, + test_utils::{fix_sql, lint_errors, lint_ok}, }; fn fix(sql: &str) -> String { @@ -69,10 +69,7 @@ mod test { -- instead of DROP INDEX IF EXISTS "field_name_idx"; "#; - let errors = lint(sql, Rule::RequireConcurrentIndexDeletion); - assert_eq!(errors.len(), 1); - assert_eq!(errors[0].code, Rule::RequireConcurrentIndexDeletion); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::RequireConcurrentIndexDeletion)); } #[test] @@ -80,8 +77,7 @@ mod test { let sql = r#" DROP INDEX CONCURRENTLY IF EXISTS "field_name_idx"; "#; - let errors = lint(sql, Rule::RequireConcurrentIndexDeletion); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireConcurrentIndexDeletion); } #[test] @@ -89,8 +85,7 @@ DROP INDEX CONCURRENTLY IF EXISTS "field_name_idx"; let sql = r#" DROP INDEX CONCURRENTLY IF EXISTS "field_name_idx"; "#; - let errors = lint(sql, Rule::RequireConcurrentIndexDeletion); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireConcurrentIndexDeletion); } #[test] @@ -98,8 +93,7 @@ DROP INDEX CONCURRENTLY IF EXISTS "field_name_idx"; let sql = r#" DROP TABLE IF EXISTS some_table; "#; - let errors = lint(sql, Rule::RequireConcurrentIndexDeletion); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireConcurrentIndexDeletion); } #[test] @@ -107,7 +101,6 @@ DROP TABLE IF EXISTS some_table; let sql = r#" DROP TRIGGER IF EXISTS trigger on foo_table; "#; - let errors = lint(sql, Rule::RequireConcurrentIndexDeletion); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireConcurrentIndexDeletion); } } diff --git a/crates/squawk_linter/src/rules/require_timeout_settings.rs b/crates/squawk_linter/src/rules/require_timeout_settings.rs index 8044fa5c..5404d3b8 100644 --- a/crates/squawk_linter/src/rules/require_timeout_settings.rs +++ b/crates/squawk_linter/src/rules/require_timeout_settings.rs @@ -106,11 +106,11 @@ pub(crate) fn require_timeout_settings(ctx: &mut Linter, parse: &Parse String { @@ -122,54 +122,26 @@ mod test { let sql = r#" ALTER TABLE t ADD COLUMN c BOOLEAN; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors, @r#" - [ - Violation { - code: RequireTimeoutSettings, - message: "Missing `set lock_timeout` before potentially slow operations", - text_range: 1..35, - help: Some( - "Configure a `lock_timeout` before this statement.", - ), - fix: Some( - Fix { - title: "Add lock timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set lock_timeout = '1s';\n", - ), - }, - ], - }, - ), - }, - Violation { - code: RequireTimeoutSettings, - message: "Missing `set statement_timeout` before potentially slow operations", - text_range: 1..35, - help: Some( - "Configure a `statement_timeout` before this statement", - ), - fix: Some( - Fix { - title: "Add statement timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set statement_timeout = '5s';\n", - ), - }, - ], - }, - ), - }, - ] - "#); + assert_snapshot!(lint_errors(sql, Rule::RequireTimeoutSettings), @r" + error[RequireTimeoutSettings]: Missing `set lock_timeout` before potentially slow operations + ╭▸ + 2 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `lock_timeout` before this statement. + ╭╴ + 2 + set lock_timeout = '1s'; + ╰╴ + error[RequireTimeoutSettings]: Missing `set statement_timeout` before potentially slow operations + ╭▸ + 2 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `statement_timeout` before this statement + ╭╴ + 2 + set statement_timeout = '5s'; + ╰╴ + "); } #[test] @@ -178,33 +150,17 @@ ALTER TABLE t ADD COLUMN c BOOLEAN; SET statement_timeout = '5s'; ALTER TABLE t ADD COLUMN c BOOLEAN; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors, @r#" - [ - Violation { - code: RequireTimeoutSettings, - message: "Missing `set lock_timeout` before potentially slow operations", - text_range: 31..65, - help: Some( - "Configure a `lock_timeout` before this statement.", - ), - fix: Some( - Fix { - title: "Add lock timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set lock_timeout = '1s';\n", - ), - }, - ], - }, - ), - }, - ] - "#); + assert_snapshot!(lint_errors(sql, Rule::RequireTimeoutSettings), @r" + error[RequireTimeoutSettings]: Missing `set lock_timeout` before potentially slow operations + ╭▸ + 3 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `lock_timeout` before this statement. + ╭╴ + 2 + set lock_timeout = '1s'; + ╰╴ + "); } #[test] @@ -213,33 +169,17 @@ ALTER TABLE t ADD COLUMN c BOOLEAN; SET lock_timeout = '1s'; ALTER TABLE t ADD COLUMN c BOOLEAN; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors, @r#" - [ - Violation { - code: RequireTimeoutSettings, - message: "Missing `set statement_timeout` before potentially slow operations", - text_range: 26..60, - help: Some( - "Configure a `statement_timeout` before this statement", - ), - fix: Some( - Fix { - title: "Add statement timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set statement_timeout = '5s';\n", - ), - }, - ], - }, - ), - }, - ] - "#); + assert_snapshot!(lint_errors(sql, Rule::RequireTimeoutSettings), @r" + error[RequireTimeoutSettings]: Missing `set statement_timeout` before potentially slow operations + ╭▸ + 3 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `statement_timeout` before this statement + ╭╴ + 2 + set statement_timeout = '5s'; + ╰╴ + "); } #[test] @@ -249,8 +189,7 @@ SET lock_timeout = '1s'; SET statement_timeout = '5s'; ALTER TABLE t ADD COLUMN c BOOLEAN; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireTimeoutSettings); } #[test] @@ -260,8 +199,7 @@ SET Lock_Timeout = '1s'; SET Statement_Timeout = '5s'; ALTER TABLE t ADD COLUMN c BOOLEAN; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireTimeoutSettings); } #[test] @@ -269,8 +207,7 @@ ALTER TABLE t ADD COLUMN c BOOLEAN; let sql = r#" SELECT * FROM t; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireTimeoutSettings); } #[test] @@ -280,54 +217,26 @@ SET foo.lock_timeout = '1s'; SET foo.statement_timeout = '5s'; ALTER TABLE t ADD COLUMN c BOOLEAN; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors, @r#" - [ - Violation { - code: RequireTimeoutSettings, - message: "Missing `set lock_timeout` before potentially slow operations", - text_range: 64..98, - help: Some( - "Configure a `lock_timeout` before this statement.", - ), - fix: Some( - Fix { - title: "Add lock timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set lock_timeout = '1s';\n", - ), - }, - ], - }, - ), - }, - Violation { - code: RequireTimeoutSettings, - message: "Missing `set statement_timeout` before potentially slow operations", - text_range: 64..98, - help: Some( - "Configure a `statement_timeout` before this statement", - ), - fix: Some( - Fix { - title: "Add statement timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set statement_timeout = '5s';\n", - ), - }, - ], - }, - ), - }, - ] - "#); + assert_snapshot!(lint_errors(sql, Rule::RequireTimeoutSettings), @r" + error[RequireTimeoutSettings]: Missing `set lock_timeout` before potentially slow operations + ╭▸ + 4 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `lock_timeout` before this statement. + ╭╴ + 2 + set lock_timeout = '1s'; + ╰╴ + error[RequireTimeoutSettings]: Missing `set statement_timeout` before potentially slow operations + ╭▸ + 4 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `statement_timeout` before this statement + ╭╴ + 2 + set statement_timeout = '5s'; + ╰╴ + "); } #[test] fn err_timeouts_after_ddl() { @@ -336,54 +245,26 @@ ALTER TABLE t ADD COLUMN c BOOLEAN; SET lock_timeout = '1s'; SET statement_timeout = '5s'; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors, @r#" - [ - Violation { - code: RequireTimeoutSettings, - message: "Missing `set lock_timeout` before potentially slow operations", - text_range: 1..35, - help: Some( - "Configure a `lock_timeout` before this statement.", - ), - fix: Some( - Fix { - title: "Add lock timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set lock_timeout = '1s';\n", - ), - }, - ], - }, - ), - }, - Violation { - code: RequireTimeoutSettings, - message: "Missing `set statement_timeout` before potentially slow operations", - text_range: 1..35, - help: Some( - "Configure a `statement_timeout` before this statement", - ), - fix: Some( - Fix { - title: "Add statement timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set statement_timeout = '5s';\n", - ), - }, - ], - }, - ), - }, - ] - "#); + assert_snapshot!(lint_errors(sql, Rule::RequireTimeoutSettings), @r" + error[RequireTimeoutSettings]: Missing `set lock_timeout` before potentially slow operations + ╭▸ + 2 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `lock_timeout` before this statement. + ╭╴ + 2 + set lock_timeout = '1s'; + ╰╴ + error[RequireTimeoutSettings]: Missing `set statement_timeout` before potentially slow operations + ╭▸ + 2 │ ALTER TABLE t ADD COLUMN c BOOLEAN; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `statement_timeout` before this statement + ╭╴ + 2 + set statement_timeout = '5s'; + ╰╴ + "); } #[test] @@ -391,54 +272,26 @@ SET statement_timeout = '5s'; let sql = r#" CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors, @r#" - [ - Violation { - code: RequireTimeoutSettings, - message: "Missing `set lock_timeout` before potentially slow operations", - text_range: 1..48, - help: Some( - "Configure a `lock_timeout` before this statement.", - ), - fix: Some( - Fix { - title: "Add lock timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set lock_timeout = '1s';\n", - ), - }, - ], - }, - ), - }, - Violation { - code: RequireTimeoutSettings, - message: "Missing `set statement_timeout` before potentially slow operations", - text_range: 1..48, - help: Some( - "Configure a `statement_timeout` before this statement", - ), - fix: Some( - Fix { - title: "Add statement timeout", - edits: [ - Edit { - text_range: 1..1, - text: Some( - "set statement_timeout = '5s';\n", - ), - }, - ], - }, - ), - }, - ] - "#); + assert_snapshot!(lint_errors(sql, Rule::RequireTimeoutSettings), @r" + error[RequireTimeoutSettings]: Missing `set lock_timeout` before potentially slow operations + ╭▸ + 2 │ CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `lock_timeout` before this statement. + ╭╴ + 2 + set lock_timeout = '1s'; + ╰╴ + error[RequireTimeoutSettings]: Missing `set statement_timeout` before potentially slow operations + ╭▸ + 2 │ CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Configure a `statement_timeout` before this statement + ╭╴ + 2 + set statement_timeout = '5s'; + ╰╴ + "); } #[test] @@ -450,8 +303,7 @@ CREATE FUNCTION add(integer, integer) RETURNS integer AS 'select $1 + $2;' LANGUAGE SQL; "#; - let errors = lint(sql, Rule::RequireTimeoutSettings); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::RequireTimeoutSettings); } #[test] diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap index f429598f..bb5146e7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__arbitrary_func_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingFieldWithDefault)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 74..83, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +3 │ ALTER TABLE "core_recipe" ADD COLUMN "foo" jsonb DEFAULT myjsonb(); + │ ━━━━━━━━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap index 4429f769..33e95266 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_random_with_args_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingFieldWithDefault)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 80..88, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +3 │ ALTER TABLE "core_recipe" ADD COLUMN "foo" timestamptz DEFAULT now(123); + │ ━━━━━━━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap index 1a44702e..8e8a6a60 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingFieldWithDefault)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 60..66, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +2 │ ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT uuid(); + │ ━━━━━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap index 46cd598d..25378817 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_uuid_error_multi_stmt.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingFieldWithDefault)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 56..62, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +2 │ alter table t set logged, add column c integer default uuid(); + │ ━━━━━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap index 4f0ce043..a3afab8c 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__default_volatile_func_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingFieldWithDefault)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 76..84, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +3 │ ALTER TABLE "core_recipe" ADD COLUMN "foo" boolean DEFAULT random(); + │ ━━━━━━━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap index 2fa8357d..adcaf02f 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__generated_stored_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingFieldWithDefault)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 40..78, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +3 │ ADD COLUMN bar numeric GENERATED ALWAYS AS (bar + baz) STORED; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_column_references_lock.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_column_references_lock.snap new file mode 100644 index 00000000..81806122 --- /dev/null +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_column_references_lock.snap @@ -0,0 +1,10 @@ +--- +source: crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs +expression: "lint_errors(sql, Rule::AddingForeignKeyConstraint)" +--- +error[AddingForeignKeyConstraint]: Adding a foreign key constraint requires a table scan and a `SHARE ROW EXCLUSIVE` lock on both tables, which blocks writes to each table. + ╭▸ +3 │ ALTER TABLE "emails" ADD COLUMN "user_id" INT REFERENCES "user" ("id"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Add `NOT VALID` to the constraint in one transaction and then VALIDATE the constraint in a separate transaction. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_foreign_key_constraint_lock.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_foreign_key_constraint_lock.snap new file mode 100644 index 00000000..3ff9ca5f --- /dev/null +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_foreign_key_constraint__test__add_foreign_key_constraint_lock.snap @@ -0,0 +1,10 @@ +--- +source: crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs +expression: "lint_errors(sql, Rule::AddingForeignKeyConstraint)" +--- +error[AddingForeignKeyConstraint]: Adding a foreign key constraint requires a table scan and a `SHARE ROW EXCLUSIVE` lock on both tables, which blocks writes to each table. + ╭▸ +4 │ ALTER TABLE "email" ADD CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES "user" ("id"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Add `NOT VALID` to the constraint in one transaction and then VALIDATE the constraint in a separate transaction. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap index ed9a3ff1..7f48057c 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__regression_gh_issue_519.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_not_null_field.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingNotNullableField)" --- -[ - Violation { - code: AddingNotNullableField, - message: "Setting a column `NOT NULL` blocks reads while the table is scanned.", - text_range: 78..90, - help: Some( - "Make the field nullable and use a `CHECK` constraint instead.", - ), - fix: None, - }, -] +error[AddingNotNullableField]: Setting a column `NOT NULL` blocks reads while the table is scanned. + ╭▸ +4 │ ALTER TABLE my_table ALTER COLUMN my_column SET NOT NULL; + │ ━━━━━━━━━━━━ + │ + ╰ help: Make the field nullable and use a `CHECK` constraint instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap index 51e9855c..aebf4772 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_not_null_field__test__set_not_null.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_not_null_field.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingNotNullableField)" --- -[ - Violation { - code: AddingNotNullableField, - message: "Setting a column `NOT NULL` blocks reads while the table is scanned.", - text_range: 46..58, - help: Some( - "Make the field nullable and use a `CHECK` constraint instead.", - ), - fix: None, - }, -] +error[AddingNotNullableField]: Setting a column `NOT NULL` blocks reads while the table is scanned. + ╭▸ +2 │ ALTER TABLE "core_recipe" ALTER COLUMN "foo" SET NOT NULL; + │ ━━━━━━━━━━━━ + │ + ╰ help: Make the field nullable and use a `CHECK` constraint instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap index 8b2a448c..1710661e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__plain_primary_key.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_primary_key_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingSerialPrimaryKeyField)" --- -[ - Violation { - code: AddingSerialPrimaryKeyField, - message: "Adding a primary key constraint requires an `ACCESS EXCLUSIVE` lock that will block all reads and writes to the table while the primary key index is built.", - text_range: 23..39, - help: Some( - "Add the `PRIMARY KEY` constraint `USING` an index.", - ), - fix: None, - }, -] +error[AddingSerialPrimaryKeyField]: Adding a primary key constraint requires an `ACCESS EXCLUSIVE` lock that will block all reads and writes to the table while the primary key index is built. + ╭▸ +2 │ ALTER TABLE items ADD PRIMARY KEY (id); + │ ━━━━━━━━━━━━━━━━ + │ + ╰ help: Add the `PRIMARY KEY` constraint `USING` an index. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap index f201d162..d59f5e75 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_primary_key_constraint__test__serial_primary_key.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_primary_key_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingSerialPrimaryKeyField)" --- -[ - Violation { - code: AddingSerialPrimaryKeyField, - message: "Adding a primary key constraint requires an `ACCESS EXCLUSIVE` lock that will block all reads and writes to the table while the primary key index is built.", - text_range: 43..54, - help: Some( - "Add the `PRIMARY KEY` constraint `USING` an index.", - ), - fix: None, - }, -] +error[AddingSerialPrimaryKeyField]: Adding a primary key constraint requires an `ACCESS EXCLUSIVE` lock that will block all reads and writes to the table while the primary key index is built. + ╭▸ +2 │ ALTER TABLE a ADD COLUMN b SERIAL PRIMARY KEY; + │ ━━━━━━━━━━━ + │ + ╰ help: Add the `PRIMARY KEY` constraint `USING` an index. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap index 08a38cfd..e949bafd 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_required_field__test__not_null_without_default.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_required_field.rs -expression: errors +expression: "lint_errors(sql, Rule::AddingRequiredField)" --- -[ - Violation { - code: AddingRequiredField, - message: "Adding a new column that is `NOT NULL` and has no default value to an existing table effectively makes it required.", - text_range: 22..58, - help: Some( - "Make the field nullable or add a non-VOLATILE DEFAULT", - ), - fix: None, - }, -] +error[AddingRequiredField]: Adding a new column that is `NOT NULL` and has no default value to an existing table effectively makes it required. + ╭▸ +2 │ ALTER TABLE "recipe" ADD COLUMN "public" boolean NOT NULL; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Make the field nullable or add a non-VOLATILE DEFAULT diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap index 368ac139..fb797bcb 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_alter_domain_with_add_constraint__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/ban_alter_domain_with_add_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::BanAlterDomainWithAddConstraint)" --- -[ - Violation { - code: BanAlterDomainWithAddConstraint, - message: "Domains with constraints have poor support for online migrations. Use table and column constraints instead.", - text_range: 31..79, - help: None, - fix: None, - }, -] +error[BanAlterDomainWithAddConstraint]: Domains with constraints have poor support for online migrations. Use table and column constraints instead. + ╭▸ +2 │ ALTER DOMAIN domain_name ADD CONSTRAINT constraint_name CHECK (value > 0); + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap index 59d81fdd..2875ce21 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__all_the_types.snap @@ -1,120 +1,51 @@ --- source: crates/squawk_linter/src/rules/ban_char_field.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCharField)" --- -[ - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 59..68, - help: None, - fix: Some( - Fix { - title: "Replace with `varchar`", - edits: [ - Edit { - text_range: 59..63, - text: Some( - "varchar", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 76..90, - help: None, - fix: Some( - Fix { - title: "Replace with `varchar`", - edits: [ - Edit { - text_range: 76..85, - text: Some( - "varchar", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 98..102, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 98..102, - text: Some( - "text", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 110..119, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 110..119, - text: Some( - "text", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 265..280, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 276..280, - text: Some( - "text", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 288..292, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 288..292, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +4 │ b char(100), + │ ━━━━━━━━━ + ╭╴ +4 │ b varchar(100), + ╰╴ +++ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +5 │ c character(100), + │ ━━━━━━━━━━━━━━ + ╭╴ +5 - c character(100), +5 + c varchar(100), + ╰╴ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +6 │ d char, + │ ━━━━ + ╭╴ +6 - d char, +6 + d text, + ╰╴ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +7 │ e character, + │ ━━━━━━━━━ + ╭╴ +7 - e character, +7 + e text, + ╰╴ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +15 │ o pg_catalog.char, + │ ━━━━━━━━━━━━━━━ + ╭╴ +15 - o pg_catalog.char, +15 + o pg_catalog.text, + ╰╴ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +16 │ p char[] + │ ━━━━ + ╭╴ +16 - p char[] +16 + p text[] + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap index 7d34714d..b380d412 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__alter_table_err.snap @@ -1,25 +1,12 @@ --- source: crates/squawk_linter/src/rules/ban_char_field.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCharField)" --- -[ - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 28..32, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 28..32, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +2 │ alter table t add column c char; + │ ━━━━ + ╭╴ +2 - alter table t add column c char; +2 + alter table t add column c text; + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap index f91d14fa..93c1c2f6 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__array_char_type_err.snap @@ -1,25 +1,12 @@ --- source: crates/squawk_linter/src/rules/ban_char_field.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCharField)" --- -[ - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 22..26, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 22..26, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +3 │ a char[] + │ ━━━━ + ╭╴ +3 - a char[] +3 + a text[] + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__case_insensitive.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__case_insensitive.snap index f91d14fa..650fc7f1 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__case_insensitive.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__case_insensitive.snap @@ -1,25 +1,12 @@ --- source: crates/squawk_linter/src/rules/ban_char_field.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCharField)" --- -[ - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 22..26, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 22..26, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +3 │ a Char + │ ━━━━ + ╭╴ +3 - a Char +3 + a text + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap index c1b4c4b1..5e2a99d3 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_char_field__test__creating_table_with_char_errors.snap @@ -1,82 +1,35 @@ --- source: crates/squawk_linter/src/rules/ban_char_field.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCharField)" --- -[ - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 77..86, - help: None, - fix: Some( - Fix { - title: "Replace with `varchar`", - edits: [ - Edit { - text_range: 77..81, - text: Some( - "varchar", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 108..122, - help: None, - fix: Some( - Fix { - title: "Replace with `varchar`", - edits: [ - Edit { - text_range: 108..117, - text: Some( - "varchar", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 147..151, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 147..151, - text: Some( - "text", - ), - }, - ], - }, - ), - }, - Violation { - code: BanCharField, - message: "Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`.", - text_range: 174..183, - help: None, - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 174..183, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +4 │ "alpha" char(100) NOT NULL, + │ ━━━━━━━━━ + ╭╴ +4 │ "alpha" varchar(100) NOT NULL, + ╰╴ +++ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +5 │ "beta" character(100) NOT NULL, + │ ━━━━━━━━━━━━━━ + ╭╴ +5 - "beta" character(100) NOT NULL, +5 + "beta" varchar(100) NOT NULL, + ╰╴ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +6 │ "charlie" char NOT NULL, + │ ━━━━ + ╭╴ +6 - "charlie" char NOT NULL, +6 + "charlie" text NOT NULL, + ╰╴ +error[BanCharField]: Using `character` is likely a mistake and should almost always be replaced by `text` or `varchar`. + ╭▸ +7 │ "delta" character NOT NULL + │ ━━━━━━━━━ + ╭╴ +7 - "delta" character NOT NULL +7 + "delta" text NOT NULL + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap index f16b21b9..15de87f7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__ban_concurrent_index_creation_in_transaction_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs -expression: errors +expression: "lint_errors(sql, Rule::BanConcurrentIndexCreationInTransaction)" --- -[ - Violation { - code: BanConcurrentIndexCreationInTransaction, - message: "While regular index creation can happen inside a transaction, this is not allowed when the `CONCURRENTLY` option is used.", - text_range: 59..71, - help: Some( - "Build the index outside any transactions.", - ), - fix: None, - }, -] +error[BanConcurrentIndexCreationInTransaction]: While regular index creation can happen inside a transaction, this is not allowed when the `CONCURRENTLY` option is used. + ╭▸ +4 │ CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); + │ ━━━━━━━━━━━━ + │ + ╰ help: Build the index outside any transactions. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap index 7e3316fe..ce823202 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/ban_create_domain_with_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCreateDomainWithConstraint)" --- -[ - Violation { - code: BanCreateDomainWithConstraint, - message: "Domains with constraints have poor support for online migrations. Use table and column constraints instead.", - text_range: 46..63, - help: None, - fix: None, - }, -] +error[BanCreateDomainWithConstraint]: Domains with constraints have poor support for online migrations. Use table and column constraints instead. + ╭▸ +2 │ CREATE DOMAIN domain_name_3 AS NUMERIC(15,5) CHECK (value > 0); + ╰╴ ━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap index 708d8d91..907b3fdc 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_create_domain_with_constraint__test__err_with_multiple_constraints.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/ban_create_domain_with_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::BanCreateDomainWithConstraint)" --- -[ - Violation { - code: BanCreateDomainWithConstraint, - message: "Domains with constraints have poor support for online migrations. Use table and column constraints instead.", - text_range: 22..48, - help: None, - fix: None, - }, -] +error[BanCreateDomainWithConstraint]: Domains with constraints have poor support for online migrations. Use table and column constraints instead. + ╭▸ +2 │ create domain d as t check (value > 0) not null; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap index a75a5baf..faf85989 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_column__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/ban_drop_column.rs -expression: errors +expression: "lint_errors(sql, Rule::BanDropColumn)" --- -[ - Violation { - code: BanDropColumn, - message: "Dropping a column may break existing clients.", - text_range: 23..52, - help: None, - fix: None, - }, -] +error[BanDropColumn]: Dropping a column may break existing clients. + ╭▸ +2 │ ALTER TABLE "bar_tbl" DROP COLUMN "foo_col" CASCADE; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap index f6e18540..24caece0 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_database__test__ban_drop_database.snap @@ -1,27 +1,16 @@ --- source: crates/squawk_linter/src/rules/ban_drop_database.rs -expression: errors +expression: "lint_errors(sql, Rule::BanDropDatabase)" --- -[ - Violation { - code: BanDropDatabase, - message: "Dropping a database may break existing clients.", - text_range: 9..35, - help: None, - fix: None, - }, - Violation { - code: BanDropDatabase, - message: "Dropping a database may break existing clients.", - text_range: 45..81, - help: None, - fix: None, - }, - Violation { - code: BanDropDatabase, - message: "Dropping a database may break existing clients.", - text_range: 91..127, - help: None, - fix: None, - }, -] +error[BanDropDatabase]: Dropping a database may break existing clients. + ╭▸ +2 │ DROP DATABASE "table_name"; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━ +error[BanDropDatabase]: Dropping a database may break existing clients. + ╭▸ +3 │ DROP DATABASE IF EXISTS "table_name"; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +error[BanDropDatabase]: Dropping a database may break existing clients. + ╭▸ +4 │ DROP DATABASE IF EXISTS "table_name" + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap index f050cf78..043e9645 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_not_null__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/ban_drop_not_null.rs -expression: errors +expression: "lint_errors(sql, Rule::BanDropNotNull)" --- -[ - Violation { - code: BanDropNotNull, - message: "Dropping a `NOT NULL` constraint may break existing clients.", - text_range: 46..59, - help: None, - fix: None, - }, -] +error[BanDropNotNull]: Dropping a `NOT NULL` constraint may break existing clients. + ╭▸ +2 │ ALTER TABLE "bar_tbl" ALTER COLUMN "foo_col" DROP NOT NULL; + ╰╴ ━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap index 067309b2..5476d14a 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_drop_table__test__err.snap @@ -1,27 +1,16 @@ --- source: crates/squawk_linter/src/rules/ban_drop_table.rs -expression: errors +expression: "lint_errors(sql, Rule::BanDropTable)" --- -[ - Violation { - code: BanDropTable, - message: "Dropping a table may break existing clients.", - text_range: 1..24, - help: None, - fix: None, - }, - Violation { - code: BanDropTable, - message: "Dropping a table may break existing clients.", - text_range: 26..59, - help: None, - fix: None, - }, - Violation { - code: BanDropTable, - message: "Dropping a table may break existing clients.", - text_range: 61..94, - help: None, - fix: None, - }, -] +error[BanDropTable]: Dropping a table may break existing clients. + ╭▸ +2 │ DROP TABLE "table_name"; + ╰╴━━━━━━━━━━━━━━━━━━━━━━━ +error[BanDropTable]: Dropping a table may break existing clients. + ╭▸ +3 │ DROP TABLE IF EXISTS "table_name"; + ╰╴━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +error[BanDropTable]: Dropping a table may break existing clients. + ╭▸ +4 │ DROP TABLE IF EXISTS "table_name" + ╰╴━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap index d4243942..13171d75 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_truncate_cascade__test__err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/ban_truncate_cascade.rs -expression: errors +expression: "lint_errors(sql, Rule::BanTruncateCascade)" --- -[ - Violation { - code: BanTruncateCascade, - message: "Using `CASCADE` will recursively truncate any tables that foreign key to the referenced tables! So if you had foreign keys setup as `a <- b <- c` and truncated `a`, then `b` & `c` would also be truncated!", - text_range: 26..33, - help: Some( - "Remove the `CASCADE` and specify exactly which tables you want to truncate.", - ), - fix: None, - }, -] +error[BanTruncateCascade]: Using `CASCADE` will recursively truncate any tables that foreign key to the referenced tables! So if you had foreign keys setup as `a <- b <- c` and truncated `a`, then `b` & `c` would also be truncated! + ╭▸ +2 │ truncate a, b, c cascade; + │ ━━━━━━━ + │ + ╰ help: Remove the `CASCADE` and specify exactly which tables you want to truncate. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap index b255650e..dfcfe62f 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__another_err.snap @@ -1,20 +1,12 @@ --- source: crates/squawk_linter/src/rules/changing_column_type.rs -expression: errors +expression: "lint_errors(sql, Rule::ChangingColumnType)" --- -[ - Violation { - code: ChangingColumnType, - message: "Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table.", - text_range: 88..131, - help: None, - fix: None, - }, - Violation { - code: ChangingColumnType, - message: "Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table.", - text_range: 178..205, - help: None, - fix: None, - }, -] +error[ChangingColumnType]: Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table. + ╭▸ +6 │ ALTER TABLE "core_recipe" ALTER COLUMN "foo" TYPE varchar(255) USING "foo"::varchar(255); + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +error[ChangingColumnType]: Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table. + ╭▸ +7 │ ALTER TABLE "core_recipe" ALTER COLUMN "foo" TYPE text USING "foo"::text; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap index b37002a9..75a10bb0 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__changing_column_type__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/changing_column_type.rs -expression: errors +expression: "lint_errors(sql, Rule::ChangingColumnType)" --- -[ - Violation { - code: ChangingColumnType, - message: "Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table.", - text_range: 92..121, - help: None, - fix: None, - }, -] +error[ChangingColumnType]: Changing a column type requires an `ACCESS EXCLUSIVE` lock on the table which blocks reads and writes while the table is rewritten. Changing the type of the column may also break other clients reading from the table. + ╭▸ +6 │ ALTER TABLE "core_recipe" ALTER COLUMN "edits" TYPE text USING "edits"::text; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap index af200c62..f8e4a8ce 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_fk_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/constraint_missing_not_valid.rs -expression: errors +expression: "lint_errors(sql, Rule::ConstraintMissingNotValid)" --- -[ - Violation { - code: ConstraintMissingNotValid, - message: "By default new constraints require a table scan and block writes to the table while that scan occurs.", - text_range: 40..114, - help: Some( - "Use `NOT VALID` with a later `VALIDATE CONSTRAINT` call.", - ), - fix: None, - }, -] +error[ConstraintMissingNotValid]: By default new constraints require a table scan and block writes to the table while that scan occurs. + ╭▸ +3 │ ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Use `NOT VALID` with a later `VALIDATE CONSTRAINT` call. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap index 788986de..607d16b8 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_transaction_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/constraint_missing_not_valid.rs -expression: errors +expression: "lint_errors(sql, Rule::ConstraintMissingNotValid)" --- -[ - Violation { - code: ConstraintMissingNotValid, - message: "Using `NOT VALID` and `VALIDATE CONSTRAINT` in the same transaction will block all reads while the constraint is validated.", - text_range: 141..170, - help: Some( - "Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.", - ), - fix: None, - }, -] +error[ConstraintMissingNotValid]: Using `NOT VALID` and `VALIDATE CONSTRAINT` in the same transaction will block all reads while the constraint is validated. + ╭▸ +4 │ ALTER TABLE "app_email" VALIDATE CONSTRAINT "fk_user"; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap index 5f9dca16..54f0cf58 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__adding_unique_constraint_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/disallow_unique_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::DisallowedUniqueConstraint)" --- -[ - Violation { - code: DisallowedUniqueConstraint, - message: "Adding a `UNIQUE` constraint requires an `ACCESS EXCLUSIVE` lock which blocks reads and writes to the table while the index is built.", - text_range: 28..80, - help: Some( - "Create an index `CONCURRENTLY` and create the constraint using the index.", - ), - fix: None, - }, -] +error[DisallowedUniqueConstraint]: Adding a `UNIQUE` constraint requires an `ACCESS EXCLUSIVE` lock which blocks reads and writes to the table while the index is built. + ╭▸ +2 │ ALTER TABLE table_name ADD CONSTRAINT field_name_constraint UNIQUE (field_name); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Create an index `CONCURRENTLY` and create the constraint using the index. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap index 2ae89e29..f4b9e216 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/disallow_unique_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::DisallowedUniqueConstraint)" --- -[ - Violation { - code: DisallowedUniqueConstraint, - message: "Adding a `UNIQUE` constraint requires an `ACCESS EXCLUSIVE` lock which blocks reads and writes to the table while the index is built.", - text_range: 37..69, - help: Some( - "Create an index `CONCURRENTLY` and create the constraint using the index.", - ), - fix: None, - }, -] +error[DisallowedUniqueConstraint]: Adding a `UNIQUE` constraint requires an `ACCESS EXCLUSIVE` lock which blocks reads and writes to the table while the index is built. + ╭▸ +2 │ ALTER TABLE foo ADD COLUMN bar text CONSTRAINT foo_bar_unique UNIQUE; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Create an index `CONCURRENTLY` and create the constraint using the index. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap index ab126ca4..47191119 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__disallow_unique_constraint__test__unique_constraint_inline_add_column_unique_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/disallow_unique_constraint.rs -expression: errors +expression: "lint_errors(sql, Rule::DisallowedUniqueConstraint)" --- -[ - Violation { - code: DisallowedUniqueConstraint, - message: "Adding a `UNIQUE` constraint requires an `ACCESS EXCLUSIVE` lock which blocks reads and writes to the table while the index is built.", - text_range: 37..43, - help: Some( - "Create an index `CONCURRENTLY` and create the constraint using the index.", - ), - fix: None, - }, -] +error[DisallowedUniqueConstraint]: Adding a `UNIQUE` constraint requires an `ACCESS EXCLUSIVE` lock which blocks reads and writes to the table while the index is built. + ╭▸ +2 │ ALTER TABLE foo ADD COLUMN bar text UNIQUE; + │ ━━━━━━ + │ + ╰ help: Create an index `CONCURRENTLY` and create the constraint using the index. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap index b0950a0a..e9d81540 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_int__test__err.snap @@ -1,111 +1,52 @@ --- source: crates/squawk_linter/src/rules/prefer_bigint_over_int.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferBigintOverInt)" --- -[ - Violation { - code: PreferBigintOverInt, - message: "Using 32-bit integer fields can result in hitting the max `int` limit.", - text_range: 29..32, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `bigint`", - edits: [ - Edit { - text_range: 29..32, - text: Some( - "bigint", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverInt, - message: "Using 32-bit integer fields can result in hitting the max `int` limit.", - text_range: 64..71, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `bigint`", - edits: [ - Edit { - text_range: 64..71, - text: Some( - "bigint", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverInt, - message: "Using 32-bit integer fields can result in hitting the max `int` limit.", - text_range: 103..107, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `int8`", - edits: [ - Edit { - text_range: 103..107, - text: Some( - "int8", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverInt, - message: "Using 32-bit integer fields can result in hitting the max `int` limit.", - text_range: 139..145, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `bigserial`", - edits: [ - Edit { - text_range: 139..145, - text: Some( - "bigserial", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverInt, - message: "Using 32-bit integer fields can result in hitting the max `int` limit.", - text_range: 177..184, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `serial8`", - edits: [ - Edit { - text_range: 177..184, - text: Some( - "serial8", - ), - }, - ], - }, - ), - }, -] +error[PreferBigintOverInt]: Using 32-bit integer fields can result in hitting the max `int` limit. + ╭▸ +3 │ id int + │ ━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +3 │ id bigint + ╰╴ +++ +error[PreferBigintOverInt]: Using 32-bit integer fields can result in hitting the max `int` limit. + ╭▸ +6 │ id integer + │ ━━━━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +6 - id integer +6 + id bigint + ╰╴ +error[PreferBigintOverInt]: Using 32-bit integer fields can result in hitting the max `int` limit. + ╭▸ +9 │ id int4 + │ ━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +9 - id int4 +9 + id int8 + ╰╴ +error[PreferBigintOverInt]: Using 32-bit integer fields can result in hitting the max `int` limit. + ╭▸ +12 │ id serial + │ ━━━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +12 │ id bigserial + ╰╴ +++ +error[PreferBigintOverInt]: Using 32-bit integer fields can result in hitting the max `int` limit. + ╭▸ +15 │ id serial4 + │ ━━━━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +15 - id serial4 +15 + id serial8 + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap index bcd7fac5..19be6d91 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_bigint_over_smallint__test__err.snap @@ -1,90 +1,44 @@ --- source: crates/squawk_linter/src/rules/prefer_bigint_over_smallint.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferBigintOverSmallint)" --- -[ - Violation { - code: PreferBigintOverSmallint, - message: "Using 16-bit integer fields can result in hitting the max `int` limit.", - text_range: 29..37, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `bigint`", - edits: [ - Edit { - text_range: 29..37, - text: Some( - "bigint", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverSmallint, - message: "Using 16-bit integer fields can result in hitting the max `int` limit.", - text_range: 69..73, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `int8`", - edits: [ - Edit { - text_range: 69..73, - text: Some( - "int8", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverSmallint, - message: "Using 16-bit integer fields can result in hitting the max `int` limit.", - text_range: 105..116, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `bigserial`", - edits: [ - Edit { - text_range: 105..116, - text: Some( - "bigserial", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferBigintOverSmallint, - message: "Using 16-bit integer fields can result in hitting the max `int` limit.", - text_range: 148..155, - help: Some( - "Use 64-bit integer values instead to prevent hitting this limit.", - ), - fix: Some( - Fix { - title: "Replace with a 64-bit integer type: `serial8`", - edits: [ - Edit { - text_range: 148..155, - text: Some( - "serial8", - ), - }, - ], - }, - ), - }, -] +error[PreferBigintOverSmallint]: Using 16-bit integer fields can result in hitting the max `int` limit. + ╭▸ +3 │ id smallint + │ ━━━━━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +3 - id smallint +3 + id bigint + ╰╴ +error[PreferBigintOverSmallint]: Using 16-bit integer fields can result in hitting the max `int` limit. + ╭▸ +6 │ id int2 + │ ━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +6 - id int2 +6 + id int8 + ╰╴ +error[PreferBigintOverSmallint]: Using 16-bit integer fields can result in hitting the max `int` limit. + ╭▸ +9 │ id smallserial + │ ━━━━━━━━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +9 - id smallserial +9 + id bigserial + ╰╴ +error[PreferBigintOverSmallint]: Using 16-bit integer fields can result in hitting the max `int` limit. + ╭▸ +12 │ id serial2 + │ ━━━━━━━ + │ + ├ help: Use 64-bit integer values instead to prevent hitting this limit. + ╭╴ +12 - id serial2 +12 + id serial8 + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap index 4ed7068e..44581f3c 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__err.snap @@ -1,153 +1,74 @@ --- source: crates/squawk_linter/src/rules/prefer_identity.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferIdentity)" --- -[ - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 29..35, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 29..35, - text: Some( - "integer generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 67..74, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 67..74, - text: Some( - "smallint generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 106..113, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 106..113, - text: Some( - "integer generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 145..152, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 145..152, - text: Some( - "bigint generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 184..195, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 184..195, - text: Some( - "smallint generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 227..236, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 227..236, - text: Some( - "bigint generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 268..277, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 268..277, - text: Some( - "bigint generated by default as identity", - ), - }, - ], - }, - ), - }, -] +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +3 │ id serial + │ ━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +3 - id serial +3 + id integer generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +6 │ id serial2 + │ ━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +6 - id serial2 +6 + id smallint generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +9 │ id serial4 + │ ━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +9 - id serial4 +9 + id integer generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +12 │ id serial8 + │ ━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +12 - id serial8 +12 + id bigint generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +15 │ id smallserial + │ ━━━━━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +15 - id smallserial +15 + id smallint generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +18 │ id bigserial + │ ━━━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +18 - id bigserial +18 + id bigint generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +21 │ id BIGSERIAL + │ ━━━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +21 - id BIGSERIAL +21 + id bigint generated by default as identity + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap index 74612939..fd77017a 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_identity__test__ok_when_quoted.snap @@ -1,48 +1,24 @@ --- source: crates/squawk_linter/src/rules/prefer_identity.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferIdentity)" --- -[ - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 29..37, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 29..37, - text: Some( - "integer generated by default as identity", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferIdentity, - message: "Serial types make schema, dependency, and permission management difficult.", - text_range: 69..80, - help: Some( - "Use an `IDENTITY` column instead.", - ), - fix: Some( - Fix { - title: "Replace with IDENTITY column", - edits: [ - Edit { - text_range: 69..80, - text: Some( - "integer generated by default as identity", - ), - }, - ], - }, - ), - }, -] +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +3 │ id "serial" + │ ━━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +3 - id "serial" +3 + id integer generated by default as identity + ╰╴ +error[PreferIdentity]: Serial types make schema, dependency, and permission management difficult. + ╭▸ +6 │ id "bigserial" + │ ━━━━━━━━━━━ + │ + ├ help: Use an `IDENTITY` column instead. + ╭╴ +6 - id "bigserial" +6 + id integer generated by default as identity + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap index a0c9f90e..32642898 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_column_set_not_null.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nalter table t alter column c set not null;\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing transaction, the migration can't be rerun if it fails part way through.", - text_range: 54..81, - help: None, - fix: None, - }, -] +error[PreferRobustStmts]: Missing transaction, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ alter table t alter column c set not null; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap index 3ca9971e..aa3a1aca 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_column_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nalter table t drop column c cascade;\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 54..75, - help: None, - fix: Some( - Fix { - title: "Insert `if exists`", - edits: [ - Edit { - text_range: 65..65, - text: Some( - " if exists", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ alter table t drop column c cascade; + │ ━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ alter table t drop column if exists c cascade; + ╰╴ +++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap index 4d5fffb7..9851b522 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_drop_constraint_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nALTER TABLE \"core_foo\" DROP CONSTRAINT \"core_foo_idx\";\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 63..93, - help: None, - fix: Some( - Fix { - title: "Insert `if exists`", - edits: [ - Edit { - text_range: 78..78, - text: Some( - " if exists", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ ALTER TABLE "core_foo" DROP CONSTRAINT "core_foo_idx"; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ ALTER TABLE "core_foo" DROP CONSTRAINT if exists "core_foo_idx"; + ╰╴ +++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap index b666e1d1..fa9a82bc 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__alter_table_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nALTER TABLE \"core_foo\" ADD COLUMN \"answer_id\" integer NULL;\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 63..98, - help: None, - fix: Some( - Fix { - title: "Insert `if not exists`", - edits: [ - Edit { - text_range: 73..73, - text: Some( - " if not exists", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ ALTER TABLE "core_foo" ADD COLUMN "answer_id" integer NULL; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ ALTER TABLE "core_foo" ADD COLUMN if not exists "answer_id" integer NULL; + ╰╴ +++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap index 963ddbeb..5629448a 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_index_concurrently_err.snap @@ -1,27 +1,13 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nCREATE INDEX CONCURRENTLY \"core_foo_idx\" ON \"core_foo\" (\"answer_id\");\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 40..108, - help: Some( - "Use an explicit name for a concurrently created index", - ), - fix: Some( - Fix { - title: "Insert `if not exists`", - edits: [ - Edit { - text_range: 66..66, - text: Some( - "if not exists ", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ CREATE INDEX CONCURRENTLY "core_foo_idx" ON "core_foo" ("answer_id"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Use an explicit name for a concurrently created index + ╭╴ +3 │ CREATE INDEX CONCURRENTLY if not exists "core_foo_idx" ON "core_foo" ("answer_id"); + ╰╴ +++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap index e81bfa96..63e5e83e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nCREATE TABLE \"core_bar\" ( \"id\" serial NOT NULL PRIMARY KEY, \"bravo\" text NOT NULL);\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 40..122, - help: None, - fix: Some( - Fix { - title: "Insert `if not exists`", - edits: [ - Edit { - text_range: 52..52, - text: Some( - " if not exists", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ CREATE TABLE "core_bar" ( "id" serial NOT NULL PRIMARY KEY, "bravo" text NOT NULL); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ CREATE TABLE if not exists "core_bar" ( "id" serial NOT NULL PRIMARY KEY, "bravo" text NOT NULL); + ╰╴ +++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_with_on_commit_drop_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_with_on_commit_drop_err.snap index 56c25a61..d8454ae7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_with_on_commit_drop_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_table_with_on_commit_drop_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nCREATE TABLE test_table (id int) ON COMMIT DROP;\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 40..87, - help: None, - fix: Some( - Fix { - title: "Insert `if not exists`", - edits: [ - Edit { - text_range: 52..52, - text: Some( - " if not exists", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ CREATE TABLE test_table (id int) ON COMMIT DROP; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ CREATE TABLE if not exists test_table (id int) ON COMMIT DROP; + ╰╴ +++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_temp_table_without_on_commit_drop_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_temp_table_without_on_commit_drop_err.snap index 96759523..5900db1e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_temp_table_without_on_commit_drop_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__create_temp_table_without_on_commit_drop_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nCREATE TEMP TABLE test_table (id int);\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 40..77, - help: None, - fix: Some( - Fix { - title: "Insert `if not exists`", - edits: [ - Edit { - text_range: 57..57, - text: Some( - " if not exists", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF NOT EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ CREATE TEMP TABLE test_table (id int); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ CREATE TEMP TABLE if not exists test_table (id int); + ╰╴ +++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap index be7cc6e1..4ea297df 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__disable_row_level_security_err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nCREATE TABLE IF NOT EXISTS test();\nALTER TABLE IF EXISTS test DISABLE ROW LEVEL SECURITY;\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing transaction, the migration can't be rerun if it fails part way through.", - text_range: 63..89, - help: None, - fix: None, - }, -] +error[PreferRobustStmts]: Missing transaction, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ ALTER TABLE IF EXISTS test DISABLE ROW LEVEL SECURITY; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap index f7f0c201..d4034730 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__double_add_after_drop_err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nALTER TABLE \"app_email\" DROP CONSTRAINT IF EXISTS \"email_uniq\";\nALTER TABLE \"app_email\" ADD CONSTRAINT \"email_uniq\" UNIQUE USING INDEX \"email_idx\";\n-- this second add constraint should error because it's not robust\nALTER TABLE \"app_email\" ADD CONSTRAINT \"email_uniq\" UNIQUE USING INDEX \"email_idx\";\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing transaction, the migration can't be rerun if it fails part way through.", - text_range: 240..298, - help: None, - fix: None, - }, -] +error[PreferRobustStmts]: Missing transaction, the migration can't be rerun if it fails part way through. + ╭▸ +5 │ ALTER TABLE "app_email" ADD CONSTRAINT "email_uniq" UNIQUE USING INDEX "email_idx"; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap index 7997ae42..1a360ba1 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__drop_index_err.snap @@ -1,25 +1,11 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nselect 1; -- so we don't skip checking\nDROP INDEX CONCURRENTLY \"email_idx\";\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing `IF EXISTS`, the migration can't be rerun if it fails part way through.", - text_range: 40..75, - help: None, - fix: Some( - Fix { - title: "Insert `if exists`", - edits: [ - Edit { - text_range: 64..64, - text: Some( - "if exists ", - ), - }, - ], - }, - ), - }, -] +error[PreferRobustStmts]: Missing `IF EXISTS`, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ DROP INDEX CONCURRENTLY "email_idx"; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ╭╴ +3 │ DROP INDEX CONCURRENTLY if exists "email_idx"; + ╰╴ +++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap index abec043d..3c5d6540 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "lint_formatted(r#\"\nCREATE TABLE IF NOT EXISTS test();\nALTER TABLE IF EXISTS test ENABLE ROW LEVEL SECURITY;\n \"#)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing transaction, the migration can't be rerun if it fails part way through.", - text_range: 63..88, - help: None, - fix: None, - }, -] +error[PreferRobustStmts]: Missing transaction, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ ALTER TABLE IF EXISTS test ENABLE ROW LEVEL SECURITY; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap index 7fbb59c4..2ae4f8a7 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_robust_stmts__test__enable_row_level_security_without_exists_check_err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/prefer_robust_stmts.rs -expression: errors +expression: "format_violations(sql, &errors)" --- -[ - Violation { - code: PreferRobustStmts, - message: "Missing transaction, the migration can't be rerun if it fails part way through.", - text_range: 53..78, - help: None, - fix: None, - }, -] +error[PreferRobustStmts]: Missing transaction, the migration can't be rerun if it fails part way through. + ╭▸ +3 │ ALTER TABLE test ENABLE ROW LEVEL SECURITY; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap index e4cb45fb..a7c31664 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__adding_column_non_text_err.snap @@ -1,27 +1,14 @@ --- source: crates/squawk_linter/src/rules/prefer_text_field.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferTextField)" --- -[ - Violation { - code: PreferTextField, - message: "Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table.", - text_range: 56..68, - help: Some( - "Use a `TEXT` field with a `CHECK` constraint.", - ), - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 56..68, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[PreferTextField]: Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table. + ╭▸ +3 │ ALTER TABLE "foo_table" ADD COLUMN "foo_column" varchar(256) NULL; + │ ━━━━━━━━━━━━ + │ + ├ help: Use a `TEXT` field with a `CHECK` constraint. + ╭╴ +3 - ALTER TABLE "foo_table" ADD COLUMN "foo_column" varchar(256) NULL; +3 + ALTER TABLE "foo_table" ADD COLUMN "foo_column" text NULL; + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap index f0ab270e..84a48fbf 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_pgcatalog_varchar_err.snap @@ -1,27 +1,14 @@ --- source: crates/squawk_linter/src/rules/prefer_text_field.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferTextField)" --- -[ - Violation { - code: PreferTextField, - message: "Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table.", - text_range: 69..92, - help: Some( - "Use a `TEXT` field with a `CHECK` constraint.", - ), - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 69..92, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[PreferTextField]: Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table. + ╭▸ +4 │ "alpha" pg_catalog.varchar(100) NOT NULL + │ ━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Use a `TEXT` field with a `CHECK` constraint. + ╭╴ +4 - "alpha" pg_catalog.varchar(100) NOT NULL +4 + "alpha" text NOT NULL + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap index 4aa3b57e..f1c8dbaf 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__create_table_with_varchar_err.snap @@ -1,27 +1,14 @@ --- source: crates/squawk_linter/src/rules/prefer_text_field.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferTextField)" --- -[ - Violation { - code: PreferTextField, - message: "Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table.", - text_range: 111..123, - help: Some( - "Use a `TEXT` field with a `CHECK` constraint.", - ), - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 111..123, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[PreferTextField]: Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table. + ╭▸ +8 │ "alpha" varchar(100) NOT NULL + │ ━━━━━━━━━━━━ + │ + ├ help: Use a `TEXT` field with a `CHECK` constraint. + ╭╴ +8 - "alpha" varchar(100) NOT NULL +8 + "alpha" text NOT NULL + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap index 19133abb..9bbe4cec 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_text_field__test__increase_varchar_size_err.snap @@ -1,27 +1,14 @@ --- source: crates/squawk_linter/src/rules/prefer_text_field.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferTextField)" --- -[ - Violation { - code: PreferTextField, - message: "Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table.", - text_range: 89..102, - help: Some( - "Use a `TEXT` field with a `CHECK` constraint.", - ), - fix: Some( - Fix { - title: "Replace with `text`", - edits: [ - Edit { - text_range: 89..102, - text: Some( - "text", - ), - }, - ], - }, - ), - }, -] +error[PreferTextField]: Changing the size of a `varchar` field requires an `ACCESS EXCLUSIVE` lock, that will prevent all reads and writes to the table. + ╭▸ +6 │ ALTER TABLE "core_foo" ALTER COLUMN "kind" TYPE varchar(1000) USING "kind"::varchar(1000); + │ ━━━━━━━━━━━━━ + │ + ├ help: Use a `TEXT` field with a `CHECK` constraint. + ╭╴ +6 - ALTER TABLE "core_foo" ALTER COLUMN "kind" TYPE varchar(1000) USING "kind"::varchar(1000); +6 + ALTER TABLE "core_foo" ALTER COLUMN "kind" TYPE text USING "kind"::varchar(1000); + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap index d504bdbb..e0827d99 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__alter_table_with_timestamp_err.snap @@ -1,48 +1,23 @@ --- source: crates/squawk_linter/src/rules/prefer_timestamptz.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferTimestampTz)" --- -[ - Violation { - code: PreferTimestampTz, - message: "When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time.", - text_range: 56..65, - help: Some( - "Use `timestamptz` instead of `timestamp` for your column type.", - ), - fix: Some( - Fix { - title: "Replace with `timestamptz`", - edits: [ - Edit { - text_range: 56..65, - text: Some( - "timestamptz", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferTimestampTz, - message: "When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time.", - text_range: 125..152, - help: Some( - "Use `timestamptz` instead of `timestamp` for your column type.", - ), - fix: Some( - Fix { - title: "Replace with `timestamptz`", - edits: [ - Edit { - text_range: 125..152, - text: Some( - "timestamptz", - ), - }, - ], - }, - ), - }, -] +error[PreferTimestampTz]: When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time. + ╭▸ +3 │ alter column created_ts type timestamp; + │ ━━━━━━━━━ + │ + ├ help: Use `timestamptz` instead of `timestamp` for your column type. + ╭╴ +3 │ alter column created_ts type timestamptz; + ╰╴ ++ +error[PreferTimestampTz]: When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time. + ╭▸ +5 │ alter column created_ts type timestamp without time zone; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Use `timestamptz` instead of `timestamp` for your column type. + ╭╴ +5 - alter column created_ts type timestamp without time zone; +5 + alter column created_ts type timestamptz; + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap index 3ba8b819..0b25040e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__prefer_timestamptz__test__create_table_with_timestamp_err.snap @@ -1,48 +1,23 @@ --- source: crates/squawk_linter/src/rules/prefer_timestamptz.rs -expression: errors +expression: "lint_errors(sql, Rule::PreferTimestampTz)" --- -[ - Violation { - code: PreferTimestampTz, - message: "When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time.", - text_range: 43..52, - help: Some( - "Use `timestamptz` instead of `timestamp` for your column type.", - ), - fix: Some( - Fix { - title: "Replace with `timestamptz`", - edits: [ - Edit { - text_range: 43..52, - text: Some( - "timestamptz", - ), - }, - ], - }, - ), - }, - Violation { - code: PreferTimestampTz, - message: "When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time.", - text_range: 99..126, - help: Some( - "Use `timestamptz` instead of `timestamp` for your column type.", - ), - fix: Some( - Fix { - title: "Replace with `timestamptz`", - edits: [ - Edit { - text_range: 99..126, - text: Some( - "timestamptz", - ), - }, - ], - }, - ), - }, -] +error[PreferTimestampTz]: When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time. + ╭▸ +4 │ created_ts timestamp + │ ━━━━━━━━━ + │ + ├ help: Use `timestamptz` instead of `timestamp` for your column type. + ╭╴ +4 │ created_ts timestamptz + ╰╴ ++ +error[PreferTimestampTz]: When Postgres stores a datetime in a `timestamp` field, Postgres drops the UTC offset. This means 2019-10-11 21:11:24+02 and 2019-10-11 21:11:24-06 will both be stored as 2019-10-11 21:11:24 in the database, even though they are eight hours apart in time. + ╭▸ +8 │ created_ts timestamp without time zone + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Use `timestamptz` instead of `timestamp` for your column type. + ╭╴ +8 - created_ts timestamp without time zone +8 + created_ts timestamptz + ╰╴ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap index f2d10f27..e184f73e 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_column__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/renaming_column.rs -expression: errors +expression: "lint_errors(sql, Rule::RenamingColumn)" --- -[ - Violation { - code: RenamingColumn, - message: "Renaming a column may break existing clients.", - text_range: 26..74, - help: None, - fix: None, - }, -] +error[RenamingColumn]: Renaming a column may break existing clients. + ╭▸ +2 │ ALTER TABLE "table_name" RENAME COLUMN "column_name" TO "new_column_name"; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap index 65e6026d..e8052a00 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__renaming_table__test__err.snap @@ -1,13 +1,8 @@ --- source: crates/squawk_linter/src/rules/renaming_table.rs -expression: errors +expression: "lint_errors(sql, Rule::RenamingTable)" --- -[ - Violation { - code: RenamingTable, - message: "Renaming a table may break existing clients.", - text_range: 26..52, - help: None, - fix: None, - }, -] +error[RenamingTable]: Renaming a table may break existing clients. + ╭▸ +2 │ ALTER TABLE "table_name" RENAME TO "new_table_name"; + ╰╴ ━━━━━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap index 5575d2f9..1460a098 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_creation__test__adding_index_non_concurrently_err.snap @@ -1,27 +1,13 @@ --- source: crates/squawk_linter/src/rules/require_concurrent_index_creation.rs -expression: errors +expression: "lint_errors(sql, Rule::RequireConcurrentIndexCreation)" --- -[ - Violation { - code: RequireConcurrentIndexCreation, - message: "During normal index creation, table updates are blocked, but reads are still allowed.", - text_range: 15..75, - help: Some( - "Use `concurrently` to avoid blocking writes.", - ), - fix: Some( - Fix { - title: "Add `concurrently`", - edits: [ - Edit { - text_range: 27..27, - text: Some( - " concurrently", - ), - }, - ], - }, - ), - }, -] +error[RequireConcurrentIndexCreation]: During normal index creation, table updates are blocked, but reads are still allowed. + ╭▸ +3 │ CREATE INDEX "field_name_idx" ON "table_name" ("field_name"); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Use `concurrently` to avoid blocking writes. + ╭╴ +3 │ CREATE INDEX concurrently "field_name_idx" ON "table_name" ("field_name"); + ╰╴ ++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap index 62689296..36e83262 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__require_concurrent_index_deletion__test__drop_index_missing_concurrently_err.snap @@ -1,27 +1,13 @@ --- source: crates/squawk_linter/src/rules/require_concurrent_index_deletion.rs -expression: errors +expression: "lint_errors(sql, Rule::RequireConcurrentIndexDeletion)" --- -[ - Violation { - code: RequireConcurrentIndexDeletion, - message: "A normal `DROP INDEX` acquires an `ACCESS EXCLUSIVE` lock on the table, blocking other accesses until the index drop can complete.", - text_range: 19..56, - help: Some( - "Drop the index `CONCURRENTLY`.", - ), - fix: Some( - Fix { - title: "Add `concurrently`", - edits: [ - Edit { - text_range: 29..29, - text: Some( - " concurrently", - ), - }, - ], - }, - ), - }, -] +error[RequireConcurrentIndexDeletion]: A normal `DROP INDEX` acquires an `ACCESS EXCLUSIVE` lock on the table, blocking other accesses until the index drop can complete. + ╭▸ +3 │ DROP INDEX IF EXISTS "field_name_idx"; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ├ help: Drop the index `CONCURRENTLY`. + ╭╴ +3 │ DROP INDEX concurrently IF EXISTS "field_name_idx"; + ╰╴ ++++++++++++ diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap index 78e27c4a..9f971cec 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_assume_transaction_err.snap @@ -1,33 +1,22 @@ --- source: crates/squawk_linter/src/rules/transaction_nesting.rs -expression: errors +expression: "lint_errors_with(sql, settings)" --- -[ - Violation { - code: TransactionNesting, - message: "There is an existing transaction already in progress, managed by your migration tool.", - text_range: 1..6, - help: Some( - "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", - ), - fix: None, - }, - Violation { - code: TransactionNesting, - message: "There is an existing transaction already in progress, managed by your migration tool.", - text_range: 8..13, - help: Some( - "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", - ), - fix: None, - }, - Violation { - code: TransactionNesting, - message: "Attempting to end the transaction that is managed by your migration tool", - text_range: 25..31, - help: Some( - "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", - ), - fix: None, - }, -] +error[TransactionNesting]: There is an existing transaction already in progress, managed by your migration tool. + ╭▸ +2 │ BEGIN; + │ ━━━━━ + │ + ╰ help: Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting. +error[TransactionNesting]: There is an existing transaction already in progress, managed by your migration tool. + ╭▸ +3 │ BEGIN; + │ ━━━━━ + │ + ╰ help: Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting. +error[TransactionNesting]: Attempting to end the transaction that is managed by your migration tool + ╭▸ +5 │ COMMIT; + │ ━━━━━━ + │ + ╰ help: Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap index c5ecc608..b1fd5cbc 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__begin_repeated_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/transaction_nesting.rs -expression: errors +expression: "lint_errors(sql, Rule::TransactionNesting)" --- -[ - Violation { - code: TransactionNesting, - message: "There is an existing transaction already in progress.", - text_range: 8..13, - help: Some( - "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", - ), - fix: None, - }, -] +error[TransactionNesting]: There is an existing transaction already in progress. + ╭▸ +3 │ BEGIN; + │ ━━━━━ + │ + ╰ help: Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap index 211c4420..3ff9f402 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_repeated_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/transaction_nesting.rs -expression: errors +expression: "lint_errors(sql, Rule::TransactionNesting)" --- -[ - Violation { - code: TransactionNesting, - message: "There is no transaction to `COMMIT` or `ROLLBACK`.", - text_range: 26..32, - help: Some( - "`BEGIN` a transaction at an earlier point in the migration or remove this statement.", - ), - fix: None, - }, -] +error[TransactionNesting]: There is no transaction to `COMMIT` or `ROLLBACK`. + ╭▸ +5 │ COMMIT; + │ ━━━━━━ + │ + ╰ help: `BEGIN` a transaction at an earlier point in the migration or remove this statement. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap index c4250fde..8ed94d6f 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__commit_with_assume_in_transaction_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/transaction_nesting.rs -expression: errors +expression: "lint_errors_with(sql, settings)" --- -[ - Violation { - code: TransactionNesting, - message: "Attempting to end the transaction that is managed by your migration tool", - text_range: 11..17, - help: Some( - "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", - ), - fix: None, - }, -] +error[TransactionNesting]: Attempting to end the transaction that is managed by your migration tool + ╭▸ +3 │ COMMIT; + │ ━━━━━━ + │ + ╰ help: Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap index cec58281..eff187cc 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__transaction_nesting__test__rollback_with_assume_in_transaction_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/transaction_nesting.rs -expression: errors +expression: "lint_errors_with(sql, settings)" --- -[ - Violation { - code: TransactionNesting, - message: "Attempting to end the transaction that is managed by your migration tool", - text_range: 92..100, - help: Some( - "Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting.", - ), - fix: None, - }, -] +error[TransactionNesting]: Attempting to end the transaction that is managed by your migration tool + ╭▸ +4 │ ROLLBACK; + │ ━━━━━━━━ + │ + ╰ help: Put migration statements in separate files to have them be in separate transactions or don't use the assume-in-transaction setting. diff --git a/crates/squawk_linter/src/rules/transaction_nesting.rs b/crates/squawk_linter/src/rules/transaction_nesting.rs index 26c412a4..22f0706e 100644 --- a/crates/squawk_linter/src/rules/transaction_nesting.rs +++ b/crates/squawk_linter/src/rules/transaction_nesting.rs @@ -55,10 +55,18 @@ pub(crate) fn transaction_nesting(ctx: &mut Linter, parse: &Parse) { #[cfg(test)] mod test { - use insta::assert_debug_snapshot; + use insta::assert_snapshot; - use crate::{Linter, Rule}; - use squawk_syntax::SourceFile; + use crate::{LinterSettings, Rule}; + use crate::test_utils::{lint_errors, lint_ok}; + + fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { + crate::test_utils::lint_errors_with(sql, settings, Rule::TransactionNesting) + } + + fn lint_ok_with(sql: &str, settings: LinterSettings) { + crate::test_utils::lint_ok_with(sql, settings, Rule::TransactionNesting); + } #[test] fn begin_repeated_err() { @@ -68,11 +76,7 @@ BEGIN; SELECT 1; COMMIT; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - let errors = linter.lint(&file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::TransactionNesting)); } #[test] @@ -83,11 +87,7 @@ SELECT 1; COMMIT; COMMIT; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - let errors = linter.lint(&file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + assert_snapshot!(lint_errors(sql, Rule::TransactionNesting)); } #[test] @@ -96,12 +96,11 @@ COMMIT; SELECT 1; COMMIT; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - linter.settings.assume_in_transaction = true; - let errors = linter.lint(&file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + let settings = LinterSettings { + assume_in_transaction: true, + ..Default::default() + }; + assert_snapshot!(lint_errors_with(sql, settings)); } #[test] @@ -111,12 +110,11 @@ SELECT 1; -- Not sure why rollback would be used in a migration, but test for completeness ROLLBACK; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - linter.settings.assume_in_transaction = true; - let errors = linter.lint(&file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + let settings = LinterSettings { + assume_in_transaction: true, + ..Default::default() + }; + assert_snapshot!(lint_errors_with(sql, settings)); } #[test] @@ -127,12 +125,11 @@ BEGIN; SELECT 1; COMMIT; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - linter.settings.assume_in_transaction = true; - let errors = linter.lint(&file, sql); - assert_ne!(errors.len(), 0); - assert_debug_snapshot!(errors); + let settings = LinterSettings { + assume_in_transaction: true, + ..Default::default() + }; + assert_snapshot!(lint_errors_with(sql, settings)); } #[test] @@ -142,10 +139,7 @@ BEGIN; SELECT 1; COMMIT; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - let errors = linter.lint(&file, sql); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::TransactionNesting); } #[test] @@ -160,10 +154,7 @@ BEGIN; SELECT 2; COMMIT; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - let errors = linter.lint(&file, sql); - assert_eq!(errors.len(), 0); + lint_ok(sql, Rule::TransactionNesting); } #[test] @@ -171,10 +162,10 @@ COMMIT; let sql = r#" SELECT 1; "#; - let file = SourceFile::parse(sql); - let mut linter = Linter::from([Rule::TransactionNesting]); - linter.settings.assume_in_transaction = true; - let errors = linter.lint(&file, sql); - assert_eq!(errors.len(), 0); + let settings = LinterSettings { + assume_in_transaction: true, + ..Default::default() + }; + lint_ok_with(sql, settings); } } diff --git a/crates/squawk_linter/src/test_utils.rs b/crates/squawk_linter/src/test_utils.rs index 73452655..703dccc3 100644 --- a/crates/squawk_linter/src/test_utils.rs +++ b/crates/squawk_linter/src/test_utils.rs @@ -1,39 +1,61 @@ -use crate::{Edit, Linter, Rule, Violation}; +use crate::{Edit, Linter, LinterSettings, Rule, Violation}; +use annotate_snippets::{AnnotationKind, Level, Patch, Renderer, Snippet, renderer::DecorStyle}; -pub(crate) fn lint(sql: &str, rule: Rule) -> Vec { - let file = squawk_syntax::SourceFile::parse(sql); - assert_eq!(file.errors().len(), 0); - let mut linter = Linter::from([rule]); - linter.lint(&file, sql) +fn lint(sql: &str, rule: Rule) -> Vec { + lint_settings(sql, Default::default(), rule) } -pub(crate) fn lint_with_postgres_version( - sql: &str, - rule: Rule, - postgres_version: &str, -) -> Vec { - let file = squawk_syntax::SourceFile::parse(sql); - assert_eq!(file.errors().len(), 0); - let mut linter = Linter::from([rule]); - linter.settings.pg_version = postgres_version - .parse() - .expect("Invalid PostgreSQL version"); - linter.lint(&file, sql) +pub(crate) fn lint_ok(sql: &str, rule: Rule) { + let errors = lint(sql, rule); + assert_eq!( + errors.len(), + 0, + "Expected no errors. Use `lint_errors` if you want ensure are errors." + ); +} + +#[must_use] +pub(crate) fn lint_errors(sql: &str, rule: Rule) -> String { + let errors = lint(sql, rule); + assert_ne!( + errors.len(), + 0, + "Expected errors. Use `lint_ok` if you want to ensure there aren't errors." + ); + format_violations(sql, &errors) } -pub(crate) fn lint_with_assume_in_transaction(sql: &str, rule: Rule) -> Vec { +#[must_use] +fn lint_settings(sql: &str, settings: LinterSettings, rule: Rule) -> Vec { let file = squawk_syntax::SourceFile::parse(sql); assert_eq!(file.errors().len(), 0); let mut linter = Linter::from([rule]); - linter.settings.assume_in_transaction = true; + linter.settings = settings; linter.lint(&file, sql) } +pub(crate) fn lint_ok_with(sql: &str, settings: LinterSettings, rule: Rule) { + let errors = lint_settings(sql, settings, rule); + assert_eq!( + errors.len(), + 0, + "Expected no errors. Use `lint_errors_with` if you want to ensure there are errors." + ); +} + +#[must_use] +pub(crate) fn lint_errors_with(sql: &str, settings: LinterSettings, rule: Rule) -> String { + let errors = lint_settings(sql, settings, rule); + assert_ne!( + errors.len(), + 0, + "Expected errors. Use `lint_ok_with` if you want to ensure there aren't errors." + ); + format_violations(sql, &errors) +} + pub(crate) fn fix_sql(sql: &str, rule: Rule) -> String { - let file = squawk_syntax::SourceFile::parse(sql); - assert_eq!(file.errors().len(), 0, "Shouldn't start with syntax errors"); - let mut linter = Linter::from([rule]); - let errors = linter.lint(&file, sql); + let errors = lint(sql, rule); assert!(!errors.is_empty(), "Should start with linter errors"); let fixes = errors.into_iter().flat_map(|x| x.fix).collect::>(); @@ -67,3 +89,46 @@ pub(crate) fn fix_sql(sql: &str, rule: Rule) -> String { result } + +fn format_violations(sql: &str, violations: &[Violation]) -> String { + let mut buf = String::new(); + let renderer = Renderer::plain().decor_style(DecorStyle::Unicode); + + for violation in violations { + let start: usize = violation.text_range.start().into(); + let end: usize = violation.text_range.end().into(); + + let snippet = Snippet::source(sql) + .fold(true) + .annotation(AnnotationKind::Primary.span(start..end)); + + let code = format!("{:?}", violation.code); + let mut group = Level::ERROR + .primary_title(&violation.message) + .id(&code) + .element(snippet); + + if let Some(help) = &violation.help { + group = group.element(Level::HELP.message(help)); + } + + if let Some(fix) = &violation.fix { + let mut patch_snippet = Snippet::source(sql).fold(true); + + for edit in &fix.edits { + let edit_start: usize = edit.text_range.start().into(); + let edit_end: usize = edit.text_range.end().into(); + let replacement = edit.text.as_deref().unwrap_or(""); + patch_snippet = patch_snippet.patch(Patch::new(edit_start..edit_end, replacement)); + } + + group = group.element(patch_snippet); + } + + let rendered = renderer.render(&[group]).to_string(); + buf.push_str(&rendered); + buf.push('\n'); + } + + buf +} From 79b4497b3114ae2c21a10ea87bd9c9dc66f8180e Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 20 Dec 2025 21:00:28 -0500 Subject: [PATCH 2/3] lint --- .../src/rules/adding_foreign_key_constraint.rs | 6 +----- ...concurrent_index_creation_in_transaction.rs | 15 +++++++++------ .../src/rules/constraint_missing_not_valid.rs | 18 +++++++++--------- .../src/rules/disallow_unique_constraint.rs | 6 +----- .../src/rules/prefer_robust_stmts.rs | 4 ---- .../rules/require_concurrent_index_creation.rs | 6 +----- .../src/rules/transaction_nesting.rs | 2 +- 7 files changed, 22 insertions(+), 35 deletions(-) diff --git a/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs b/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs index b9b8ba0b..900600d7 100644 --- a/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs +++ b/crates/squawk_linter/src/rules/adding_foreign_key_constraint.rs @@ -80,17 +80,13 @@ pub(crate) fn adding_foreign_key_constraint(ctx: &mut Linter, parse: &Parse String { - crate::test_utils::lint_errors_with(sql, settings, Rule::AddingForeignKeyConstraint) - } - #[test] fn create_table_with_foreign_key_constraint() { let sql = r#" diff --git a/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs b/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs index 14ff7f97..fe556653 100644 --- a/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs +++ b/crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs @@ -44,8 +44,8 @@ pub(crate) fn ban_concurrent_index_creation_in_transaction( mod test { use insta::assert_snapshot; + use crate::test_utils::{lint_errors, lint_ok}; use crate::{LinterSettings, Rule}; - use crate::test_utils::{lint_ok, lint_errors}; fn lint_ok_with(sql: &str, settings: LinterSettings) { crate::test_utils::lint_ok_with( @@ -55,12 +55,12 @@ mod test { ); } - fn lint_errors_with(sql: &str, settings: LinterSettings) { + fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { crate::test_utils::lint_errors_with( sql, settings, Rule::BanConcurrentIndexCreationInTransaction, - ); + ) } #[test] @@ -71,7 +71,10 @@ mod test { CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); COMMIT; "#; - assert_snapshot!(lint_errors(sql, Rule::BanConcurrentIndexCreationInTransaction)); + assert_snapshot!(lint_errors( + sql, + Rule::BanConcurrentIndexCreationInTransaction + )); } #[test] @@ -90,13 +93,13 @@ mod test { CREATE UNIQUE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); ALTER TABLE "table_name" ADD CONSTRAINT "field_name_id" UNIQUE USING INDEX "field_name_idx"; "#; - lint_errors_with( + assert_snapshot!(lint_errors_with( sql, LinterSettings { assume_in_transaction: true, ..Default::default() }, - ); + )); } #[test] diff --git a/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs b/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs index 96e84b7d..d999c2a1 100644 --- a/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs +++ b/crates/squawk_linter/src/rules/constraint_missing_not_valid.rs @@ -148,15 +148,15 @@ pub(crate) fn constraint_missing_not_valid(ctx: &mut Linter, parse: &Parse String { + crate::test_utils::lint_errors_with(sql, settings, Rule::ConstraintMissingNotValid) } #[test] @@ -176,13 +176,13 @@ COMMIT; ALTER TABLE "app_email" ADD CONSTRAINT "fk_user" FOREIGN KEY (user_id) REFERENCES "app_user" (id) NOT VALID; ALTER TABLE "app_email" VALIDATE CONSTRAINT "fk_user"; "#; - lint_errors_with( + assert_snapshot!(lint_errors_with( sql, LinterSettings { assume_in_transaction: true, ..Default::default() }, - ); + )); } #[test] @@ -192,13 +192,13 @@ ALTER TABLE "app_email" ADD CONSTRAINT "fk_user" FOREIGN KEY (user_id) REFERENCE ALTER TABLE "app_email" VALIDATE CONSTRAINT "fk_user"; COMMIT; "#; - lint_errors_with( + assert_snapshot!(lint_errors_with( sql, LinterSettings { assume_in_transaction: true, ..Default::default() }, - ); + )); } #[test] @@ -234,13 +234,13 @@ ALTER TABLE account ADD CONSTRAINT account_pk PRIMARY KEY USING INDEX account_pk -- instead of ALTER TABLE "accounts" ADD CONSTRAINT "positive_balance" CHECK ("balance" >= 0); "#; - lint_errors_with( + assert_snapshot!(lint_errors_with( sql, LinterSettings { assume_in_transaction: true, ..Default::default() }, - ); + )); } #[test] diff --git a/crates/squawk_linter/src/rules/disallow_unique_constraint.rs b/crates/squawk_linter/src/rules/disallow_unique_constraint.rs index f16fa5a0..7e740087 100644 --- a/crates/squawk_linter/src/rules/disallow_unique_constraint.rs +++ b/crates/squawk_linter/src/rules/disallow_unique_constraint.rs @@ -70,17 +70,13 @@ pub(crate) fn disallow_unique_constraint(ctx: &mut Linter, parse: &Parse String { - crate::test_utils::lint_errors_with(sql, settings, Rule::DisallowedUniqueConstraint) - } - #[test] fn adding_unique_constraint_err() { let sql = r#" diff --git a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs index 560a1dfa..9d04cbcd 100644 --- a/crates/squawk_linter/src/rules/prefer_robust_stmts.rs +++ b/crates/squawk_linter/src/rules/prefer_robust_stmts.rs @@ -251,10 +251,6 @@ mod test { crate::test_utils::lint_ok_with(sql, settings, Rule::PreferRobustStmts); } - fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { - crate::test_utils::lint_errors_with(sql, settings, Rule::PreferRobustStmts) - } - #[test] fn fix_drop_type_if_exists() { assert_snapshot!(fix(" diff --git a/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs b/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs index a8c1475d..8dffd8cd 100644 --- a/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs +++ b/crates/squawk_linter/src/rules/require_concurrent_index_creation.rs @@ -50,17 +50,13 @@ mod test { use crate::{ LinterSettings, Rule, - test_utils::{fix_sql, lint_ok, lint_errors}, + test_utils::{fix_sql, lint_errors, lint_ok}, }; fn lint_ok_with(sql: &str, settings: LinterSettings) { crate::test_utils::lint_ok_with(sql, settings, Rule::RequireConcurrentIndexCreation); } - fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { - crate::test_utils::lint_errors_with(sql, settings, Rule::RequireConcurrentIndexCreation) - } - fn fix(sql: &str) -> String { fix_sql(sql, Rule::RequireConcurrentIndexCreation) } diff --git a/crates/squawk_linter/src/rules/transaction_nesting.rs b/crates/squawk_linter/src/rules/transaction_nesting.rs index 22f0706e..f63239f9 100644 --- a/crates/squawk_linter/src/rules/transaction_nesting.rs +++ b/crates/squawk_linter/src/rules/transaction_nesting.rs @@ -57,8 +57,8 @@ pub(crate) fn transaction_nesting(ctx: &mut Linter, parse: &Parse) { mod test { use insta::assert_snapshot; - use crate::{LinterSettings, Rule}; use crate::test_utils::{lint_errors, lint_ok}; + use crate::{LinterSettings, Rule}; fn lint_errors_with(sql: &str, settings: LinterSettings) -> String { crate::test_utils::lint_errors_with(sql, settings, Rule::TransactionNesting) From 58bbc57677e07ecef7ebb50e328a16e8d2b117d7 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 20 Dec 2025 21:03:17 -0500 Subject: [PATCH 3/3] update test --- ...lt__test__docs_example_error_on_pg_11.snap | 19 +++++++------------ ...on__test__assuming_in_transaction_err.snap | 19 +++++++------------ ...id__test__adding_check_constraint_err.snap | 19 +++++++------------ ...valid_validate_assume_transaction_err.snap | 19 +++++++------------ ..._transaction_with_explicit_commit_err.snap | 19 +++++++------------ 5 files changed, 35 insertions(+), 60 deletions(-) diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__docs_example_error_on_pg_11.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__docs_example_error_on_pg_11.snap index 33a984ee..016d041d 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__docs_example_error_on_pg_11.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__adding_field_with_default__test__docs_example_error_on_pg_11.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/adding_field_with_default.rs -expression: errors +expression: "lint_errors_with(sql, LinterSettings\n{\n pg_version: \"11\".parse().expect(\"Invalid PostgreSQL version\"),\n ..Default::default()\n},)" --- -[ - Violation { - code: AddingFieldWithDefault, - message: "Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite.", - text_range: 74..76, - help: Some( - "Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead.", - ), - fix: None, - }, -] +error[AddingFieldWithDefault]: Adding a generated column requires a table rewrite with an `ACCESS EXCLUSIVE` lock. In Postgres versions 11+, non-VOLATILE DEFAULTs can be added without a rewrite. + ╭▸ +3 │ ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10; + │ ━━ + │ + ╰ help: Add the column as nullable, backfill existing rows, and add a trigger to update the column on write instead. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap index 0d42ea57..ac0128b8 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__ban_concurrent_index_creation_in_transaction__test__assuming_in_transaction_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/ban_concurrent_index_creation_in_transaction.rs -expression: errors +expression: "lint_errors_with(sql, LinterSettings\n{ assume_in_transaction: true, ..Default::default() },)" --- -[ - Violation { - code: BanConcurrentIndexCreationInTransaction, - message: "While regular index creation can happen inside a transaction, this is not allowed when the `CONCURRENTLY` option is used.", - text_range: 39..51, - help: Some( - "Build the index outside any transactions.", - ), - fix: None, - }, -] +error[BanConcurrentIndexCreationInTransaction]: While regular index creation can happen inside a transaction, this is not allowed when the `CONCURRENTLY` option is used. + ╭▸ +3 │ CREATE UNIQUE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); + │ ━━━━━━━━━━━━ + │ + ╰ help: Build the index outside any transactions. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap index e7047dd3..163bf5eb 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__adding_check_constraint_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/constraint_missing_not_valid.rs -expression: errors +expression: "lint_errors_with(sql, LinterSettings\n{ assume_in_transaction: true, ..Default::default() },)" --- -[ - Violation { - code: ConstraintMissingNotValid, - message: "By default new constraints require a table scan and block writes to the table while that scan occurs.", - text_range: 38..94, - help: Some( - "Use `NOT VALID` with a later `VALIDATE CONSTRAINT` call.", - ), - fix: None, - }, -] +error[ConstraintMissingNotValid]: By default new constraints require a table scan and block writes to the table while that scan occurs. + ╭▸ +3 │ ALTER TABLE "accounts" ADD CONSTRAINT "positive_balance" CHECK ("balance" >= 0); + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Use `NOT VALID` with a later `VALIDATE CONSTRAINT` call. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap index 64886572..e44ab58d 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_assume_transaction_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/constraint_missing_not_valid.rs -expression: errors +expression: "lint_errors_with(sql, LinterSettings\n{ assume_in_transaction: true, ..Default::default() },)" --- -[ - Violation { - code: ConstraintMissingNotValid, - message: "Using `NOT VALID` and `VALIDATE CONSTRAINT` in the same transaction will block all reads while the constraint is validated.", - text_range: 134..163, - help: Some( - "Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.", - ), - fix: None, - }, -] +error[ConstraintMissingNotValid]: Using `NOT VALID` and `VALIDATE CONSTRAINT` in the same transaction will block all reads while the constraint is validated. + ╭▸ +3 │ ALTER TABLE "app_email" VALIDATE CONSTRAINT "fk_user"; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction. diff --git a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap index 64886572..e44ab58d 100644 --- a/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap +++ b/crates/squawk_linter/src/rules/snapshots/squawk_linter__rules__constraint_missing_not_valid__test__not_valid_validate_with_assume_in_transaction_with_explicit_commit_err.snap @@ -1,15 +1,10 @@ --- source: crates/squawk_linter/src/rules/constraint_missing_not_valid.rs -expression: errors +expression: "lint_errors_with(sql, LinterSettings\n{ assume_in_transaction: true, ..Default::default() },)" --- -[ - Violation { - code: ConstraintMissingNotValid, - message: "Using `NOT VALID` and `VALIDATE CONSTRAINT` in the same transaction will block all reads while the constraint is validated.", - text_range: 134..163, - help: Some( - "Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.", - ), - fix: None, - }, -] +error[ConstraintMissingNotValid]: Using `NOT VALID` and `VALIDATE CONSTRAINT` in the same transaction will block all reads while the constraint is validated. + ╭▸ +3 │ ALTER TABLE "app_email" VALIDATE CONSTRAINT "fk_user"; + │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + │ + ╰ help: Add constraint as `NOT VALID` in one transaction and `VALIDATE CONSTRAINT` in a separate transaction.