Skip to content

Commit

Permalink
Merge pull request #19 from jamf/JPRO-3315-MySQL-Views-create-databas…
Browse files Browse the repository at this point in the history
…e-backup-errors

Jpro 3315 my sql views create database backup errors
  • Loading branch information
ryan-brown-jamf committed Dec 28, 2023
2 parents 3254d43 + b4972f9 commit f5a2aa3
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
vendor/*
.idea/*
44 changes: 34 additions & 10 deletions dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import (
/*
Data struct to configure dump behavior
Out: Stream to wite to
Connection: Database connection to dump
IgnoreTables: Mark sensitive tables to ignore
MaxAllowedPacket: Sets the largest packet size to use in backups
LockTables: Lock all tables for the duration of the dump
Out: Stream to wite to
Connection: Database connection to dump
IgnoreTables: Mark sensitive tables to ignore
MaxAllowedPacket: Sets the largest packet size to use in backups
LockTables: Lock all tables for the duration of the dump
*/
type Data struct {
Out io.Writer
Expand Down Expand Up @@ -290,16 +290,40 @@ func (table *table) NameEsc() string {
}

func (table *table) CreateSQL() (string, error) {
var tableReturn, tableSQL sql.NullString
if err := table.data.tx.QueryRow("SHOW CREATE TABLE "+table.NameEsc()).Scan(&tableReturn, &tableSQL); err != nil {
rows, err := table.data.tx.Query("SHOW CREATE TABLE " + table.NameEsc())
if err != nil {
return "", err
}
defer rows.Close()

// get the column names from the query
columnNames, err := rows.Columns()
if err != nil {
return "", err
}
columnCount := len(columnNames)

info := make([]sql.NullString, columnCount)
scans := make([]interface{}, columnCount)
for i := range info {
scans[i] = &info[i]
}

if rows.Next() {
if err := rows.Scan(scans...); err != nil {
return "", err
}
}

if len(info) < 2 {
return "", errors.New("database column information is malformed")
}

if tableReturn.String != table.Name {
return "", errors.New("Returned table is not the same as requested table")
if info[0].String != table.Name {
return "", errors.New("returned table is not the same as requested table")
}

return tableSQL.String, nil
return info[1].String, nil
}

func (table *table) initColumnData() error {
Expand Down
82 changes: 80 additions & 2 deletions dump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package mysqldump
import (
"bytes"
"database/sql"
"errors"
"reflect"
"strings"
"testing"

sqlmock "github.com/DATA-DOG/go-sqlmock"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -122,7 +123,7 @@ func TestGetServerVersionOk(t *testing.T) {
assert.Equal(t, "test_version", meta.ServerVersion)
}

func TestCreateTableSQLOk(t *testing.T) {
func TestCreateSQLSQLOk(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()
Expand All @@ -147,6 +148,83 @@ func TestCreateTableSQLOk(t *testing.T) {
}
}

func TestCreateSQLQueryFail(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()

queryError := errors.New("query failure")
mock.ExpectQuery("^SHOW CREATE TABLE `Test_Table`$").WillReturnError(queryError)

table := data.createTable("Test_Table")

result, err := table.CreateSQL()
assert.Error(t, err)
assert.Equal(t, queryError, err)

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

expectedResult := ""

if !reflect.DeepEqual(result, expectedResult) {
t.Fatalf("expected %#v, got %#v", expectedResult, result)
}
}

func TestCreateSQLWrongTable(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()

rows := sqlmock.NewRows([]string{"Table", "Create Table"}).
AddRow("Diff_Table", "CREATE TABLE 'Test_Table' (`id` int(11) NOT NULL AUTO_INCREMENT,`s` char(60) DEFAULT NULL, PRIMARY KEY (`id`))ENGINE=InnoDB DEFAULT CHARSET=latin1")

mock.ExpectQuery("^SHOW CREATE TABLE `Test_Table`$").WillReturnRows(rows)

table := data.createTable("Test_Table")

result, err := table.CreateSQL()
assert.Error(t, err)
expectedError := errors.New("returned table is not the same as requested table")
assert.Equal(t, expectedError, err)

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

expectedResult := ""

if !reflect.DeepEqual(result, expectedResult) {
t.Fatalf("expected %#v, got %#v", expectedResult, result)
}
}

func TestCreateTableInvalidColumns(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()

rows := sqlmock.NewRows([]string{"Table"}).
AddRow("Test_Table")

mock.ExpectQuery("^SHOW CREATE TABLE `Test_Table`$").WillReturnRows(rows)

table := data.createTable("Test_Table")

result, err := table.CreateSQL()
assert.Error(t, err)
assert.Equal(t, errors.New("database column information is malformed"), err)

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

expectedResult := ""

if !reflect.DeepEqual(result, expectedResult) {
t.Fatalf("expected %#v, got %#v", expectedResult, result)
}
}

func mockTableSelect(mock sqlmock.Sqlmock, name string) {
cols := sqlmock.NewRows([]string{"Field", "Extra"}).
AddRow("id", "").
Expand Down
10 changes: 5 additions & 5 deletions mysqldump.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ func Dump(db *sql.DB, out io.Writer) error {
// Will also close the database the dumper is connected to as well as the out stream if it has a Close method.
//
// Not required.
func (d *Data) Close() error {
func (data *Data) Close() error {
defer func() {
d.Connection = nil
d.Out = nil
data.Connection = nil
data.Out = nil
}()
if out, ok := d.Out.(io.Closer); ok {
if out, ok := data.Out.(io.Closer); ok {
out.Close()
}
return d.Connection.Close()
return data.Connection.Close()
}

func exists(p string) (bool, os.FileInfo) {
Expand Down

0 comments on commit f5a2aa3

Please sign in to comment.