sqlite_lint: add support for FTS5 and virtual tables
Some checks failed
CI / build-docker (push) Successful in 5s
CI / build-docker-bootstrap (push) Has been skipped
CI / release-test (push) Failing after 6s

This commit is contained in:
wispem-wantex 2026-01-17 09:00:18 +09:00
parent d70cbc1913
commit 0371fb4144
11 changed files with 68 additions and 36 deletions

3
ops/compile.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
go build -tags fts5 -o gas ./cmd

View File

@ -13,7 +13,8 @@ cd "$(dirname "${BASH_SOURCE[0]}")/.."
# Compile `gas` # Compile `gas`
gas="/tmp/gas" gas="/tmp/gas"
go build -o $gas ./cmd ops/compile.sh
mv gas $gas
test_project="/memory/test_gasproj" test_project="/memory/test_gasproj"
if [[ -e $test_project ]]; then if [[ -e $test_project ]]; then

View File

@ -13,7 +13,8 @@ cd "$(dirname "${BASH_SOURCE[0]}")/.."
# Compile `gas` # Compile `gas`
gas="/tmp/gas" gas="/tmp/gas"
go build -o $gas ./cmd ops/compile.sh
mv gas $gas
test_schema_dir="pkg/schema/lint/test_schemas" test_schema_dir="pkg/schema/lint/test_schemas"

View File

@ -76,7 +76,7 @@ func GenerateModelAST(table schema.Table) *ast.GenDecl {
return &ast.GenDecl{ return &ast.GenDecl{
Tok: token.TYPE, Tok: token.TYPE,
Specs: []ast.Spec{&ast.TypeSpec{ Specs: []ast.Spec{&ast.TypeSpec{
Name: ast.NewIdent(table.TypeName), Name: ast.NewIdent(table.GoTypeName),
Type: &ast.StructType{Fields: &ast.FieldList{List: fields}}, Type: &ast.StructType{Fields: &ast.FieldList{List: fields}},
}}, }},
} }
@ -171,7 +171,7 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
Op: token.NEQ, Op: token.NEQ,
Y: &ast.BasicLit{Kind: token.INT, Value: "1"}, Y: &ast.BasicLit{Kind: token.INT, Value: "1"},
}, },
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ExprStmt{X: &ast.CallExpr{Fun: ast.NewIdent("panic"), Args: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("fmt.Errorf"), Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"got %s with ID (%%d), so attempted update, but it doesn't exist\"", strings.ToLower(tbl.TypeName))}, &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")}}}}}}}}, Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ExprStmt{X: &ast.CallExpr{Fun: ast.NewIdent("panic"), Args: []ast.Expr{&ast.CallExpr{Fun: ast.NewIdent("fmt.Errorf"), Args: []ast.Expr{&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"got %s with ID (%%d), so attempted update, but it doesn't exist\"", strings.ToLower(tbl.GoTypeName))}, &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")}}}}}}}},
}, },
}, },
}, },
@ -181,9 +181,9 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
funcDecl := &ast.FuncDecl{ funcDecl := &ast.FuncDecl{
Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}}, Recv: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}},
Name: ast.NewIdent("Save" + tbl.TypeName), Name: ast.NewIdent("Save" + tbl.GoTypeName),
Type: &ast.FuncType{ Type: &ast.FuncType{
Params: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: &ast.StarExpr{X: ast.NewIdent(tbl.TypeName)}}}}, Params: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: &ast.StarExpr{X: ast.NewIdent(tbl.GoTypeName)}}}},
Results: nil, Results: nil,
}, },
Body: funcBody, Body: funcBody,
@ -194,11 +194,11 @@ func GenerateSaveItemFunc(tbl schema.Table) *ast.FuncDecl {
// GenerateGetItemByIDFunc produces an AST for the `GetXyzByID()` function. // GenerateGetItemByIDFunc produces an AST for the `GetXyzByID()` function.
// E.g., a table with `table.TypeName = "foods"` will produce a "GetFoodByID()" function. // E.g., a table with `table.TypeName = "foods"` will produce a "GetFoodByID()" function.
func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl { func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
funcName := "Get" + tbl.TypeName + "ByID" funcName := "Get" + tbl.GoTypeName + "ByID"
recv := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}} recv := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}}
arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("id")}, Type: ast.NewIdent(tbl.TypeIDName)}}} arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("id")}, Type: ast.NewIdent(tbl.TypeIDName)}}}
result := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: ast.NewIdent(tbl.TypeName)}, {Names: []*ast.Ident{ast.NewIdent("err")}, Type: ast.NewIdent("error")}}} result := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: ast.NewIdent(tbl.GoTypeName)}, {Names: []*ast.Ident{ast.NewIdent("err")}, Type: ast.NewIdent("error")}}}
// Use the xyzSQLFields constant in the select query // Use the xyzSQLFields constant in the select query
selectExpr := &ast.BinaryExpr{ selectExpr := &ast.BinaryExpr{
@ -220,7 +220,7 @@ func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
}, },
&ast.IfStmt{ &ast.IfStmt{
Cond: &ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}}}, Cond: &ast.CallExpr{Fun: &ast.SelectorExpr{X: ast.NewIdent("errors"), Sel: ast.NewIdent("Is")}, Args: []ast.Expr{ast.NewIdent("err"), &ast.SelectorExpr{X: ast.NewIdent("sql"), Sel: ast.NewIdent("ErrNoRows")}}},
Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{&ast.CompositeLit{Type: ast.NewIdent(tbl.TypeName)}, ast.NewIdent("ErrNotInDB")}}}}, Body: &ast.BlockStmt{List: []ast.Stmt{&ast.ReturnStmt{Results: []ast.Expr{&ast.CompositeLit{Type: ast.NewIdent(tbl.GoTypeName)}, ast.NewIdent("ErrNotInDB")}}}},
}, },
&ast.ReturnStmt{}, &ast.ReturnStmt{},
}, },
@ -238,12 +238,12 @@ func GenerateGetItemByIDFunc(tbl schema.Table) *ast.FuncDecl {
// GenerateGetAllItemsFunc produces an AST for the `GetAllXyzs()` function. // GenerateGetAllItemsFunc produces an AST for the `GetAllXyzs()` function.
// E.g., a table with `table.TypeName = "foods"` will produce a "GetAllFoods()" function. // E.g., a table with `table.TypeName = "foods"` will produce a "GetAllFoods()" function.
func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl { func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
funcName := "GetAll" + inflection.Plural(tbl.TypeName) funcName := "GetAll" + inflection.Plural(tbl.GoTypeName)
recv := &ast.FieldList{List: []*ast.Field{ recv := &ast.FieldList{List: []*ast.Field{
{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}, {Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")},
}} }}
result := &ast.FieldList{List: []*ast.Field{ result := &ast.FieldList{List: []*ast.Field{
{Names: []*ast.Ident{ast.NewIdent("ret")}, Type: &ast.ArrayType{Elt: ast.NewIdent(tbl.TypeName)}}, {Names: []*ast.Ident{ast.NewIdent("ret")}, Type: &ast.ArrayType{Elt: ast.NewIdent(tbl.GoTypeName)}},
}} }}
selectCall := &ast.CallExpr{ selectCall := &ast.CallExpr{
@ -291,9 +291,9 @@ func GenerateGetAllItemsFunc(tbl schema.Table) *ast.FuncDecl {
// GenerateDeleteItemFunc produces an AST for the `DeleteXyz()` function. // GenerateDeleteItemFunc produces an AST for the `DeleteXyz()` function.
// E.g., a table with `table.TypeName = "foods"` will produce a "DeleteFood()" function. // E.g., a table with `table.TypeName = "foods"` will produce a "DeleteFood()" function.
func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl { func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
funcName := "Delete" + tbl.TypeName funcName := "Delete" + tbl.GoTypeName
recv := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}} recv := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent("db")}, Type: ast.NewIdent("DB")}}}
arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: ast.NewIdent(tbl.TypeName)}}} arg := &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{ast.NewIdent(tbl.VarName)}, Type: ast.NewIdent(tbl.GoTypeName)}}}
funcBody := &ast.BlockStmt{ funcBody := &ast.BlockStmt{
List: []ast.Stmt{ List: []ast.Stmt{
@ -328,7 +328,7 @@ func GenerateDeleteItemFunc(tbl schema.Table) *ast.FuncDecl {
Args: []ast.Expr{&ast.CallExpr{ Args: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("fmt.Errorf"), Fun: ast.NewIdent("fmt.Errorf"),
Args: []ast.Expr{ Args: []ast.Expr{
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"tried to delete %s with ID (%%d) but it doesn't exist\"", strings.ToLower(tbl.TypeName))}, &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"tried to delete %s with ID (%%d) but it doesn't exist\"", strings.ToLower(tbl.GoTypeName))},
&ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")}, &ast.SelectorExpr{X: ast.NewIdent(tbl.VarName), Sel: ast.NewIdent("ID")},
}, },
}}, }},
@ -372,5 +372,5 @@ func GenerateSQLFieldsConst(tbl schema.Table) *ast.GenDecl {
// --------------- // ---------------
func SQLFieldsConstIdent(tbl schema.Table) *ast.Ident { func SQLFieldsConstIdent(tbl schema.Table) *ast.Ident {
return ast.NewIdent(strings.ToLower(tbl.TypeName) + "SQLFields") return ast.NewIdent(strings.ToLower(tbl.GoTypeName) + "SQLFields")
} }

View File

@ -90,7 +90,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
description2 := `"a big item"` description2 := `"a big item"`
testCreateUpdateDelete := &ast.FuncDecl{ testCreateUpdateDelete := &ast.FuncDecl{
Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.TypeName), Name: ast.NewIdent("TestCreateUpdateDelete" + tbl.GoTypeName),
Type: &ast.FuncType{ Type: &ast.FuncType{
Params: &ast.FieldList{ Params: &ast.FieldList{
List: []*ast.Field{{ List: []*ast.Field{{
@ -106,7 +106,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
Lhs: []ast.Expr{testObj}, Lhs: []ast.Expr{testObj},
Tok: token.DEFINE, Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CompositeLit{ Rhs: []ast.Expr{&ast.CompositeLit{
Type: ast.NewIdent(tbl.TypeName), Type: ast.NewIdent(tbl.GoTypeName),
Elts: []ast.Expr{ Elts: []ast.Expr{
&ast.KeyValueExpr{ &ast.KeyValueExpr{
Key: fieldName, Key: fieldName,
@ -118,7 +118,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
// TestDB.SaveItem(&item) // TestDB.SaveItem(&item)
&ast.ExprStmt{X: &ast.CallExpr{ &ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Save" + tbl.TypeName), Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}}, Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}}, }},
@ -135,7 +135,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
Rhs: []ast.Expr{&ast.CallExpr{ Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("Must"), Fun: ast.NewIdent("Must"),
Args: []ast.Expr{&ast.CallExpr{ Args: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.TypeName + "ByID"), Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}}, Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}}, }},
}}, }},
@ -160,7 +160,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
// TestDB.SaveItem(&item) // TestDB.SaveItem(&item)
&ast.ExprStmt{X: &ast.CallExpr{ &ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Save" + tbl.TypeName), Fun: ast.NewIdent("TestDB.Save" + tbl.GoTypeName),
Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}}, Args: []ast.Expr{&ast.UnaryExpr{Op: token.AND, X: testObj}},
}}, }},
@ -171,7 +171,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
Rhs: []ast.Expr{&ast.CallExpr{ Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("Must"), Fun: ast.NewIdent("Must"),
Args: []ast.Expr{&ast.CallExpr{ Args: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.TypeName + "ByID"), Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}}, Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}}, }},
}}, }},
@ -189,7 +189,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
// TestDB.DeleteItem(item) // TestDB.DeleteItem(item)
&ast.ExprStmt{X: &ast.CallExpr{ &ast.ExprStmt{X: &ast.CallExpr{
Fun: ast.NewIdent("TestDB.Delete" + tbl.TypeName), Fun: ast.NewIdent("TestDB.Delete" + tbl.GoTypeName),
Args: []ast.Expr{testObj}, Args: []ast.Expr{testObj},
}}, }},
@ -198,7 +198,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")}, Lhs: []ast.Expr{ast.NewIdent("_"), ast.NewIdent("err")},
Tok: token.DEFINE, Tok: token.DEFINE,
Rhs: []ast.Expr{&ast.CallExpr{ Rhs: []ast.Expr{&ast.CallExpr{
Fun: ast.NewIdent("TestDB.Get" + tbl.TypeName + "ByID"), Fun: ast.NewIdent("TestDB.Get" + tbl.GoTypeName + "ByID"),
Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}}, Args: []ast.Expr{&ast.SelectorExpr{X: testObj, Sel: ast.NewIdent("ID")}},
}}, }},
}, },
@ -217,7 +217,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
} }
testGetAll := &ast.FuncDecl{ testGetAll := &ast.FuncDecl{
Name: ast.NewIdent("TestGetAll" + inflection.Plural(tbl.TypeName)), Name: ast.NewIdent("TestGetAll" + inflection.Plural(tbl.GoTypeName)),
Type: &ast.FuncType{Params: &ast.FieldList{List: []*ast.Field{ Type: &ast.FuncType{Params: &ast.FieldList{List: []*ast.Field{
{Names: []*ast.Ident{ast.NewIdent("t")}, Type: &ast.StarExpr{X: ast.NewIdent("testing.T")}}, {Names: []*ast.Ident{ast.NewIdent("t")}, Type: &ast.StarExpr{X: ast.NewIdent("testing.T")}},
}}, Results: nil}, }}, Results: nil},
@ -229,7 +229,7 @@ func GenerateModelTestAST(tbl schema.Table, gomodName string) *ast.File {
Rhs: []ast.Expr{&ast.CallExpr{ Rhs: []ast.Expr{&ast.CallExpr{
Fun: &ast.SelectorExpr{ Fun: &ast.SelectorExpr{
X: ast.NewIdent("TestDB"), X: ast.NewIdent("TestDB"),
Sel: ast.NewIdent("GetAll" + inflection.Plural(tbl.TypeName)), Sel: ast.NewIdent("GetAll" + inflection.Plural(tbl.GoTypeName)),
}, },
}}, }},
}, },

View File

@ -23,7 +23,10 @@ var Checks = []Check{
Explanation: "All columns should be marked as `not null` unless they are foreign keys. (Primary keys are\n" + Explanation: "All columns should be marked as `not null` unless they are foreign keys. (Primary keys are\n" +
"automatically not-null, and don't need to be specified.)", "automatically not-null, and don't need to be specified.)",
Execute: func(s schema.Schema) (ret []CheckResult) { Execute: func(s schema.Schema) (ret []CheckResult) {
for tablename := range s.Tables { for tablename, tbl := range s.Tables {
if tbl.TableType != "table" {
continue
}
for _, column := range s.Tables[tablename].Columns { for _, column := range s.Tables[tablename].Columns {
if !column.IsNotNull && !column.IsForeignKey && !column.IsPrimaryKey { if !column.IsNotNull && !column.IsForeignKey && !column.IsPrimaryKey {
ret = append(ret, CheckResult{ ret = append(ret, CheckResult{
@ -43,7 +46,10 @@ var Checks = []Check{
"integer, real, text, blob or any). This disallows all 'date' and 'time' column types.\n" + "integer, real, text, blob or any). This disallows all 'date' and 'time' column types.\n" +
"See more: https://www.sqlite.org/stricttables.html", "See more: https://www.sqlite.org/stricttables.html",
Execute: func(s schema.Schema) (ret []CheckResult) { Execute: func(s schema.Schema) (ret []CheckResult) {
for tablename := range s.Tables { for tablename, tbl := range s.Tables {
if tbl.TableType != "table" {
continue
}
if !s.Tables[tablename].IsStrict { if !s.Tables[tablename].IsStrict {
ret = append(ret, CheckResult{ ret = append(ret, CheckResult{
ErrorMsg: "Table should be marked \"strict\"", ErrorMsg: "Table should be marked \"strict\"",
@ -78,7 +84,10 @@ var Checks = []Check{
Explanation: "All tables must have a primary key. If it's rowid, it has to be named explicitly.", Explanation: "All tables must have a primary key. If it's rowid, it has to be named explicitly.",
Execute: func(s schema.Schema) (ret []CheckResult) { Execute: func(s schema.Schema) (ret []CheckResult) {
tableloop: tableloop:
for tablename := range s.Tables { for tablename, tbl := range s.Tables {
if tbl.TableType != "table" {
continue
}
for _, column := range s.Tables[tablename].Columns { for _, column := range s.Tables[tablename].Columns {
if column.IsPrimaryKey { if column.IsPrimaryKey {
continue tableloop continue tableloop
@ -161,6 +170,9 @@ var Checks = []Check{
Execute: func(s schema.Schema) (ret []CheckResult) { Execute: func(s schema.Schema) (ret []CheckResult) {
tbl_loop: tbl_loop:
for tblName, tbl := range s.Tables { for tblName, tbl := range s.Tables {
if tbl.TableType != "table" {
continue
}
if tbl.IsWithoutRowid { if tbl.IsWithoutRowid {
continue continue
} }

View File

@ -5,6 +5,14 @@ create table stuff (
) strict; ) strict;
create index index_stuff_amount on stuff (amount); create index index_stuff_amount on stuff (amount);
create virtual table stuff_fts using fts5(
data,
content='stuff',
content_rowid='rowid',
tokenize='trigram'
);
create table stuff2 ( create table stuff2 (
weird_pk integer primary key, weird_pk integer primary key,
label text not null unique, label text not null unique,

View File

@ -38,10 +38,10 @@ func SchemaFromDB(db *sqlx.DB) Schema {
ret := Schema{Tables: map[string]Table{}, Indexes: map[string]Index{}} ret := Schema{Tables: map[string]Table{}, Indexes: map[string]Index{}}
var tables []Table var tables []Table
PanicIf(db.Select(&tables, `select name, is_strict, is_without_rowid from tables`)) PanicIf(db.Select(&tables, `select name, table_type, is_strict, is_without_rowid from tables`))
for _, tbl := range tables { for _, tbl := range tables {
tbl.TypeName = TypenameFromTablename(tbl.TableName) tbl.GoTypeName = TypenameFromTablename(tbl.TableName)
tbl.TypeIDName = tbl.TypeName + "ID" tbl.TypeIDName = tbl.GoTypeName + "ID"
tbl.VarName = strings.ToLower(string(tbl.TableName[0])) tbl.VarName = strings.ToLower(string(tbl.TableName[0]))
PanicIf(db.Select(&tbl.Columns, `select * from columns where table_name = ?`, tbl.TableName)) PanicIf(db.Select(&tbl.Columns, `select * from columns where table_name = ?`, tbl.TableName))

View File

@ -25,7 +25,7 @@ func TestParseSchema(t *testing.T) {
foods := schema.Tables["foods"] foods := schema.Tables["foods"]
assert.Equal(foods.TableName, "foods") assert.Equal(foods.TableName, "foods")
assert.Equal(foods.TypeName, "Food") assert.Equal(foods.GoTypeName, "Food")
assert.Equal(foods.TypeIDName, "FoodID") assert.Equal(foods.TypeIDName, "FoodID")
assert.Equal(foods.IsStrict, true) assert.Equal(foods.IsStrict, true)
assert.Len(foods.Columns, 20) assert.Len(foods.Columns, 20)

View File

@ -23,14 +23,21 @@ func (c Column) IsNullableForeignKey() bool {
// Table is a single SQLite table. // Table is a single SQLite table.
type Table struct { type Table struct {
TableName string `db:"name"` TableName string `db:"name"`
// One of "table", "view", "shadow", or "virtual"
TableType string `db:"table_type"`
IsStrict bool `db:"is_strict"` IsStrict bool `db:"is_strict"`
IsWithoutRowid bool `db:"is_without_rowid"` IsWithoutRowid bool `db:"is_without_rowid"`
Columns []Column Columns []Column
TypeIDName string TypeIDName string
// Default variable name for variables of this type to use when generating Go code
VarName string VarName string
TypeName string
// Name of corresponding model type to be generated
GoTypeName string
} }
type Index struct { type Index struct {

View File

@ -1,7 +1,7 @@
create temporary view tables as create temporary view tables as
select l.schema, select l.schema,
l.name, l.name,
l.type, l.type as table_type,
l.wr as is_without_rowid, l.wr as is_without_rowid,
l.strict as is_strict l.strict as is_strict
from sqlite_schema s from sqlite_schema s