Skip to content

Commit

Permalink
fix: error message
Browse files Browse the repository at this point in the history
  • Loading branch information
alexisvisco committed Oct 28, 2024
1 parent d394b8a commit 9ebd289
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 52 deletions.
4 changes: 2 additions & 2 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
Expand All @@ -176,7 +177,7 @@ golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
Expand All @@ -185,7 +186,6 @@ golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
Expand Down
42 changes: 42 additions & 0 deletions pkg/schema/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"strings"
)

// Schema is the base schema. It is used to support unknown database types and provide a default implementation.
Expand Down Expand Up @@ -115,3 +116,44 @@ func (p *Schema) FindAppliedVersions() []string {

return versions
}

func ColumnType(ctx *schema.MigratorContext, options schema.ColumnData) func() string {
return func() string {
strBuilder := strings.Builder{}
strBuilder.WriteString(options.GetType())
if options.GetLimit() > 0 {
if strings.ToLower(options.GetType()) == "varchar" {
strBuilder.WriteString(fmt.Sprintf("(%d)", options.GetLimit()))
}
} else {

// would add precision and scale for decimal and numeric types
// example : DECIMAL(15,2) where 15 is precision and 2 is scale
// The scale cannot be set without setting the precision

precisionAndScale := make([]string, 0, 2)
if options.GetPrecision() > 0 {
precisionAndScale = append(precisionAndScale, fmt.Sprintf("%d", options.GetPrecision()))
}

if options.GetScale() > 0 && options.GetPrecision() == 0 {
ctx.RaiseError(fmt.Errorf("scale cannot be set without setting the precision"))
return ""
}

if options.GetScale() > 0 {
precisionAndScale = append(precisionAndScale, fmt.Sprintf("%d", options.GetScale()))
}

if len(precisionAndScale) > 0 {
strBuilder.WriteString(fmt.Sprintf("(%s)", strings.Join(precisionAndScale, ", ")))
}
}

if options.IsArray() {
strBuilder.WriteString("[]")
}

return strBuilder.String()
}
}
8 changes: 5 additions & 3 deletions pkg/schema/migrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package schema
import (
"context"
"fmt"
"regexp"
"slices"
"time"

"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"github.com/alexisvisco/amigo/pkg/utils/dblog"
"github.com/alexisvisco/amigo/pkg/utils/events"
"github.com/alexisvisco/amigo/pkg/utils/logger"
"regexp"
"slices"
"time"
)

// MigratorOption is the option of the migrator.
Expand Down Expand Up @@ -297,6 +298,7 @@ func isTableDoesNotExists(err error) bool {
regexp.MustCompile(`Error 1146 \(42S02\): Table '.*' doesn't exist`),
regexp.MustCompile(`ERROR: relation ".*" does not exist \(SQLSTATE 42P01\)`),
regexp.MustCompile(`no such table: .*`),
regexp.MustCompile(`.*does not exist (SQLSTATE=42P01).*`),
}

for _, r := range re {
Expand Down
6 changes: 3 additions & 3 deletions pkg/schema/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package schema

import (
"fmt"
"github.com/alexisvisco/amigo/pkg/utils"
"strings"

"github.com/alexisvisco/amigo/pkg/utils"
)

type ConstraintOption interface {
Expand Down Expand Up @@ -372,8 +373,7 @@ type ColumnOptions struct {
// NotNull specifies if the column can be null.
NotNull bool

// Limit is a maximum column length. This is the number of characters for a ColumnTypeString column and number of
// bytes for ColumnTypeText, ColumnTypeBinary, ColumnTypeBlob, and ColumnTypeInteger Columns.
// Limit is a maximum column length. This is the number of characters for a varchar column.
Limit int

// IfNotExists specifies if the column already exists to not try to re-add it. This will avoid duplicate column errors.
Expand Down
49 changes: 5 additions & 44 deletions pkg/schema/pg/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package pg

import (
"fmt"
"strings"

"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/schema/base"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
"github.com/alexisvisco/amigo/pkg/utils/events"
"github.com/alexisvisco/amigo/pkg/utils/logger"
"strings"
)

// AddColumn adds a new column to the Table.
Expand Down Expand Up @@ -98,7 +100,7 @@ func (p *Schema) column(options schema.ColumnOptions) string {

replacer := utils.Replacer{
"column_name": utils.StrFunc(options.ColumnName),
"column_type": p.columnType(&options),
"column_type": base.ColumnType(p.Context, &options),
"if_not_exists": func() string {
if options.IfNotExists {
return "IF NOT EXISTS"
Expand Down Expand Up @@ -131,47 +133,6 @@ func (p *Schema) column(options schema.ColumnOptions) string {
return replacer.Replace(sql)
}

func (p *Schema) columnType(options schema.ColumnData) func() string {
return func() string {
strBuilder := strings.Builder{}
strBuilder.WriteString(options.GetType())
if options.GetLimit() > 0 {
if strings.ToLower(options.GetType()) == "varchar" {
strBuilder.WriteString(fmt.Sprintf("(%d)", options.GetLimit()))
}
} else {

// would add precision and scale for decimal and numeric types
// example : DECIMAL(15,2) where 15 is precision and 2 is scale
// The scale cannot be set without setting the precision

precisionAndScale := make([]string, 0, 2)
if options.GetPrecision() > 0 {
precisionAndScale = append(precisionAndScale, fmt.Sprintf("%d", options.GetPrecision()))
}

if options.GetScale() > 0 && options.GetPrecision() == 0 {
p.Context.RaiseError(fmt.Errorf("scale cannot be set without setting the precision"))
return ""
}

if options.GetScale() > 0 {
precisionAndScale = append(precisionAndScale, fmt.Sprintf("%d", options.GetScale()))
}

if len(precisionAndScale) > 0 {
strBuilder.WriteString(fmt.Sprintf("(%s)", strings.Join(precisionAndScale, ", ")))
}
}

if options.IsArray() {
strBuilder.WriteString("[]")
}

return strBuilder.String()
}
}

// AddTimestamps adds the timestamps columns created_at and updated_at to the table.
// It's a shortcut for adding two columns with the current timestamp as default value.
//
Expand Down Expand Up @@ -384,7 +345,7 @@ func (p *Schema) ChangeColumnType(tableName schema.TableName, columnName string,
replacer := utils.Replacer{
"table_name": utils.StrFunc(options.Table.String()),
"column_name": utils.StrFunc(options.ColumnName),
"column_type": p.columnType(&options),
"column_type": base.ColumnType(p.Context, &options),
"using": utils.StrFuncPredicate(options.Using != "", fmt.Sprintf("USING %s", options.Using)),
}

Expand Down
184 changes: 184 additions & 0 deletions pkg/schema/sqlite/column.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package sqlite

import (
"fmt"

"github.com/alexisvisco/amigo/pkg/schema"
"github.com/alexisvisco/amigo/pkg/schema/base"
"github.com/alexisvisco/amigo/pkg/types"
"github.com/alexisvisco/amigo/pkg/utils"
)

// AddColumn adds a new column to the Table.
// - You cannot add a primary key column to a table.
//
// Example:
//
// p.Column("users", "picture", schema.ColumnTypeBinary)
//
// Generates:
//
// ALTER TABLE "users" ADD "picture" BLOB
//
// Adding a column with a limit, default value and null constraint:
//
// p.Column("articles", "status", schema.ColumnTypeString, schema.ColumnOptions{Limit: 20, Default: "draft", NotNull: false})
//
// Generates:
//
// ALTER TABLE "articles" ADD "status" VARCHAR(20) DEFAULT 'draft' NOT NULL
//
// Adding a column with precision and scale:
//
// p.Column("answers", "bill_gates_money", schema.ColumnTypeDecimal, schema.ColumnOptions{Precision: 15, Scale: 2})
//
// Generates:
//
// ALTER TABLE "answers" ADD "bill_gates_money" NUMERIC(15,2)
//
// Adding a column if it does not exist:
//
// p.Column("shapes", "triangle", "polygon", schema.ColumnOptions{IfNotExists: true})
//
// Generates:
//
// ALTER TABLE "shapes" ADD "triangle" IF NOT EXISTS POLYGON
func (p *Schema) AddColumn(tableName schema.TableName, columnName string, columnType schema.ColumnType, opts ...schema.ColumnOptions) {
options := schema.ColumnOptions{}
if len(opts) > 0 {
options = opts[0]
}

options.Table = tableName
options.ColumnName = columnName
options.ColumnType = p.toType(columnType, &options)

if p.Context.MigrationDirection == types.MigrationDirectionDown {
//p.rollbackMode().DropColumn(tableName, columnName, schema.DropColumnOptions{IfExists: true})
return
}

query := fmt.Sprintf("ALTER TABLE %s ADD %s", options.Table, p.column(options))

_, err := p.TX.ExecContext(p.Context.Context, query)
if err != nil {
p.Context.RaiseError(fmt.Errorf("error while adding column: %w", err))
return
}

p.Context.AddColumnCreated(options)

if options.Comment != "" {
//p.AddColumnComment(options.Table, options.ColumnName, &options.Comment, schema.ColumnCommentOptions{})
}
}

func (p *Schema) column(options schema.ColumnOptions) string {
sql := `{if_not_exists} "{column_name}" {column_type} {primary_key} {default} {nullable}`

replacer := utils.Replacer{
"column_name": utils.StrFunc(options.ColumnName),
"column_type": base.ColumnType(p.Context, &options),
"if_not_exists": func() string {
if options.IfNotExists {
return "IF NOT EXISTS"
}
return ""
},
"primary_key": func() string {
if options.PrimaryKey {
s := ""
if options.ColumnType == schema.ColumnTypePrimaryKey {
s += "INTEGER"
} else {
s += options.ColumnType
}
s = "PRIMARY KEY"
fmt.Println(options.ColumnType)
if options.ColumnType == schema.ColumnTypeSerial || options.ColumnType == schema.ColumnTypePrimaryKey {
s += " AUTOINCREMENT"
}

return s
}
return ""
},
"default": func() string {
if options.Default != "" {
return fmt.Sprintf("DEFAULT %s", options.Default)
}
return ""
},
"nullable": func() string {
if options.NotNull {
return "NOT NULL"
}
return ""
},
//"constraints": func() string {
// var constraints []string
//
// for _, constraint := range options.Constraints {
// constraints = append(constraints, p.applyConstraint(constraint))
// }
//
// return strings.Join(constraints, " ")
//},
}

return replacer.Replace(sql)
}

func (p *Schema) toType(c schema.ColumnType, co schema.ColumnData) string {
p.modifyColumnOptionFromType(c, co)

switch c {
case schema.ColumnTypeString:
return "TEXT"
case schema.ColumnTypeText:
return "TEXT"
case schema.ColumnTypeInteger:
return "INTEGER"
case schema.ColumnTypeSerial:
return "INTEGER" // SQLite does not have SERIAL, SERIAL is for PRIMARY KEY with the add of AUTOINCREMENT
case schema.ColumnTypeBigInt:
return "INTEGER"
case schema.ColumnTypeFloat:
return "REAL"
case schema.ColumnTypeDecimal, schema.ColumnTypeNumeric:
return "NUMERIC"
case schema.ColumnTypeDatetime:
return "DATETIME"
case schema.ColumnTypeTime:
return "TEXT" // SQLite does not have a native TIME type
case schema.ColumnTypeDate:
return "TEXT" // SQLite does not have a native DATE type
case schema.ColumnTypeBinary, schema.ColumnTypeBlob:
return "BLOB"
case schema.ColumnTypeBoolean:
return "INTEGER" // SQLite does not have a native BOOLEAN type, typically use INTEGER
case schema.ColumnTypeUUID:
return "TEXT" // UUIDs are typically stored as TEXT in SQLite
case schema.ColumnTypeJSON:
return "TEXT" // SQLite does not have a native JSON type, store as TEXT
case schema.ColumnTypeJSONB:
return "TEXT" // SQLite does not have a native JSONB type, store as TEXT
case schema.ColumnTypeHstore:
return "TEXT" // SQLite does not have a native HSTORE type, store as TEXT

default:
return c
}
}

func (p *Schema) modifyColumnOptionFromType(c schema.ColumnType, co schema.ColumnData) {
switch c {
case schema.ColumnTypePrimaryKey:
co.SetNotNull(true)
co.SetPrimaryKey(true)
case schema.ColumnTypeDatetime:
if co.GetPrecision() == 0 {
co.SetPrecision(6)
}
}
}
Loading

0 comments on commit 9ebd289

Please sign in to comment.