Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b12cfc0
refactor: analyse
psteinroe Dec 14, 2025
dc098c0
fix: dont use dyn
psteinroe Dec 14, 2025
6fae041
fix: correct AnalyserRules reference in codegen
psteinroe Dec 15, 2025
4e9757f
fix: update test imports to use LinterOptions and LinterDiagnostic
psteinroe Dec 15, 2025
4f16e91
style: apply rustfmt to test files
psteinroe Dec 15, 2025
4798b24
chore: integrate splinter into codegen
psteinroe Dec 14, 2025
821dd3a
fix: update bun.lock and remove unused mut
psteinroe Dec 15, 2025
0379759
chore: rebase onto refactor/rules with test import fixes
psteinroe Dec 15, 2025
cc43d1f
fix: use relative paths in splinter registry codegen
psteinroe Dec 15, 2025
85b1802
fix: remove unused RuleMeta import and apply clippy fixes
psteinroe Dec 15, 2025
a197a12
fix: apply clippy format! macro fix in convert.rs
psteinroe Dec 15, 2025
5f46e53
fix: panic on unknown splinter rule instead of fallback
psteinroe Dec 15, 2025
df44cfd
fix: apply formatting
psteinroe Dec 15, 2025
8c982d8
chore: integrate splinter runtime with analyse
psteinroe Dec 15, 2025
13afe90
fix: resolve configuration.rs from parent branch
psteinroe Dec 15, 2025
e2950b5
fix: strip outer parentheses from SQL queries before UNION ALL
psteinroe Dec 15, 2025
7a2d56e
fix: wrap queries in SELECT * FROM to handle CTEs in UNION ALL
psteinroe Dec 15, 2025
947426b
fix: add alias to subqueries in FROM clause
psteinroe Dec 15, 2025
404bab9
fix: simplify SQL query combination in run_splinter
psteinroe Dec 15, 2025
a8c551c
fix: ensure all SQL queries are wrapped in parentheses for UNION ALL
psteinroe Dec 15, 2025
7dd9257
fix: use column names with ! suffix in FromRow implementation
psteinroe Dec 15, 2025
3f06f59
fix: add ORDER BY to combined SQL for deterministic result ordering
psteinroe Dec 15, 2025
d00534e
test: update snapshots with corrected formatting
psteinroe Dec 15, 2025
0b27360
chore: apply clippy formatting suggestions
psteinroe Dec 15, 2025
22cfd84
chore(splinter): integrate into docs codegen (#621)
psteinroe Dec 16, 2025
56484d9
Merge main into chore/integrate-splinter-runtime
psteinroe Dec 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/pgls_analyser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub use linter_rule::{LinterDiagnostic, LinterRule};
pub use LinterDiagnostic as RuleDiagnostic;
pub use LinterRule as Rule;
pub use LinterRuleContext as RuleContext;

pub static METADATA: LazyLock<MetadataRegistry> = LazyLock::new(|| {
let mut metadata = MetadataRegistry::default();
// Use a separate visitor for metadata that implements pgls_analyse::RegistryVisitor
Expand Down
10 changes: 5 additions & 5 deletions crates/pgls_analyser/src/lint/safety/add_serial_column.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -38,10 +38,10 @@ declare_lint_rule! {
}
}

impl Rule for AddSerialColumn {
impl LinterRule for AddSerialColumn {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
Expand All @@ -56,7 +56,7 @@ impl Rule for AddSerialColumn {
let type_str = get_type_name(type_name);
if is_serial_type(&type_str) {
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down Expand Up @@ -86,7 +86,7 @@ impl Rule for AddSerialColumn {

if has_stored_generated {
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -39,10 +39,10 @@ declare_lint_rule! {
}
}

impl Rule for AddingFieldWithDefault {
impl LinterRule for AddingFieldWithDefault {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

// Check PostgreSQL version - in 11+, non-volatile defaults are safe
Expand Down Expand Up @@ -75,7 +75,7 @@ impl Rule for AddingFieldWithDefault {

if has_generated {
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand All @@ -102,7 +102,7 @@ impl Rule for AddingFieldWithDefault {

if !is_safe_default {
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand All @@ -116,7 +116,7 @@ impl Rule for AddingFieldWithDefault {
} else {
// Pre PG 11, all defaults cause rewrites
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -43,10 +43,10 @@ declare_lint_rule! {
}
}

impl Rule for AddingForeignKeyConstraint {
impl LinterRule for AddingForeignKeyConstraint {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
Expand Down Expand Up @@ -95,7 +95,7 @@ impl Rule for AddingForeignKeyConstraint {
fn check_foreign_key_constraint(
constraint: &pgls_query::protobuf::Constraint,
is_column_constraint: bool,
) -> Option<RuleDiagnostic> {
) -> Option<LinterDiagnostic> {
// Only check foreign key constraints
if constraint.contype() != pgls_query::protobuf::ConstrType::ConstrForeign {
return None;
Expand All @@ -121,7 +121,7 @@ fn check_foreign_key_constraint(
};

Some(
RuleDiagnostic::new(rule_category!(), None, markup! { {message} })
LinterDiagnostic::new(rule_category!(), None, markup! { {message} })
.detail(None, detail)
.note(note),
)
Expand Down
8 changes: 4 additions & 4 deletions crates/pgls_analyser/src/lint/safety/adding_not_null_field.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -42,10 +42,10 @@ declare_lint_rule! {
}
}

impl Rule for AddingNotNullField {
impl LinterRule for AddingNotNullField {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

// In Postgres 11+, this is less of a concern
Expand All @@ -60,7 +60,7 @@ impl Rule for AddingNotNullField {
for cmd in &stmt.cmds {
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node {
if cmd.subtype() == pgls_query::protobuf::AlterTableType::AtSetNotNull {
diagnostics.push(RuleDiagnostic::new(
diagnostics.push(LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -40,10 +40,10 @@ declare_lint_rule! {
}
}

impl Rule for AddingPrimaryKeyConstraint {
impl LinterRule for AddingPrimaryKeyConstraint {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
Expand Down Expand Up @@ -92,12 +92,12 @@ impl Rule for AddingPrimaryKeyConstraint {

fn check_for_primary_key_constraint(
constraint: &pgls_query::protobuf::Constraint,
) -> Option<RuleDiagnostic> {
) -> Option<LinterDiagnostic> {
if constraint.contype() == pgls_query::protobuf::ConstrType::ConstrPrimary
&& constraint.indexname.is_empty()
{
Some(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
8 changes: 4 additions & 4 deletions crates/pgls_analyser/src/lint/safety/adding_required_field.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand All @@ -25,10 +25,10 @@ declare_lint_rule! {
}
}

impl Rule for AddingRequiredField {
impl LinterRule for AddingRequiredField {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = vec![];

if let pgls_query::NodeEnum::AlterTableStmt(stmt) = ctx.stmt() {
Expand All @@ -47,7 +47,7 @@ impl Rule for AddingRequiredField {
== pgls_query::protobuf::AlterTableType::AtAddColumn
{
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
12 changes: 7 additions & 5 deletions crates/pgls_analyser/src/lint/safety/ban_char_field.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -41,10 +41,10 @@ declare_lint_rule! {
}
}

impl Rule for BanCharField {
impl LinterRule for BanCharField {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

if let pgls_query::NodeEnum::CreateStmt(stmt) = &ctx.stmt() {
Expand Down Expand Up @@ -78,7 +78,9 @@ impl Rule for BanCharField {
}
}

fn check_column_for_char_type(col_def: &pgls_query::protobuf::ColumnDef) -> Option<RuleDiagnostic> {
fn check_column_for_char_type(
col_def: &pgls_query::protobuf::ColumnDef,
) -> Option<LinterDiagnostic> {
if let Some(type_name) = &col_def.type_name {
for name_node in &type_name.names {
if let Some(pgls_query::NodeEnum::String(name)) = &name_node.node {
Expand All @@ -87,7 +89,7 @@ fn check_column_for_char_type(col_def: &pgls_query::protobuf::ColumnDef) -> Opti
let type_str = name.sval.to_lowercase();
if type_str == "bpchar" || type_str == "char" || type_str == "character" {
return Some(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -27,10 +27,10 @@ declare_lint_rule! {
}
}

impl Rule for BanConcurrentIndexCreationInTransaction {
impl LinterRule for BanConcurrentIndexCreationInTransaction {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

// check if the current statement is CREATE INDEX CONCURRENTLY and there is at least one
Expand All @@ -39,7 +39,7 @@ impl Rule for BanConcurrentIndexCreationInTransaction {
// since our analyser assumes we're always in a transaction context, we always flag concurrent indexes
if let pgls_query::NodeEnum::IndexStmt(stmt) = ctx.stmt() {
if stmt.concurrent && ctx.file_context().stmt_count() > 1 {
diagnostics.push(RuleDiagnostic::new(
diagnostics.push(LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
8 changes: 4 additions & 4 deletions crates/pgls_analyser/src/lint/safety/ban_drop_column.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -27,17 +27,17 @@ declare_lint_rule! {
}
}

impl Rule for BanDropColumn {
impl LinterRule for BanDropColumn {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
for cmd in &stmt.cmds {
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node {
if cmd.subtype() == pgls_query::protobuf::AlterTableType::AtDropColumn {
diagnostics.push(RuleDiagnostic::new(
diagnostics.push(LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
8 changes: 4 additions & 4 deletions crates/pgls_analyser/src/lint/safety/ban_drop_database.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand All @@ -16,15 +16,15 @@ declare_lint_rule! {
}
}

impl Rule for BanDropDatabase {
impl LinterRule for BanDropDatabase {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = vec![];

if let pgls_query::NodeEnum::DropdbStmt(_) = &ctx.stmt() {
diagnostics.push(
RuleDiagnostic::new(
LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
8 changes: 4 additions & 4 deletions crates/pgls_analyser/src/lint/safety/ban_drop_not_null.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Rule, RuleContext, RuleDiagnostic};
use crate::{LinterDiagnostic, LinterRule, LinterRuleContext};
use pgls_analyse::{RuleSource, declare_lint_rule};
use pgls_console::markup;
use pgls_diagnostics::Severity;
Expand Down Expand Up @@ -27,17 +27,17 @@ declare_lint_rule! {
}
}

impl Rule for BanDropNotNull {
impl LinterRule for BanDropNotNull {
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Vec<RuleDiagnostic> {
fn run(ctx: &LinterRuleContext<Self>) -> Vec<LinterDiagnostic> {
let mut diagnostics = Vec::new();

if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() {
for cmd in &stmt.cmds {
if let Some(pgls_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node {
if cmd.subtype() == pgls_query::protobuf::AlterTableType::AtDropNotNull {
diagnostics.push(RuleDiagnostic::new(
diagnostics.push(LinterDiagnostic::new(
rule_category!(),
None,
markup! {
Expand Down
Loading