diff --git a/store/adapters/rdbms/drivers/sqlite/connect.go b/store/adapters/rdbms/drivers/sqlite/connect.go index 8a37e7ab4..7ae761e03 100644 --- a/store/adapters/rdbms/drivers/sqlite/connect.go +++ b/store/adapters/rdbms/drivers/sqlite/connect.go @@ -31,7 +31,7 @@ const ( var ( // make a custom driver with REGEX support - driver = &sqlite3.SQLiteDriver{ + customDriver = &sqlite3.SQLiteDriver{ ConnectHook: func(conn *sqlite3.SQLiteConn) (err error) { // register regexp function and use Go's regexp fn if err = conn.RegisterFunc("regexp", regexp.MatchString, true); err != nil { @@ -45,9 +45,9 @@ var ( func init() { // register alter driver - sql.Register(altSchema, driver) + sql.Register(altSchema, customDriver) // register drbug driver - sql.Register(debugSchema, sqlmw.Driver(driver, instrumentation.Debug())) + sql.Register(debugSchema, sqlmw.Driver(customDriver, instrumentation.Debug())) store.Register(Connect, SCHEMA, altSchema, debugSchema) } diff --git a/store/adapters/rdbms/drivers/sqlite/dialect.go b/store/adapters/rdbms/drivers/sqlite/dialect.go index 6abcf80d2..3faf24cca 100644 --- a/store/adapters/rdbms/drivers/sqlite/dialect.go +++ b/store/adapters/rdbms/drivers/sqlite/dialect.go @@ -52,14 +52,41 @@ func (d sqliteDialect) TableCodec(m *dal.Model) drivers.TableCodec { return drivers.NewTableCodec(m, d) } -func (d sqliteDialect) TypeWrap(t dal.Type) drivers.Type { +func (d sqliteDialect) TypeWrap(dt dal.Type) drivers.Type { // Any exception to general type-wrap implementation in the drivers package // should be placed here - return drivers.TypeWrap(t) + + // Any exception to general type-wrap implementation in the drivers package + // should be placed here + switch c := dt.(type) { + case *dal.TypeDate: + return &TypeDate{c} + } + + return drivers.TypeWrap(dt) } func (sqliteDialect) AttributeCast(attr *dal.Attribute, val exp.LiteralExpression) (exp.LiteralExpression, error) { - return drivers.AttributeCast(attr, val) + var ( + c exp.CastExpression + ) + + switch attr.Type.(type) { + case *dal.TypeDate: + ce := exp.NewCaseExpression(). + When(val.RegexpLike(drivers.CheckDateISO8061), val). + Else(drivers.LiteralNULL) + + // if we cast to DATE result value is treated like number (int64) and + // we only get the year part. So we need to cast to TEXT first + // and the the full-date is parsed into time.Time + c = exp.NewCastExpression(ce, "TEXT") + default: + return drivers.AttributeCast(attr, val) + } + + return exp.NewLiteralExpression("?", c), nil + } func (sqliteDialect) AttributeToColumn(attr *dal.Attribute) (col *ddl.Column, err error) { @@ -84,12 +111,12 @@ func (sqliteDialect) AttributeToColumn(attr *dal.Attribute) (col *ddl.Column, er col.Default = ddl.DefaultValueCurrentTimestamp(t.DefaultCurrentTimestamp) case *dal.TypeDate: - col.Type.Name = "DATE" + col.Type.Name = "TEXT" col.Default = ddl.DefaultValueCurrentTimestamp(t.DefaultCurrentTimestamp) case *dal.TypeNumber: col.Type.Name = "NUMERIC" - // @todo precision, scale? + // @todo precision, scale? x col.Default = ddl.DefaultNumber(t.HasDefault, t.Precision, t.DefaultValue) case *dal.TypeText: diff --git a/store/adapters/rdbms/drivers/sqlite/types.go b/store/adapters/rdbms/drivers/sqlite/types.go new file mode 100644 index 000000000..e77f3a768 --- /dev/null +++ b/store/adapters/rdbms/drivers/sqlite/types.go @@ -0,0 +1,40 @@ +package sqlite + +import ( + "database/sql" + "database/sql/driver" + "fmt" + "github.com/spf13/cast" + "time" + + "github.com/cortezaproject/corteza-server/pkg/dal" + "github.com/cortezaproject/corteza-server/store/adapters/rdbms/drivers" +) + +type ( + TypeDate struct{ *dal.TypeDate } +) + +func (*TypeDate) MakeScanBuffer() any { return new(sql.NullString) } + +func (t *TypeDate) Decode(raw any) (any, bool, error) { + dec, is := raw.(*sql.NullString) + if !is { + return nil, false, fmt.Errorf("unexpected raw type %T for Time", raw) + } + + if !dec.Valid { + return time.Time{}, false, nil + } + + parsed, err := cast.StringToDate(dec.String) + if err != nil { + return time.Time{}, false, err + } + + return parsed.Format(drivers.DateLayout), true, nil +} + +func (t *TypeDate) Encode(val any) (driver.Value, error) { + return val, nil +}