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: _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(¶ms) 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()) }