feat: initial column definition types

This commit is contained in:
Reinaldy Rafli 2021-07-21 14:50:05 +07:00
parent 86eb9e0935
commit ca2d51bc7a
2 changed files with 163 additions and 74 deletions

170
create.go
View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"strings" "strings"
"github.com/aldy505/bob/util"
"github.com/lann/builder" "github.com/lann/builder"
) )
@ -15,11 +14,13 @@ type createData struct {
TableName string TableName string
IfNotExists bool IfNotExists bool
Schema string Schema string
Columns []string Columns []ColumnDef
Types []string }
Primary string
Unique string type ColumnDef struct {
NotNull []string Name string
Type string
Extras []string
} }
func init() { func init() {
@ -41,24 +42,129 @@ func (b CreateBuilder) WithSchema(name string) CreateBuilder {
return builder.Set(b, "Schema", name).(CreateBuilder) return builder.Set(b, "Schema", name).(CreateBuilder)
} }
// Columns sets the column names // StringColumn creates a column with VARCHAR(255) data type.
func (b CreateBuilder) Columns(cols ...string) CreateBuilder { // For SQLite please refer to TextColumn.
return builder.Set(b, "Columns", cols).(CreateBuilder) func (b CreateBuilder) StringColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "VARCHAR(255)",
Extras: extras,
}).(CreateBuilder)
} }
// Types set a type for certain column // TextColumn creates a column with TEXT data type
func (b CreateBuilder) Types(types ...string) CreateBuilder { func (b CreateBuilder) TextColumn(name string, extras ...string) CreateBuilder {
return builder.Set(b, "Types", types).(CreateBuilder) return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "TEXT",
Extras: extras,
}).(CreateBuilder)
} }
// Primary will set that column as the primary key for a table. // UUIDColumn only available for PostgreSQL
func (b CreateBuilder) Primary(column string) CreateBuilder { func (b CreateBuilder) UUIDColumn(name string, extras ...string) CreateBuilder {
return builder.Set(b, "Primary", column).(CreateBuilder) return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "UUID",
Extras: extras,
}).(CreateBuilder)
} }
// Unique adds an unique index to a table over the given columns. // BooleanColumn only available for PostgreSQL
func (b CreateBuilder) Unique(column string) CreateBuilder { func (b CreateBuilder) BooleanColumn(name string, extras ...string) CreateBuilder {
return builder.Set(b, "Unique", column).(CreateBuilder) return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "BOOLEAN",
Extras: extras,
}).(CreateBuilder)
}
// IntegerColumn only available for PostgreSQL and SQLite.
// For MySQL and MSSQL, please refer to IntColumn,
func (b CreateBuilder) IntegerColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "INTEGER",
Extras: extras,
}).(CreateBuilder)
}
// IntColumn only available for MySQL and MSSQL.
// For PostgreSQL and SQLite please refer to IntegerColumn.
func (b CreateBuilder) IntColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "INT",
Extras: extras,
}).(CreateBuilder)
}
func (b CreateBuilder) DateTimeColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "DATETIME",
Extras: extras,
}).(CreateBuilder)
}
func (b CreateBuilder) TimeStampColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "TIMESTAMP",
Extras: extras,
}).(CreateBuilder)
}
func (b CreateBuilder) TimeColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "TIME",
Extras: extras,
}).(CreateBuilder)
}
func (b CreateBuilder) DateColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "DATE",
Extras: extras,
}).(CreateBuilder)
}
// JSONColumn only available for MySQL and PostgreSQL.
// For MSSQL please use AddColumn(bob.ColumnDef{Name: "name", Type: "NVARCHAR(1000)"}).
// Not supported for SQLite.
func (b CreateBuilder) JSONColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "JSON",
Extras: extras,
}).(CreateBuilder)
}
// JSONBColumn only available for PostgreSQL.
// For MySQL please refer to JSONColumn.
func (b CreateBuilder) JSONBColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "JSONB",
Extras: extras,
}).(CreateBuilder)
}
// BlobColumn only available for MySQL and SQLite.
// For PostgreSQL and MSSQL, please use AddColumn(bob.ColumnDef{Name: "name", Type: "BYTEA"}).
func (b CreateBuilder) BlobColumn(name string, extras ...string) CreateBuilder {
return builder.Append(b, "Columns", ColumnDef{
Name: name,
Type: "BLOB",
Extras: extras,
}).(CreateBuilder)
}
// AddColumn sets custom columns
func (b CreateBuilder) AddColumn(column ColumnDef) CreateBuilder {
return builder.Append(b, "Columns", column).(CreateBuilder)
} }
// ToSql returns 3 variables filled out with the correct values based on bindings, etc. // ToSql returns 3 variables filled out with the correct values based on bindings, etc.
@ -74,8 +180,8 @@ func (d *createData) ToSql() (sqlStr string, args []interface{}, err error) {
return return
} }
if (len(d.Columns) != len(d.Types)) && len(d.Columns) > 0 { if len(d.Columns) == 0 {
err = errors.New("columns and types should have equal length") err = errors.New("a table should at least have one column")
return return
} }
@ -96,30 +202,18 @@ func (d *createData) ToSql() (sqlStr string, args []interface{}, err error) {
var columnTypes []string var columnTypes []string
for i := 0; i < len(d.Columns); i++ { for i := 0; i < len(d.Columns); i++ {
columnTypes = append(columnTypes, "\""+d.Columns[i]+"\" "+d.Types[i]) var column []string
column = append(column, "\""+d.Columns[i].Name+"\" "+d.Columns[i].Type)
if len(d.Columns[i].Extras) > 0 {
column = append(column, strings.Join(d.Columns[i].Extras, " "))
}
columnTypes = append(columnTypes, strings.Join(column, " "))
} }
sql.WriteString("(") sql.WriteString("(")
sql.WriteString(strings.Join(columnTypes, ", ")) sql.WriteString(strings.Join(columnTypes, ", "))
sql.WriteString(");") sql.WriteString(");")
if d.Primary != "" {
if !util.IsIn(d.Columns, d.Primary) {
err = errors.New("supplied primary column name doesn't exists on columns")
return
}
sql.WriteString(" ")
sql.WriteString("ALTER TABLE \"" + d.TableName + "\" ADD PRIMARY KEY (\"" + d.Primary + "\");")
}
if d.Unique != "" {
if !util.IsIn(d.Columns, d.Unique) {
err = errors.New("supplied unique column name doesn't exists on columns")
return
}
sql.WriteString(" ")
sql.WriteString("ALTER TABLE \"" + d.TableName + "\" ADD UNIQUE (\"" + d.Unique + "\");")
}
sqlStr = sql.String() sqlStr = sql.String()
return return
} }

View File

@ -8,11 +8,16 @@ import (
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
t.Run("should return correct sql string with basic columns and types", func(t *testing.T) { t.Run("should return correct sql string with basic columns and types", func(t *testing.T) {
sql, _, err := bob.CreateTable("users").Columns("name", "password", "date").Types("varchar(255)", "text", "date").ToSql() sql, _, err := bob.
CreateTable("users").
StringColumn("name").
TextColumn("password").
DateColumn("date").
ToSql()
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
result := "CREATE TABLE \"users\" (\"name\" varchar(255), \"password\" text, \"date\" date);" result := "CREATE TABLE \"users\" (\"name\" VARCHAR(255), \"password\" TEXT, \"date\" DATE);"
if sql != result { if sql != result {
t.Fatal("sql is not equal to result:", sql) t.Fatal("sql is not equal to result:", sql)
} }
@ -20,68 +25,58 @@ func TestCreate(t *testing.T) {
t.Run("should return correct sql with primary key and unique key", func(t *testing.T) { t.Run("should return correct sql with primary key and unique key", func(t *testing.T) {
sql, _, err := bob.CreateTable("users"). sql, _, err := bob.CreateTable("users").
Columns("id", "name", "email", "password", "date"). UUIDColumn("id", "PRIMARY KEY").
Types("uuid", "varchar(255)", "varchar(255)", "text", "date"). StringColumn("name").
Primary("id"). StringColumn("email", "UNIQUE").
Unique("email"). TextColumn("password").
DateTimeColumn("date").
ToSql() ToSql()
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
result := "CREATE TABLE \"users\" (\"id\" uuid, \"name\" varchar(255), \"email\" varchar(255), \"password\" text, \"date\" date); ALTER TABLE \"users\" ADD PRIMARY KEY (\"id\"); ALTER TABLE \"users\" ADD UNIQUE (\"email\");" result := "CREATE TABLE \"users\" (\"id\" UUID PRIMARY KEY, \"name\" VARCHAR(255), \"email\" VARCHAR(255) UNIQUE, \"password\" TEXT, \"date\" DATETIME);"
if sql != result { if sql != result {
t.Fatal("sql is not equal to result:", sql) t.Fatal("sql is not equal to result:", sql)
} }
}) })
t.Run("should be able to have a schema name", func(t *testing.T) { t.Run("should be able to have a schema name", func(t *testing.T) {
sql, _, err := bob.CreateTable("users").WithSchema("private").Columns("name", "password", "date").Types("varchar(255)", "text", "date").ToSql() sql, _, err := bob.
CreateTable("users").
WithSchema("private").
StringColumn("name").
TextColumn("password").
DateColumn("date").
ToSql()
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
result := "CREATE TABLE \"private\".\"users\" (\"name\" varchar(255), \"password\" text, \"date\" date);" result := "CREATE TABLE \"private\".\"users\" (\"name\" VARCHAR(255), \"password\" TEXT, \"date\" DATE);"
if sql != result { if sql != result {
t.Fatal("sql is not equal to result:", sql) t.Fatal("sql is not equal to result:", sql)
} }
}) })
t.Run("should emit error on unmatched column and types length", func(t *testing.T) {
_, _, err := bob.CreateTable("users").
Columns("id", "name", "email", "password", "date").
Types("uuid", "varchar(255)", "varchar(255)", "date").
ToSql()
if err.Error() != "columns and types should have equal length" {
t.Fatal("should throw an error, it didn't:", err.Error())
}
})
t.Run("should emit error on empty table name", func(t *testing.T) { t.Run("should emit error on empty table name", func(t *testing.T) {
_, _, err := bob.CreateTable("").Columns("name").Types("text").ToSql() _, _, err := bob.
CreateTable("").
StringColumn("name").
ToSql()
if err.Error() != "create statements must specify a table" { if err.Error() != "create statements must specify a table" {
t.Fatal("should throw an error, it didn't:", err.Error()) t.Fatal("should throw an error, it didn't:", err.Error())
} }
}) })
t.Run("should emit error for primary key not in columns", func(t *testing.T) {
_, _, err := bob.CreateTable("users").Columns("name").Types("text").Primary("id").ToSql()
if err.Error() != "supplied primary column name doesn't exists on columns" {
t.Fatal("should throw an error, it didn't:", err.Error())
}
})
t.Run("should emit error for unique key not in columns", func(t *testing.T) {
_, _, err := bob.CreateTable("users").Columns("name").Types("text").Unique("id").ToSql()
if err.Error() != "supplied unique column name doesn't exists on columns" {
t.Fatal("should throw an error, it didn't:", err.Error())
}
})
t.Run("should emit create if not exists", func(t *testing.T) { t.Run("should emit create if not exists", func(t *testing.T) {
sql, _, err := bob.CreateTableIfNotExists("users").Columns("name").Types("text").ToSql() sql, _, err := bob.
CreateTableIfNotExists("users").
TextColumn("name").
ToSql()
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatal(err.Error())
} }
result := "CREATE TABLE IF NOT EXISTS \"users\" (\"name\" text);" result := "CREATE TABLE IF NOT EXISTS \"users\" (\"name\" TEXT);"
if sql != result { if sql != result {
t.Fatal("sql is not equal to result: ", sql) t.Fatal("sql is not equal to result: ", sql)
} }