Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/database/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ func (c Connection) MySQL() string {
config.Passwd = c.Pass
config.Net = "tcp"
config.Addr = fmt.Sprintf("%s:%d", c.Host, c.Port)
config.ParseTime = true
return config.FormatDSN()
}
48 changes: 48 additions & 0 deletions pkg/database/mysql/date.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package mysql

import (
"database/sql/driver"
"fmt"
"time"
)

// NullDate represents a time.Time that may be null.
// NullDate implements the Scanner interface so
// it can be used as a scan destination, similar to NullString.
// It is distinct from sql.NullTime in that it can output formats only as a date
type NullDate struct {
Date time.Time
Valid bool
}

// Scan implements the Scanner interface.
func (n *NullDate) Scan(value any) error {
if value == nil {
n.Date, n.Valid = time.Time{}, false
return nil
}
switch s := value.(type) {
case string:
t, err := time.Parse("2006-01-02", s)
if err != nil {
return err
}
n.Date = t
n.Valid = true
return nil
case time.Time:
n.Date = s
n.Valid = true
return nil
}
n.Valid = false
return fmt.Errorf("unknown type %T for NullDate", value)
}

// Value implements the driver Valuer interface.
func (n NullDate) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return n.Date.Format("2006-01-02"), nil
}
18 changes: 18 additions & 0 deletions pkg/database/mysql/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,12 @@ func reflectColumnType(tp *sql.ColumnType) reflect.Type {
return reflect.TypeOf(sql.NullInt64{})
case "DOUBLE":
return reflect.TypeOf(sql.NullFloat64{})
case "TIMESTAMP", "DATETIME":
return reflect.TypeOf(sql.NullTime{})
case "DATE":
return reflect.TypeOf(NullDate{})
case "TIME":
return reflect.TypeOf(sql.NullString{})
}

// unknown datatype
Expand Down Expand Up @@ -557,6 +563,18 @@ func (table *table) RowBuffer() *bytes.Buffer {
} else {
fmt.Fprintf(&b, "_binary '%s'", sanitize(string(*s)))
}
case *NullDate:
if s.Valid {
fmt.Fprintf(&b, "'%s'", sanitize(s.Date.Format("2006-01-02")))
} else {
b.WriteString(nullType)
}
case *sql.NullTime:
if s.Valid {
fmt.Fprintf(&b, "'%s'", sanitize(s.Time.Format("2006-01-02 15:04:05")))
} else {
b.WriteString(nullType)
}
default:
fmt.Fprintf(&b, "'%s'", value)
}
Expand Down
12 changes: 11 additions & 1 deletion test/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,17 @@ func (d *dockerContext) createBackupFile(mysqlCID, mysqlUser, mysqlPass, outfile
ctx := context.Background()

// Create and populate the table
mysqlCreateCmd := []string{"mysql", "-hlocalhost", "--protocol=tcp", fmt.Sprintf("-u%s", mysqlUser), fmt.Sprintf("-p%s", mysqlPass), "-e", `use tester; create table t1 (id INT, name VARCHAR(20)); INSERT INTO t1 (id,name) VALUES (1, "John"), (2, "Jill"), (3, "Sam"), (4, "Sarah");`}
mysqlCreateCmd := []string{"mysql", "-hlocalhost", "--protocol=tcp", fmt.Sprintf("-u%s", mysqlUser), fmt.Sprintf("-p%s", mysqlPass), "-e", `
use tester;
create table t1
(id int, name varchar(20), d date, t time, dt datetime, ts timestamp);
INSERT INTO t1 (id,name,d,t,dt,ts)
VALUES
(1, "John", "2012-11-01", "00:15:00", "2012-11-01 00:15:00", "2012-11-01 00:15:00"),
(2, "Jill", "2012-11-02", "00:16:00", "2012-11-02 00:16:00", "2012-11-02 00:16:00"),
(3, "Sam", "2012-11-03", "00:17:00", "2012-11-03 00:17:00", "2012-11-03 00:17:00"),
(4, "Sarah", "2012-11-04", "00:18:00", "2012-11-04 00:18:00", "2012-11-04 00:18:00");
`}
attachResp, exitCode, err := d.execInContainer(ctx, mysqlCID, mysqlCreateCmd)
if err != nil {
return fmt.Errorf("failed to attach to exec: %w", err)
Expand Down