diff --git a/create.go b/create.go index 4017b65..aafe4ee 100644 --- a/create.go +++ b/create.go @@ -5,7 +5,6 @@ import ( "errors" "strings" - "github.com/aldy505/bob/util" "github.com/lann/builder" ) @@ -15,11 +14,13 @@ type createData struct { TableName string IfNotExists bool Schema string - Columns []string - Types []string - Primary string - Unique string - NotNull []string + Columns []ColumnDef +} + +type ColumnDef struct { + Name string + Type string + Extras []string } func init() { @@ -41,24 +42,129 @@ func (b CreateBuilder) WithSchema(name string) CreateBuilder { return builder.Set(b, "Schema", name).(CreateBuilder) } -// Columns sets the column names -func (b CreateBuilder) Columns(cols ...string) CreateBuilder { - return builder.Set(b, "Columns", cols).(CreateBuilder) +// StringColumn creates a column with VARCHAR(255) data type. +// For SQLite please refer to TextColumn. +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 -func (b CreateBuilder) Types(types ...string) CreateBuilder { - return builder.Set(b, "Types", types).(CreateBuilder) +// TextColumn creates a column with TEXT data type +func (b CreateBuilder) TextColumn(name string, extras ...string) 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. -func (b CreateBuilder) Primary(column string) CreateBuilder { - return builder.Set(b, "Primary", column).(CreateBuilder) +// UUIDColumn only available for PostgreSQL +func (b CreateBuilder) UUIDColumn(name string, extras ...string) 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. -func (b CreateBuilder) Unique(column string) CreateBuilder { - return builder.Set(b, "Unique", column).(CreateBuilder) +// BooleanColumn only available for PostgreSQL +func (b CreateBuilder) BooleanColumn(name string, extras ...string) 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. @@ -74,8 +180,8 @@ func (d *createData) ToSql() (sqlStr string, args []interface{}, err error) { return } - if (len(d.Columns) != len(d.Types)) && len(d.Columns) > 0 { - err = errors.New("columns and types should have equal length") + if len(d.Columns) == 0 { + err = errors.New("a table should at least have one column") return } @@ -96,30 +202,18 @@ func (d *createData) ToSql() (sqlStr string, args []interface{}, err error) { var columnTypes []string 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(strings.Join(columnTypes, ", ")) 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() return } diff --git a/create_test.go b/create_test.go index 988f6e3..a87b5bf 100644 --- a/create_test.go +++ b/create_test.go @@ -8,11 +8,16 @@ import ( func TestCreate(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 { 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 { 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) { sql, _, err := bob.CreateTable("users"). - Columns("id", "name", "email", "password", "date"). - Types("uuid", "varchar(255)", "varchar(255)", "text", "date"). - Primary("id"). - Unique("email"). + UUIDColumn("id", "PRIMARY KEY"). + StringColumn("name"). + StringColumn("email", "UNIQUE"). + TextColumn("password"). + DateTimeColumn("date"). ToSql() + if err != nil { 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 { t.Fatal("sql is not equal to result:", sql) } }) 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 { 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 { 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) { - _, _, err := bob.CreateTable("").Columns("name").Types("text").ToSql() + _, _, err := bob. + CreateTable(""). + StringColumn("name"). + ToSql() if err.Error() != "create statements must specify a table" { 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) { - sql, _, err := bob.CreateTableIfNotExists("users").Columns("name").Types("text").ToSql() + sql, _, err := bob. + CreateTableIfNotExists("users"). + TextColumn("name"). + ToSql() if err != nil { 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 { t.Fatal("sql is not equal to result: ", sql) }