terraform/vendor/github.com/ziutek/mymysql/native/native_test.go

1219 lines
26 KiB
Go

package native
import (
"bytes"
"fmt"
"github.com/ziutek/mymysql/mysql"
"io/ioutil"
"os"
"reflect"
"testing"
"time"
)
var (
my mysql.Conn
user = "testuser"
passwd = "TestPasswd9"
dbname = "test"
//conn = []string{"unix", "", "/var/run/mysqld/mysqld.sock"}
conn = []string{"", "", "127.0.0.1:3306"}
debug = false
)
type RowsResErr struct {
rows []mysql.Row
res mysql.Result
err error
}
func query(sql string, params ...interface{}) *RowsResErr {
rows, res, err := my.Query(sql, params...)
return &RowsResErr{rows, res, err}
}
func exec(stmt *Stmt, params ...interface{}) *RowsResErr {
rows, res, err := stmt.Exec(params...)
return &RowsResErr{rows, res, err}
}
func checkErr(t *testing.T, err error, exp_err error) {
if err != exp_err {
if exp_err == nil {
t.Fatalf("Error: %v", err)
} else {
t.Fatalf("Error: %v\nExpected error: %v", err, exp_err)
}
}
}
func checkWarnCount(t *testing.T, res_cnt, exp_cnt int) {
if res_cnt != exp_cnt {
t.Errorf("Warning count: res=%d exp=%d", res_cnt, exp_cnt)
rows, res, err := my.Query("show warnings")
if err != nil {
t.Fatal("Can't get warrnings from MySQL", err)
}
for _, row := range rows {
t.Errorf("%s: \"%s\"", row.Str(res.Map("Level")),
row.Str(res.Map("Message")))
}
t.FailNow()
}
}
func checkErrWarn(t *testing.T, res, exp *RowsResErr) {
checkErr(t, res.err, exp.err)
checkWarnCount(t, res.res.WarnCount(), exp.res.WarnCount())
}
func types(row mysql.Row) (tt []reflect.Type) {
tt = make([]reflect.Type, len(row))
for ii, val := range row {
tt[ii] = reflect.TypeOf(val)
}
return
}
func checkErrWarnRows(t *testing.T, res, exp *RowsResErr) {
checkErrWarn(t, res, exp)
if !reflect.DeepEqual(res.rows, exp.rows) {
rlen := len(res.rows)
elen := len(exp.rows)
t.Error("Rows are different!")
t.Errorf("len/cap: res=%d/%d exp=%d/%d",
rlen, cap(res.rows), elen, cap(exp.rows))
max := rlen
if elen > max {
max = elen
}
for ii := 0; ii < max; ii++ {
if ii < len(res.rows) {
t.Errorf("%d: res type: %s", ii, types(res.rows[ii]))
} else {
t.Errorf("%d: res: ------", ii)
}
if ii < len(exp.rows) {
t.Errorf("%d: exp type: %s", ii, types(exp.rows[ii]))
} else {
t.Errorf("%d: exp: ------", ii)
}
if ii < len(res.rows) {
t.Error(" res: ", res.rows[ii])
}
if ii < len(exp.rows) {
t.Error(" exp: ", exp.rows[ii])
}
if ii < len(res.rows) {
t.Errorf(" res: %#v", res.rows[ii][2])
}
if ii < len(exp.rows) {
t.Errorf(" exp: %#v", exp.rows[ii][2])
}
}
t.FailNow()
}
}
func checkResult(t *testing.T, res, exp *RowsResErr) {
checkErrWarnRows(t, res, exp)
r, e := res.res.(*Result), exp.res.(*Result)
if r.my != e.my || r.binary != e.binary || r.status_only != e.status_only ||
r.status&0xdf != e.status || !bytes.Equal(r.message, e.message) ||
r.affected_rows != e.affected_rows ||
r.eor_returned != e.eor_returned ||
!reflect.DeepEqual(res.rows, exp.rows) || res.err != exp.err {
t.Fatalf("Bad result:\nres=%+v\nexp=%+v", res.res, exp.res)
}
}
func cmdOK(affected uint64, binary, eor bool) *RowsResErr {
return &RowsResErr{
res: &Result{
my: my.(*Conn),
binary: binary,
status_only: true,
status: 0x2,
message: []byte{},
affected_rows: affected,
eor_returned: eor,
},
}
}
func selectOK(rows []mysql.Row, binary bool) (exp *RowsResErr) {
exp = cmdOK(0, binary, true)
exp.rows = rows
return
}
func myConnect(t *testing.T, with_dbname bool, max_pkt_size int) {
if with_dbname {
my = New(conn[0], conn[1], conn[2], user, passwd, dbname)
} else {
my = New(conn[0], conn[1], conn[2], user, passwd)
}
if max_pkt_size != 0 {
my.SetMaxPktSize(max_pkt_size)
}
my.(*Conn).Debug = debug
checkErr(t, my.Connect(), nil)
checkResult(t, query("set names utf8"), cmdOK(0, false, true))
}
func myClose(t *testing.T) {
checkErr(t, my.Close(), nil)
}
// Text queries tests
func TestUse(t *testing.T) {
myConnect(t, false, 0)
checkErr(t, my.Use(dbname), nil)
myClose(t)
}
func TestPing(t *testing.T) {
myConnect(t, false, 0)
checkErr(t, my.Ping(), nil)
myClose(t)
}
func TestQuery(t *testing.T) {
myConnect(t, true, 0)
query("drop table t") // Drop test table if exists
checkResult(t, query("create table t (s varchar(40))"),
cmdOK(0, false, true))
exp := &RowsResErr{
res: &Result{
my: my.(*Conn),
field_count: 1,
fields: []*mysql.Field{
&mysql.Field{
Catalog: "def",
Db: "test",
Table: "Test",
OrgTable: "T",
Name: "Str",
OrgName: "s",
DispLen: 3 * 40, //varchar(40)
Flags: 0,
Type: MYSQL_TYPE_VAR_STRING,
Scale: 0,
},
},
status: mysql.SERVER_STATUS_AUTOCOMMIT,
eor_returned: true,
},
}
for ii := 0; ii > 10000; ii += 3 {
var val interface{}
if ii%10 == 0 {
checkResult(t, query("insert t values (null)"),
cmdOK(1, false, true))
val = nil
} else {
txt := []byte(fmt.Sprintf("%d %d %d %d %d", ii, ii, ii, ii, ii))
checkResult(t,
query("insert t values ('%s')", txt), cmdOK(1, false, true))
val = txt
}
exp.rows = append(exp.rows, mysql.Row{val})
}
checkResult(t, query("select s as Str from t as Test"), exp)
checkResult(t, query("drop table t"), cmdOK(0, false, true))
myClose(t)
}
// Prepared statements tests
type StmtErr struct {
stmt *Stmt
err error
}
func prepare(sql string) *StmtErr {
stmt, err := my.Prepare(sql)
return &StmtErr{stmt.(*Stmt), err}
}
func checkStmt(t *testing.T, res, exp *StmtErr) {
ok := res.err == exp.err &&
// Skipping id
reflect.DeepEqual(res.stmt.fields, exp.stmt.fields) &&
res.stmt.field_count == exp.stmt.field_count &&
res.stmt.param_count == exp.stmt.param_count &&
res.stmt.warning_count == exp.stmt.warning_count &&
res.stmt.status == exp.stmt.status
if !ok {
if exp.err == nil {
checkErr(t, res.err, nil)
checkWarnCount(t, res.stmt.warning_count, exp.stmt.warning_count)
for _, v := range res.stmt.fields {
fmt.Printf("%+v\n", v)
}
t.Fatalf("Bad result statement: res=%v exp=%v", res.stmt, exp.stmt)
}
}
}
func TestPrepared(t *testing.T) {
myConnect(t, true, 0)
query("drop table p") // Drop test table if exists
checkResult(t,
query(
"create table p ("+
" ii int not null, ss varchar(20), dd datetime"+
") default charset=utf8",
),
cmdOK(0, false, true),
)
exp := Stmt{
fields: []*mysql.Field{
&mysql.Field{
Catalog: "def", Db: "test", Table: "p", OrgTable: "p",
Name: "i",
OrgName: "ii",
DispLen: 11,
Flags: _FLAG_NO_DEFAULT_VALUE | _FLAG_NOT_NULL,
Type: MYSQL_TYPE_LONG,
Scale: 0,
},
&mysql.Field{
Catalog: "def", Db: "test", Table: "p", OrgTable: "p",
Name: "s",
OrgName: "ss",
DispLen: 3 * 20, // varchar(20)
Flags: 0,
Type: MYSQL_TYPE_VAR_STRING,
Scale: 0,
},
&mysql.Field{
Catalog: "def", Db: "test", Table: "p", OrgTable: "p",
Name: "d",
OrgName: "dd",
DispLen: 19,
Flags: _FLAG_BINARY,
Type: MYSQL_TYPE_DATETIME,
Scale: 0,
},
},
field_count: 3,
param_count: 2,
warning_count: 0,
status: 0x2,
}
sel := prepare("select ii i, ss s, dd d from p where ii = ? and ss = ?")
checkStmt(t, sel, &StmtErr{&exp, nil})
all := prepare("select * from p")
checkErr(t, all.err, nil)
ins := prepare("insert p values (?, ?, ?)")
checkErr(t, ins.err, nil)
parsed, err := mysql.ParseTime("2012-01-17 01:10:10", time.Local)
checkErr(t, err, nil)
parsedZero, err := mysql.ParseTime("0000-00-00 00:00:00", time.Local)
checkErr(t, err, nil)
if !parsedZero.IsZero() {
t.Fatalf("time '%s' isn't zero", parsedZero)
}
exp_rows := []mysql.Row{
mysql.Row{
2, "Taki tekst", time.Unix(123456789, 0),
},
mysql.Row{
5, "Pąk róży", parsed,
},
mysql.Row{
-3, "基础体温", parsed,
},
mysql.Row{
11, "Zero UTC datetime", time.Unix(0, 0),
},
mysql.Row{
17, mysql.Blob([]byte("Zero datetime")), parsedZero,
},
mysql.Row{
23, []byte("NULL datetime"), (*time.Time)(nil),
},
mysql.Row{
23, "NULL", nil,
},
}
for _, row := range exp_rows {
checkErrWarn(t,
exec(ins.stmt, row[0], row[1], row[2]),
cmdOK(1, true, true),
)
}
// Convert values to expected result types
for _, row := range exp_rows {
for ii, col := range row {
val := reflect.ValueOf(col)
// Dereference pointers
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
switch val.Kind() {
case reflect.Invalid:
row[ii] = nil
case reflect.String:
row[ii] = []byte(val.String())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
reflect.Int64:
row[ii] = int32(val.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
reflect.Uint64:
row[ii] = int32(val.Uint())
case reflect.Slice:
if val.Type().Elem().Kind() == reflect.Uint8 {
bytes := make([]byte, val.Len())
for ii := range bytes {
bytes[ii] = val.Index(ii).Interface().(uint8)
}
row[ii] = bytes
}
}
}
}
checkErrWarn(t, exec(sel.stmt, 2, "Taki tekst"), selectOK(exp_rows, true))
checkErrWarnRows(t, exec(all.stmt), selectOK(exp_rows, true))
checkResult(t, query("drop table p"), cmdOK(0, false, true))
checkErr(t, sel.stmt.Delete(), nil)
checkErr(t, all.stmt.Delete(), nil)
checkErr(t, ins.stmt.Delete(), nil)
myClose(t)
}
// Bind testing
func TestVarBinding(t *testing.T) {
myConnect(t, true, 0)
query("drop table t") // Drop test table if exists
checkResult(t,
query("create table t (id int primary key, str varchar(20))"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert t values (?, ?)")
checkErr(t, err, nil)
var (
rre RowsResErr
id *int
str *string
ii int
ss string
)
ins.Bind(&id, &str)
i1 := 1
s1 := "Ala"
id = &i1
str = &s1
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
i2 := 2
s2 := "Ma kota!"
id = &i2
str = &s2
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
ins.Bind(&ii, &ss)
ii = 3
ss = "A kot ma Ale!"
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
sel, err := my.Prepare("select str from t where id = ?")
checkErr(t, err, nil)
rows, _, err := sel.Exec(1)
checkErr(t, err, nil)
if len(rows) != 1 || bytes.Compare([]byte(s1), rows[0].Bin(0)) != 0 {
t.Fatal("First string don't match")
}
rows, _, err = sel.Exec(2)
checkErr(t, err, nil)
if len(rows) != 1 || bytes.Compare([]byte(s2), rows[0].Bin(0)) != 0 {
t.Fatal("Second string don't match")
}
rows, _, err = sel.Exec(3)
checkErr(t, err, nil)
if len(rows) != 1 || bytes.Compare([]byte(ss), rows[0].Bin(0)) != 0 {
t.Fatal("Thrid string don't match")
}
checkResult(t, query("drop table t"), cmdOK(0, false, true))
myClose(t)
}
func TestBindStruct(t *testing.T) {
myConnect(t, true, 0)
query("drop table t") // Drop test table if exists
checkResult(t,
query("create table t (id int primary key, txt varchar(20), b bool)"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert t values (?, ?, ?)")
checkErr(t, err, nil)
sel, err := my.Prepare("select txt, b from t where id = ?")
checkErr(t, err, nil)
var (
s struct {
Id int
Txt string
B bool
}
rre RowsResErr
)
ins.Bind(&s)
s.Id = 2
s.Txt = "Ala ma kota."
s.B = true
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
rows, _, err := sel.Exec(s.Id)
checkErr(t, err, nil)
if len(rows) != 1 || rows[0].Str(0) != s.Txt || rows[0].Bool(1) != s.B {
t.Fatal("selected data don't match inserted data")
}
checkResult(t, query("drop table t"), cmdOK(0, false, true))
myClose(t)
}
func TestDate(t *testing.T) {
myConnect(t, true, 0)
query("drop table d") // Drop test table if exists
checkResult(t,
query("create table d (id int, dd date, dt datetime, tt time)"),
cmdOK(0, false, true),
)
test := []struct {
dd, dt string
tt time.Duration
}{
{
"2011-11-13",
"2010-12-12 11:24:00",
-time.Duration((128*3600 + 3*60 + 2) * 1e9),
}, {
"0000-00-00",
"0000-00-00 00:00:00",
time.Duration(0),
},
}
ins, err := my.Prepare("insert d values (?, ?, ?, ?)")
checkErr(t, err, nil)
sel, err := my.Prepare("select id, tt from d where dd = ? && dt = ?")
checkErr(t, err, nil)
for i, r := range test {
_, err = ins.Run(i, r.dd, r.dt, r.tt)
checkErr(t, err, nil)
sdt, err := mysql.ParseTime(r.dt, time.Local)
checkErr(t, err, nil)
sdd, err := mysql.ParseDate(r.dd)
checkErr(t, err, nil)
rows, _, err := sel.Exec(sdd, sdt)
checkErr(t, err, nil)
if rows == nil {
t.Fatal("nil result")
}
if rows[0].Int(0) != i {
t.Fatal("Bad id", rows[0].Int(1))
}
if rows[0][1].(time.Duration) != r.tt {
t.Fatal("Bad tt", rows[0].Duration(1))
}
}
checkResult(t, query("drop table d"), cmdOK(0, false, true))
myClose(t)
}
func TestDateTimeZone(t *testing.T) {
myConnect(t, true, 0)
query("drop table d") // Drop test table if exists
checkResult(t,
query("create table d (dt datetime)"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert d values (?)")
checkErr(t, err, nil)
sel, err := my.Prepare("select dt from d")
checkErr(t, err, nil)
tstr := "2013-05-10 15:26:00.000000000"
_, err = ins.Run(tstr)
checkErr(t, err, nil)
tt := make([]time.Time, 4)
row, _, err := sel.ExecFirst()
checkErr(t, err, nil)
tt[0] = row.Time(0, time.UTC)
tt[1] = row.Time(0, time.Local)
row, _, err = my.QueryFirst("select dt from d")
checkErr(t, err, nil)
tt[2] = row.Time(0, time.UTC)
tt[3] = row.Time(0, time.Local)
for _, v := range tt {
if v.Format(mysql.TimeFormat) != tstr {
t.Fatal("Timezone problem:", tstr, "!=", v)
}
}
checkResult(t, query("drop table d"), cmdOK(0, false, true))
myClose(t)
}
// Big blob
func TestBigBlob(t *testing.T) {
myConnect(t, true, 34*1024*1024)
query("drop table p") // Drop test table if exists
checkResult(t,
query("create table p (id int primary key, bb longblob)"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert p values (?, ?)")
checkErr(t, err, nil)
sel, err := my.Prepare("select bb from p where id = ?")
checkErr(t, err, nil)
big_blob := make(mysql.Blob, 33*1024*1024)
for ii := range big_blob {
big_blob[ii] = byte(ii)
}
var (
rre RowsResErr
bb mysql.Blob
id int
)
data := struct {
Id int
Bb mysql.Blob
}{}
// Individual parameters binding
ins.Bind(&id, &bb)
id = 1
bb = big_blob
// Insert full blob. Three packets are sended. First two has maximum length
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
// Struct binding
ins.Bind(&data)
data.Id = 2
data.Bb = big_blob[0 : 32*1024*1024-31]
// Insert part of blob - Two packets are sended. All has maximum length.
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
sel.Bind(&id)
// Check first insert.
tmr := "Too many rows"
id = 1
res, err := sel.Run()
checkErr(t, err, nil)
row, err := res.GetRow()
checkErr(t, err, nil)
end, err := res.GetRow()
checkErr(t, err, nil)
if end != nil {
t.Fatal(tmr)
}
if bytes.Compare(row[0].([]byte), big_blob) != 0 {
t.Fatal("Full blob data don't match")
}
// Check second insert.
id = 2
res, err = sel.Run()
checkErr(t, err, nil)
row, err = res.GetRow()
checkErr(t, err, nil)
end, err = res.GetRow()
checkErr(t, err, nil)
if end != nil {
t.Fatal(tmr)
}
if bytes.Compare(row.Bin(res.Map("bb")), data.Bb) != 0 {
t.Fatal("Partial blob data don't match")
}
checkResult(t, query("drop table p"), cmdOK(0, false, true))
myClose(t)
}
// Test for empty result
func TestEmpty(t *testing.T) {
checkNil := func(r mysql.Row) {
if r != nil {
t.Error("Not empty result")
}
}
myConnect(t, true, 0)
query("drop table e") // Drop test table if exists
// Create table
checkResult(t,
query("create table e (id int)"),
cmdOK(0, false, true),
)
// Text query
res, err := my.Start("select * from e")
checkErr(t, err, nil)
row, err := res.GetRow()
checkErr(t, err, nil)
checkNil(row)
row, err = res.GetRow()
checkErr(t, err, mysql.ErrReadAfterEOR)
checkNil(row)
// Prepared statement
sel, err := my.Prepare("select * from e")
checkErr(t, err, nil)
res, err = sel.Run()
checkErr(t, err, nil)
row, err = res.GetRow()
checkErr(t, err, nil)
checkNil(row)
row, err = res.GetRow()
checkErr(t, err, mysql.ErrReadAfterEOR)
checkNil(row)
// Drop test table
checkResult(t, query("drop table e"), cmdOK(0, false, true))
}
// Reconnect test
func TestReconnect(t *testing.T) {
myConnect(t, true, 0)
query("drop table r") // Drop test table if exists
checkResult(t,
query("create table r (id int primary key, str varchar(20))"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert r values (?, ?)")
checkErr(t, err, nil)
sel, err := my.Prepare("select str from r where id = ?")
checkErr(t, err, nil)
params := struct {
Id int
Str string
}{}
var sel_id int
ins.Bind(&params)
sel.Bind(&sel_id)
checkErr(t, my.Reconnect(), nil)
params.Id = 1
params.Str = "Bla bla bla"
_, err = ins.Run()
checkErr(t, err, nil)
checkErr(t, my.Reconnect(), nil)
sel_id = 1
res, err := sel.Run()
checkErr(t, err, nil)
row, err := res.GetRow()
checkErr(t, err, nil)
checkErr(t, res.End(), nil)
if row == nil || row[0] == nil ||
params.Str != row.Str(0) {
t.Fatal("Bad result")
}
checkErr(t, my.Reconnect(), nil)
checkResult(t, query("drop table r"), cmdOK(0, false, true))
myClose(t)
}
// StmtSendLongData test
func TestSendLongData(t *testing.T) {
myConnect(t, true, 64*1024*1024)
query("drop table l") // Drop test table if exists
checkResult(t,
query("create table l (id int primary key, bb longblob)"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert l values (?, ?)")
checkErr(t, err, nil)
sel, err := my.Prepare("select bb from l where id = ?")
checkErr(t, err, nil)
var (
rre RowsResErr
id int64
)
ins.Bind(&id, []byte(nil))
sel.Bind(&id)
// Prepare data
data := make([]byte, 4*1024*1024)
for ii := range data {
data[ii] = byte(ii)
}
// Send long data twice
checkErr(t, ins.SendLongData(1, data, 256*1024), nil)
checkErr(t, ins.SendLongData(1, data, 512*1024), nil)
id = 1
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
res, err := sel.Run()
checkErr(t, err, nil)
row, err := res.GetRow()
checkErr(t, err, nil)
checkErr(t, res.End(), nil)
if row == nil || row[0] == nil ||
bytes.Compare(append(data, data...), row.Bin(0)) != 0 {
t.Fatal("Bad result")
}
file, err := ioutil.TempFile("", "mymysql_test-")
checkErr(t, err, nil)
filename := file.Name()
defer os.Remove(filename)
buf := make([]byte, 1024)
for i := 0; i < 2048; i++ {
_, err := file.Write(buf)
checkErr(t, err, nil)
}
checkErr(t, file.Close(), nil)
// Send long data from io.Reader twice
file, err = os.Open(filename)
checkErr(t, err, nil)
checkErr(t, ins.SendLongData(1, file, 128*1024), nil)
checkErr(t, file.Close(), nil)
file, err = os.Open(filename)
checkErr(t, err, nil)
checkErr(t, ins.SendLongData(1, file, 1024*1024), nil)
checkErr(t, file.Close(), nil)
id = 2
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
res, err = sel.Run()
checkErr(t, err, nil)
row, err = res.GetRow()
checkErr(t, err, nil)
checkErr(t, res.End(), nil)
// Read file for check result
data, err = ioutil.ReadFile(filename)
checkErr(t, err, nil)
if row == nil || row[0] == nil ||
bytes.Compare(append(data, data...), row.Bin(0)) != 0 {
t.Fatal("Bad result")
}
checkResult(t, query("drop table l"), cmdOK(0, false, true))
myClose(t)
}
func TestNull(t *testing.T) {
myConnect(t, true, 0)
query("drop table if exists n")
checkResult(t,
query("create table n (i int not null, n int)"),
cmdOK(0, false, true),
)
ins, err := my.Prepare("insert n values (?, ?)")
checkErr(t, err, nil)
var (
p struct{ I, N *int }
rre RowsResErr
)
ins.Bind(&p)
p.I = new(int)
p.N = new(int)
*p.I = 0
*p.N = 1
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
*p.I = 1
p.N = nil
rre.res, rre.err = ins.Run()
checkResult(t, &rre, cmdOK(1, true, false))
checkResult(t, query("insert n values (2, 1)"), cmdOK(1, false, true))
checkResult(t, query("insert n values (3, NULL)"), cmdOK(1, false, true))
rows, res, err := my.Query("select * from n")
checkErr(t, err, nil)
if len(rows) != 4 {
t.Fatal("str: len(rows) != 4")
}
i := res.Map("i")
n := res.Map("n")
for k, row := range rows {
switch {
case row[i] == nil || row.Int(i) != k:
case k%2 == 1 && row[n] != nil:
case k%2 == 0 && (row[n] == nil || row.Int(n) != 1):
default:
continue
}
t.Fatalf("str row: %d = (%s, %s)", k, row[i], row[n])
}
sel, err := my.Prepare("select * from n")
checkErr(t, err, nil)
rows, res, err = sel.Exec()
checkErr(t, err, nil)
if len(rows) != 4 {
t.Fatal("bin: len(rows) != 4")
}
i = res.Map("i")
n = res.Map("n")
for k, row := range rows {
switch {
case row[i] == nil || row.Int(i) != k:
case k%2 == 1 && row[n] != nil:
case k%2 == 0 && (row[n] == nil || row.Int(n) != 1):
default:
continue
}
t.Fatalf("bin row: %d = (%v, %v)", k, row[i], row[n])
}
checkResult(t, query("drop table n"), cmdOK(0, false, true))
}
func TestMultipleResults(t *testing.T) {
myConnect(t, true, 0)
query("drop table m") // Drop test table if exists
checkResult(t,
query("create table m (id int primary key, str varchar(20))"),
cmdOK(0, false, true),
)
str := []string{"zero", "jeden", "dwa"}
checkResult(t, query("insert m values (0, '%s')", str[0]),
cmdOK(1, false, true))
checkResult(t, query("insert m values (1, '%s')", str[1]),
cmdOK(1, false, true))
checkResult(t, query("insert m values (2, '%s')", str[2]),
cmdOK(1, false, true))
res, err := my.Start("select id from m; select str from m")
checkErr(t, err, nil)
for ii := 0; ; ii++ {
row, err := res.GetRow()
checkErr(t, err, nil)
if row == nil {
break
}
if row.Int(0) != ii {
t.Fatal("Bad result")
}
}
res, err = res.NextResult()
checkErr(t, err, nil)
for ii := 0; ; ii++ {
row, err := res.GetRow()
checkErr(t, err, nil)
if row == nil {
break
}
if row.Str(0) != str[ii] {
t.Fatal("Bad result")
}
}
checkResult(t, query("drop table m"), cmdOK(0, false, true))
myClose(t)
}
func TestDecimal(t *testing.T) {
myConnect(t, true, 0)
query("drop table if exists d")
checkResult(t,
query("create table d (d decimal(4,2))"),
cmdOK(0, false, true),
)
checkResult(t, query("insert d values (10.01)"), cmdOK(1, false, true))
sql := "select * from d"
sel, err := my.Prepare(sql)
checkErr(t, err, nil)
rows, res, err := sel.Exec()
checkErr(t, err, nil)
if len(rows) != 1 || rows[0][res.Map("d")].(float64) != 10.01 {
t.Fatal(sql)
}
checkResult(t, query("drop table d"), cmdOK(0, false, true))
myClose(t)
}
func TestMediumInt(t *testing.T) {
myConnect(t, true, 0)
query("DROP TABLE mi")
checkResult(t,
query(
`CREATE TABLE mi (
id INT PRIMARY KEY AUTO_INCREMENT,
m MEDIUMINT
)`,
),
cmdOK(0, false, true),
)
const n = 9
for i := 0; i < n; i++ {
res, err := my.Start("INSERT mi VALUES (0, %d)", i)
checkErr(t, err, nil)
if res.InsertId() != uint64(i+1) {
t.Fatalf("Wrong insert id: %d, expected: %d", res.InsertId(), i+1)
}
}
sel, err := my.Prepare("SELECT * FROM mi")
checkErr(t, err, nil)
res, err := sel.Run()
checkErr(t, err, nil)
i := 0
for {
row, err := res.GetRow()
checkErr(t, err, nil)
if row == nil {
break
}
id, m := row.Int(0), row.Int(1)
if id != i+1 || m != i {
t.Fatalf("i=%d id=%d m=%d", i, id, m)
}
i++
}
if i != n {
t.Fatalf("%d rows read, %d expected", i, n)
}
checkResult(t, query("drop table mi"), cmdOK(0, false, true))
}
func TestStoredProcedures(t *testing.T) {
myConnect(t, true, 0)
query("DROP PROCEDURE pr")
query("DROP TABLE p")
checkResult(t,
query(
`CREATE TABLE p (
id INT PRIMARY KEY AUTO_INCREMENT,
txt VARCHAR(8)
)`,
),
cmdOK(0, false, true),
)
_, err := my.Start(
`CREATE PROCEDURE pr (IN i INT)
BEGIN
INSERT p VALUES (0, "aaa");
SELECT * FROM p;
SELECT i * id FROM p;
END`,
)
checkErr(t, err, nil)
res, err := my.Start("CALL pr(3)")
checkErr(t, err, nil)
rows, err := res.GetRows()
checkErr(t, err, nil)
if len(rows) != 1 || len(rows[0]) != 2 || rows[0].Int(0) != 1 || rows[0].Str(1) != "aaa" {
t.Fatalf("Bad result set: %+v", rows)
}
res, err = res.NextResult()
checkErr(t, err, nil)
rows, err = res.GetRows()
checkErr(t, err, nil)
if len(rows) != 1 || len(rows[0]) != 1 || rows[0].Int(0) != 3 {
t.Fatalf("Bad result set: %+v", rows)
}
res, err = res.NextResult()
checkErr(t, err, nil)
if !res.StatusOnly() {
t.Fatalf("Result includes resultset at end of procedure: %+v", res)
}
_, err = my.Start("DROP PROCEDURE pr")
checkErr(t, err, nil)
checkResult(t, query("DROP TABLE p"), cmdOK(0, false, true))
}
// Benchamrks
func check(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func BenchmarkInsertSelect(b *testing.B) {
b.StopTimer()
my := New(conn[0], conn[1], conn[2], user, passwd, dbname)
check(my.Connect())
my.Start("drop table b") // Drop test table if exists
_, err := my.Start("create table b (s varchar(40), i int)")
check(err)
for ii := 0; ii < 10000; ii++ {
_, err := my.Start("insert b values ('%d-%d-%d', %d)", ii, ii, ii, ii)
check(err)
}
b.StartTimer()
for ii := 0; ii < b.N; ii++ {
res, err := my.Start("select * from b")
check(err)
for {
row, err := res.GetRow()
check(err)
if row == nil {
break
}
}
}
b.StopTimer()
_, err = my.Start("drop table b")
check(err)
check(my.Close())
}
func BenchmarkPreparedInsertSelect(b *testing.B) {
b.StopTimer()
my := New(conn[0], conn[1], conn[2], user, passwd, dbname)
check(my.Connect())
my.Start("drop table b") // Drop test table if exists
_, err := my.Start("create table b (s varchar(40), i int)")
check(err)
ins, err := my.Prepare("insert b values (?, ?)")
check(err)
sel, err := my.Prepare("select * from b")
check(err)
for ii := 0; ii < 10000; ii++ {
_, err := ins.Run(fmt.Sprintf("%d-%d-%d", ii, ii, ii), ii)
check(err)
}
b.StartTimer()
for ii := 0; ii < b.N; ii++ {
res, err := sel.Run()
check(err)
for {
row, err := res.GetRow()
check(err)
if row == nil {
break
}
}
}
b.StopTimer()
_, err = my.Start("drop table b")
check(err)
check(my.Close())
}