From 070280bdf7fb5d08f658d0dedf68ccb6744e5256 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 9 Nov 2021 12:16:10 +0700 Subject: [PATCH] feat: create index method --- bob.go | 20 ++++ create_index.go | 127 +++++++++++++++++++++++++ create_index_test.go | 91 ++++++++++++++++++ create.go => create_table.go | 0 create_test.go => create_table_test.go | 0 5 files changed, 238 insertions(+) create mode 100644 create_index.go create mode 100644 create_index_test.go rename create.go => create_table.go (100%) rename create_test.go => create_table_test.go (100%) diff --git a/bob.go b/bob.go index a8a4470..001424d 100644 --- a/bob.go +++ b/bob.go @@ -40,6 +40,16 @@ func (b BobBuilderType) CreateTableIfNotExists(table string) CreateBuilder { return CreateBuilder(b).name(table).ifNotExists() } +// CreateIndex creates an index with CreateIndexBuilder interface. +func (b BobBuilderType) CreateIndex(name string) IndexBuilder { + return IndexBuilder(b).name(name) +} + +// CreateIndexIfNotExists creates an index with CreateIndexBuilder interface, if the index doesn't exists. +func (b BobBuilderType) CreateIndexIfNotExists(name string) IndexBuilder { + return IndexBuilder(b).name(name).ifNotExists() +} + // HasTable checks if a table exists with HasBuilder interface func (b BobBuilderType) HasTable(table string) HasBuilder { return HasBuilder(b).HasTable(table) @@ -172,3 +182,13 @@ func Truncate(table string) TruncateBuilder { func Upsert(table string, dialect int) UpsertBuilder { return BobStmtBuilder.Upsert(table, dialect) } + +// CreateIndex creates an index with CreateIndexBuilder interface. +func CreateIndex(name string) IndexBuilder { + return BobStmtBuilder.CreateIndex(name) +} + +// CreateIndexIfNotExists creates an index with CreateIndexBuilder interface, if the index doesn't exists. +func CreateIndexIfNotExists(name string) IndexBuilder { + return BobStmtBuilder.CreateIndexIfNotExists(name) +} \ No newline at end of file diff --git a/create_index.go b/create_index.go new file mode 100644 index 0000000..ab008b3 --- /dev/null +++ b/create_index.go @@ -0,0 +1,127 @@ +package bob + +import ( + "errors" + "strings" + + "github.com/lann/builder" +) + +type IndexBuilder builder.Builder + +type indexData struct { + Unique bool + Spatial bool + Fulltext bool + Name string + TableName string + Columns []IndexColumn + IfNotExists bool +} + +type IndexColumn struct { + Name string + Extras []string + Collate string +} + +func init() { + builder.Register(IndexBuilder{}, indexData{}) +} + +func (i IndexBuilder) Unique() IndexBuilder { + return builder.Set(i, "Unique", true).(IndexBuilder) +} + +func (i IndexBuilder) Spatial() IndexBuilder { + return builder.Set(i, "Spatial", true).(IndexBuilder) +} + +func (i IndexBuilder) Fulltext() IndexBuilder { + return builder.Set(i, "Fulltext", true).(IndexBuilder) +} + +func (i IndexBuilder) name(name string) IndexBuilder { + return builder.Set(i, "Name", name).(IndexBuilder) +} + +func (i IndexBuilder) ifNotExists() IndexBuilder { + return builder.Set(i, "IfNotExists", true).(IndexBuilder) +} + +func (i IndexBuilder) On(table string) IndexBuilder { + return builder.Set(i, "TableName", table).(IndexBuilder) +} + +func (i IndexBuilder) Columns(column IndexColumn) IndexBuilder { + return builder.Append(i, "Columns", column).(IndexBuilder) +} + +func (i IndexBuilder) ToSql() (string, []interface{}, error) { + data := builder.GetStruct(i).(indexData) + return data.ToSql() +} + +func (i *indexData) ToSql() (sqlStr string, args []interface{}, err error) { + if i.Name == "" { + err = errors.New("index name is required on create index statement") + return + } + if i.TableName == "" { + err = errors.New("a table name must be specified on create index statement") + return + } + + if len(i.Columns) == 0 { + err = errors.New("should at least specify one column for create index statement") + return + } + + var sql strings.Builder + + sql.WriteString("CREATE ") + + if i.Unique { + sql.WriteString("UNIQUE ") + } + + if i.Fulltext { + sql.WriteString("FULLTEXT ") + } + + if i.Spatial { + sql.WriteString("SPATIAL ") + } + + sql.WriteString("INDEX ") + + if i.IfNotExists { + sql.WriteString("IF NOT EXISTS ") + } + + sql.WriteString(i.Name + " ") + + sql.WriteString("ON ") + + sql.WriteString(i.TableName + " ") + + var columns []string + for _, column := range i.Columns { + var colBuilder strings.Builder + colBuilder.WriteString(column.Name) + if column.Collate != "" { + colBuilder.WriteString(" COLLATE " + column.Collate) + } + if len(column.Extras) > 0 { + colBuilder.WriteString(" " + strings.Join(column.Extras, " ")) + } + columns = append(columns, colBuilder.String()) + } + + sql.WriteString("(") + sql.WriteString(strings.Join(columns, ", ")) + sql.WriteString(");") + + sqlStr = sql.String() + return +} \ No newline at end of file diff --git a/create_index_test.go b/create_index_test.go new file mode 100644 index 0000000..daa499b --- /dev/null +++ b/create_index_test.go @@ -0,0 +1,91 @@ +package bob_test + +import ( + "testing" + + "github.com/aldy505/bob" +) + +func TestCreateIndex(t *testing.T) { + sql, _, err := bob. + CreateIndexIfNotExists("email_idx"). + On("users"). + Unique(). + Spatial(). + Fulltext(). + Columns(bob.IndexColumn{Name: "email"}). + ToSql() + if err != nil { + t.Fatal(err.Error()) + } + + result := "CREATE UNIQUE FULLTEXT SPATIAL INDEX IF NOT EXISTS email_idx ON users (email);" + if sql != result { + t.Fatal("sql is not equal to result:", sql) + } +} + +func TestCreateIndex_Simple(t *testing.T) { + sql, _, err := bob. + CreateIndex("idx_email"). + On("users"). + Columns(bob.IndexColumn{Name: "email", Collate: "DEFAULT", Extras: []string{"ASC"}}). + Columns(bob.IndexColumn{Name: "name", Extras: []string{"DESC"}}). + ToSql() + if err != nil { + t.Fatal(err.Error()) + } + + result := "CREATE INDEX idx_email ON users (email COLLATE DEFAULT ASC, name DESC);" + if sql != result { + t.Fatal("sql is not equal to result:", sql) + } +} + +func TestCreateIndex_Error(t *testing.T) { + t.Run("without index", func(t *testing.T) { + _, _, err := bob. + CreateIndex(""). + On("users"). + Columns(bob.IndexColumn{Name: "email", Collate: "DEFAULT", Extras: []string{"ASC"}}). + Columns(bob.IndexColumn{Name: "name", Extras: []string{"DESC"}}). + ToSql() + if err == nil { + t.Fatal("error is nil") + } + + if err.Error() != "index name is required on create index statement" { + t.Fatal("error is not equal to result:", err.Error()) + } + }) + + t.Run("without table name", func(t *testing.T) { + _, _, err := bob. + CreateIndex("name"). + On(""). + Columns(bob.IndexColumn{Name: "email", Collate: "DEFAULT", Extras: []string{"ASC"}}). + Columns(bob.IndexColumn{Name: "name", Extras: []string{"DESC"}}). + ToSql() + if err == nil { + t.Fatal("error is nil") + } + + if err.Error() != "a table name must be specified on create index statement" { + t.Fatal("error is not equal to result:", err.Error()) + } + }) + + t.Run("without columns", func(t *testing.T) { + _, _, err := bob. + CreateIndex("name"). + On("users"). + ToSql() + if err == nil { + t.Fatal("error is nil") + } + + if err.Error() != "should at least specify one column for create index statement" { + t.Fatal("error is not equal to result:", err.Error()) + } + }) +} \ No newline at end of file diff --git a/create.go b/create_table.go similarity index 100% rename from create.go rename to create_table.go diff --git a/create_test.go b/create_table_test.go similarity index 100% rename from create_test.go rename to create_table_test.go