Skip to content

Commit

Permalink
support filtering table definitions (#39)
Browse files Browse the repository at this point in the history
* add `Table.DefinitionFilter` to filter table definitions

* update method signature

* fix test

* remove switch with single case

* add test

* improve test order

* improve `Table.filterDefinition`

* rename method

* output log when filtering out table definitions

* add log output to `Table.definitions`

* DefinitionFilter returns the reason why it excludes a method definition

* simpler log output
  • Loading branch information
youpy committed Oct 10, 2022
1 parent 2a66d51 commit 1a68ed2
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 5 deletions.
35 changes: 30 additions & 5 deletions builder/table.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package builder

import (
"log"
"strconv"

"github.com/go-rel/rel"
)

type ColumnMapper func(*rel.Column) (string, int, int)
type DefinitionFilter func(table rel.Table, def rel.TableDefinition) bool

// Table builder.
type Table struct {
BufferFactory BufferFactory
ColumnMapper ColumnMapper
BufferFactory BufferFactory
ColumnMapper ColumnMapper
DefinitionFilter DefinitionFilter
}

// Build SQL query for table creation and modification.
Expand All @@ -36,17 +39,19 @@ func (t Table) Build(table rel.Table) string {

// WriteCreateTable query to buffer.
func (t Table) WriteCreateTable(buffer *Buffer, table rel.Table) {
defs := t.definitions(table)

buffer.WriteString("CREATE TABLE ")

if table.Optional {
buffer.WriteString("IF NOT EXISTS ")
}

buffer.WriteEscape(table.Name)
if len(table.Definitions) > 0 {
if len(defs) > 0 {
buffer.WriteString(" (")

for i, def := range table.Definitions {
for i, def := range defs {
if i > 0 {
buffer.WriteString(", ")
}
Expand All @@ -68,7 +73,9 @@ func (t Table) WriteCreateTable(buffer *Buffer, table rel.Table) {

// WriteAlterTable query to buffer.
func (t Table) WriteAlterTable(buffer *Buffer, table rel.Table) {
for _, def := range table.Definitions {
defs := t.definitions(table)

for _, def := range defs {
buffer.WriteString("ALTER TABLE ")
buffer.WriteEscape(table.Name)
buffer.WriteByte(' ')
Expand Down Expand Up @@ -228,3 +235,21 @@ func (t Table) WriteOptions(buffer *Buffer, options string) {
buffer.WriteByte(' ')
buffer.WriteString(options)
}

func (t Table) definitions(table rel.Table) []rel.TableDefinition {
if t.DefinitionFilter == nil {
return table.Definitions
}

result := []rel.TableDefinition{}

for _, def := range table.Definitions {
if t.DefinitionFilter(table, def) {
result = append(result, def)
} else {
log.Printf("[REL] An unsupported table definition has been excluded: %T", def)
}
}

return result
}
79 changes: 79 additions & 0 deletions builder/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,82 @@ func TestTable_Build(t *testing.T) {
})
}
}

func TestTable_BuildWithDefinitionFilter(t *testing.T) {
var (
definitionFilter = func(table rel.Table, def rel.TableDefinition) bool {
_, ok := def.(rel.Key)
// https://www.sqlite.org/omitted.html
// > Only the RENAME TABLE, ADD COLUMN, RENAME COLUMN, and DROP COLUMN variants of the ALTER TABLE command are supported.
if ok && table.Op == rel.SchemaAlter {
return false
}

return true
}
tableBuilder = Table{
BufferFactory: BufferFactory{InlineValues: true, BoolTrueValue: "true", BoolFalseValue: "false", Quoter: Quote{IDPrefix: "`", IDSuffix: "`", IDSuffixEscapeChar: "`", ValueQuote: "'", ValueQuoteEscapeChar: "'"}},
ColumnMapper: sql.ColumnMapper,
DefinitionFilter: definitionFilter,
}
)

tests := []struct {
result string
table rel.Table
}{
{
result: "CREATE TABLE `columns` (`bool` BOOL NOT NULL DEFAULT false, `int` INT(11) UNSIGNED, `bigint` BIGINT(20) UNSIGNED, `float` FLOAT(24) UNSIGNED, `decimal` DECIMAL(6,2) UNSIGNED, `string` VARCHAR(144) UNIQUE, `text` TEXT(1000), `date` DATE, `datetime` DATETIME DEFAULT '2020-01-01 01:00:00', `time` TIME, `blob` blob, PRIMARY KEY (`int`), FOREIGN KEY (`int`, `string`) REFERENCES `products` (`id`, `name`) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE `date_unique` (`date`)) Engine=InnoDB;",
table: rel.Table{
Op: rel.SchemaCreate,
Name: "columns",
Definitions: []rel.TableDefinition{
rel.Column{Name: "bool", Type: rel.Bool, Required: true, Default: false},
rel.Column{Name: "int", Type: rel.Int, Limit: 11, Unsigned: true},
rel.Column{Name: "bigint", Type: rel.BigInt, Limit: 20, Unsigned: true},
rel.Column{Name: "float", Type: rel.Float, Precision: 24, Unsigned: true},
rel.Column{Name: "decimal", Type: rel.Decimal, Precision: 6, Scale: 2, Unsigned: true},
rel.Column{Name: "string", Type: rel.String, Limit: 144, Unique: true},
rel.Column{Name: "text", Type: rel.Text, Limit: 1000},
rel.Column{Name: "date", Type: rel.Date},
rel.Column{Name: "datetime", Type: rel.DateTime, Default: time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC)},
rel.Column{Name: "time", Type: rel.Time},
rel.Column{Name: "blob", Type: "blob"},
rel.Key{Columns: []string{"int"}, Type: rel.PrimaryKey},
rel.Key{Columns: []string{"int", "string"}, Type: rel.ForeignKey, Reference: rel.ForeignKeyReference{Table: "products", Columns: []string{"id", "name"}, OnDelete: "CASCADE", OnUpdate: "CASCADE"}},
rel.Key{Columns: []string{"date"}, Name: "date_unique", Type: rel.UniqueKey},
},
Options: "Engine=InnoDB",
},
},
{
result: "ALTER TABLE `columns` ADD COLUMN `verified` BOOL;ALTER TABLE `columns` RENAME COLUMN `string` TO `name`;ALTER TABLE `columns` ;ALTER TABLE `columns` DROP COLUMN `blob`;",
table: rel.Table{
Op: rel.SchemaAlter,
Name: "columns",
Definitions: []rel.TableDefinition{
rel.Column{Name: "verified", Type: rel.Bool, Op: rel.SchemaCreate},
rel.Column{Name: "string", Rename: "name", Op: rel.SchemaRename},
rel.Column{Name: "bool", Type: rel.Int, Op: rel.SchemaAlter},
rel.Column{Name: "blob", Op: rel.SchemaDrop},
},
},
},
{
result: "",
table: rel.Table{
Op: rel.SchemaAlter,
Name: "transactions",
Definitions: []rel.TableDefinition{
rel.Key{Op: rel.SchemaCreate, Columns: []string{"user_id"}, Type: rel.ForeignKey, Reference: rel.ForeignKeyReference{Table: "products", Columns: []string{"id", "name"}, OnDelete: "CASCADE", OnUpdate: "CASCADE"}},
},
},
},
}

for _, test := range tests {
t.Run(test.result, func(t *testing.T) {
assert.Equal(t, test.result, tableBuilder.Build(test.table))
})
}
}

0 comments on commit 1a68ed2

Please sign in to comment.