Compare commits

...

2 Commits

Author SHA1 Message Date
dbb43567d7 Fix a bug where non-rowid primary keys would be identified as 'not indexed' for foreign key indexes rule
Some checks failed
Build / build (push) Failing after 7m1s
Build / test-action (push) Successful in 3m7s
2025-07-31 22:35:37 -07:00
740767ea11 Make it use the gas stack instead of implementing its own schema reading 2025-07-31 22:34:46 -07:00
6 changed files with 33 additions and 43 deletions

3
go.mod
View File

@ -3,6 +3,9 @@ module github.com/playfulpachyderm/sqlite-lint
go 1.24.0
require (
git.offline-twitter.com/offline-labs/gas-stack v0.0.0-20250712192928-1b04b71d9d02
github.com/jmoiron/sqlx v1.4.0
github.com/mattn/go-sqlite3 v1.14.28
)
require github.com/jinzhu/inflection v1.0.0 // indirect

12
go.sum
View File

@ -1,7 +1,13 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.offline-twitter.com/offline-labs/gas-stack v0.0.0-20250712192928-1b04b71d9d02 h1:WAJJkEcRBe3E6uujBcQrYxI2Y5O/Dcx4U7z+JLBjUF4=
git.offline-twitter.com/offline-labs/gas-stack v0.0.0-20250712192928-1b04b71d9d02/go.mod h1:0NdKdzmgSOGyh7Q7VvElVYkB0E2xyjeZBDRbsBkF/G4=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
@ -9,3 +15,9 @@ github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -41,7 +41,7 @@ var Checks = map[string]Check{
column_name
from columns
where columns."notnull" = 0
and fk_target_column is null
and is_foreign_key = 0
and is_primary_key = 0 -- primary keys are automatically not-null, but aren't listed as such in pragma_table_info
`,
Explanation: "All columns should be marked as `not null` unless they are foreign keys. (Primary keys are\n" +
@ -55,7 +55,7 @@ var Checks = map[string]Check{
// column_name
// from columns
// where dflt_value is null
// and fk_target_column is null
// and is_foreign_key = 0
// and is_primary_key = 0;
// `,
// Explanation: "All columns should have a default value specified, unless they are foreign keys or primary keys.",
@ -104,16 +104,15 @@ var Checks = map[string]Check{
from tables
join pragma_index_list(tables.name) as indexes
join pragma_index_info(indexes.name) as columns
union
select table_name,
column_name
from columns
where column_name = 'rowid'
and is_primary_key != 0 -- 'pk' is either 0, or the 1-based index of the column within the primary key
where is_primary_key != 0 -- 'pk' is either 0, or the 1-based index of the column within the primary key
), foreign_keys as (
select * from columns where fk_target_column is not null
select * from columns where is_foreign_key = 1
)
select 'Foreign keys should point to indexed columns' as error_msg,
foreign_keys.table_name as table_name,

View File

@ -54,7 +54,8 @@ func TestFailureCases(t *testing.T) {
}
func TestSuccessCase(t *testing.T) {
db, err := checks.OpenSchema("../../test_schemas/success.sql")
file := "../../test_schemas/success.sql"
db, err := checks.OpenSchema(file)
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
@ -64,8 +65,8 @@ func TestSuccessCase(t *testing.T) {
if err != nil {
t.Errorf("failed to execute check '%s': %v", check.Name, err)
}
if len(results) > 0 {
t.Errorf("Should have passed, but didn't: %s", "test_schemas/success.sql")
for _, r := range results {
t.Errorf("Unexpected error in file %q: %#v", file, r)
}
}
}

View File

@ -6,47 +6,17 @@ import (
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
"git.offline-twitter.com/offline-labs/gas-stack/pkg/schema"
)
// OpenSchema opens a SQLite database in memory, executes the schema against it, and adds some views
func OpenSchema(filepath string) (*sqlx.DB, error) {
// Open a SQLite database in memory
db, err := sqlx.Open("sqlite3", ":memory:")
if err != nil {
return nil, fmt.Errorf("failed to open in-memory database: %w", err)
}
// Read the SQL file
sqlBytes, err := os.ReadFile(filepath)
if err != nil {
return nil, fmt.Errorf("failed to read SQL file: %w", err)
}
// Execute the SQL statements
db.MustExec(string(sqlBytes))
// Execute the SQL statements for creating views
db.MustExec(`
create view tables as
select l.*
from sqlite_schema s
left join pragma_table_list l on s.name = l.name
where s.type = 'table';
create view columns as
select tables.name as table_name,
table_info.name as column_name,
table_info.type as column_type,
"notnull",
dflt_value,
pk as is_primary_key,
fk."table" as fk_target_table,
fk."to" as fk_target_column
from tables
join pragma_table_info(tables.name) as table_info
left join pragma_foreign_key_list(tables.name) as fk on fk."from" = column_name;
`)
return db, nil
return schema.InitDB(string(sqlBytes)), nil
}

View File

@ -11,3 +11,8 @@ create table stuff2 (
stuff_id integer references stuff(rowid),
alternative_stuff_id integer references stuff(amount)
) strict;
create table stuff3 (
weird_pk3 integer primary key,
stuff2_id integer not null references stuff2(weird_pk)
) strict;