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
8 changes: 7 additions & 1 deletion pkg/util/mysql/go_mysqldump/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ func reflectColumnType(tp *sql.ColumnType) reflect.Type {

// determine by name
switch tp.DatabaseTypeName() {
case "BLOB", "BINARY":
case "BLOB", "BINARY", "VARBINARY":
return reflect.TypeOf(sql.RawBytes{})
case "VARCHAR", "TEXT", "DECIMAL", "JSON", "DATETIME", "DATE", "TIMESTAMP":
return reflect.TypeOf(sql.NullString{})
Expand Down Expand Up @@ -507,6 +507,12 @@ func (table *table) RowBuffer() *bytes.Buffer {
} else {
b.WriteString(nullType)
}
case *sql.Null[uint64]:
if s.Valid {
fmt.Fprintf(&b, "%d", s.V)
} else {
b.WriteString(nullType)
}
case *sql.NullFloat64:
if s.Valid {
fmt.Fprintf(&b, "%f", s.Float64)
Expand Down
70 changes: 70 additions & 0 deletions pkg/util/mysql/go_mysqldump/dump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package go_mysqldump
import (
"bytes"
"database/sql"
"encoding/hex"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -293,6 +294,75 @@ func TestCreateTableRowValuesUnsignedBigint(t *testing.T) {
}
}

// The MySQL driver scans a nullable BIGINT UNSIGNED column into a
// sql.Null[uint64], distinct from the non-nullable case above which scans
// into a plain uint64. Reproduces the corrupted-dump bug where such values
// were rendered via a Go fmt bad-verb diagnostic string instead of the
// numeric literal.
func TestCreateTableRowValuesNullableUnsignedBigint(t *testing.T) {
data, mock, err := getMockData()
assert.NoError(t, err, "an error was not expected when opening a stub database connection")
defer data.Close()

cols := sqlmock.NewRows([]string{"Field", "Extra"}).
AddRow("version", "")

rows := sqlmock.NewRowsWithColumnDefinition(
sqlmock.NewColumn("version").OfType("UNSIGNED BIGINT", sql.Null[uint64]{}).Nullable(true),
).
AddRow(uint64(1469))

mock.ExpectQuery("^SHOW COLUMNS FROM `test`$").WillReturnRows(cols)
mock.ExpectQuery("^SELECT (.+) FROM `test` WHERE TRUE$").WillReturnRows(rows)

table := data.createTable("test")

assert.True(t, table.Next())
result := table.RowValues()
assert.NoError(t, table.Err)

// we make sure that all expectations were met
assert.NoError(t, mock.ExpectationsWereMet(), "there were unfulfilled expections")

assert.Equal(t, "(1469)", result)
assert.NotContains(t, result, "%!s", "row values must not contain a Go fmt bad-verb diagnostic string")
}

// The MySQL driver scans VARBINARY columns (e.g. UUIDs stored as raw bytes)
// into a plain []byte, distinct from BINARY which is handled explicitly.
// Reproduces the corrupted-dump bug where such values were rendered as a
// quoted string with a stray leading '&' instead of a `_binary 0x...` literal.
func TestCreateTableRowValuesVarbinary(t *testing.T) {
data, mock, err := getMockData()
assert.NoError(t, err, "an error was not expected when opening a stub database connection")
defer data.Close()

uuid := "5430b48d-cfd5-47c7-ae6d-04c7e2ed2a21"

cols := sqlmock.NewRows([]string{"Field", "Extra"}).
AddRow("id", "")

rows := sqlmock.NewRowsWithColumnDefinition(
sqlmock.NewColumn("id").OfType("VARBINARY", []byte{}).Nullable(false),
).
AddRow([]byte(uuid))

mock.ExpectQuery("^SHOW COLUMNS FROM `test`$").WillReturnRows(cols)
mock.ExpectQuery("^SELECT (.+) FROM `test` WHERE TRUE$").WillReturnRows(rows)

table := data.createTable("test")

assert.True(t, table.Next())
result := table.RowValues()
assert.NoError(t, table.Err)

// we make sure that all expectations were met
assert.NoError(t, mock.ExpectationsWereMet(), "there were unfulfilled expections")

assert.Equal(t, "(_binary 0x"+hex.EncodeToString([]byte(uuid))+")", result)
assert.NotContains(t, result, "&"+uuid, "row values must not contain the raw UUID string with a stray leading '&'")
}

func TestCreateTableOk(t *testing.T) {
data, mock, err := getMockData()
assert.NoError(t, err, "an error was not expected when opening a stub database connection")
Expand Down
Loading