Files
tidb/session_test.go
2015-12-22 00:17:12 +08:00

1435 lines
49 KiB
Go

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package tidb
import (
"fmt"
"sync"
"sync/atomic"
"time"
. "github.com/pingcap/check"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/optimizer"
"github.com/pingcap/tidb/optimizer/plan"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/autocommit"
"github.com/pingcap/tidb/sessionctx/variable"
)
var _ = Suite(&testSessionSuite{})
type testSessionSuite struct {
dbName string
dbNameBootstrap string
createDBSQL string
dropDBSQL string
useDBSQL string
createTableSQL string
dropTableSQL string
insertSQL string
selectSQL string
}
func (s *testSessionSuite) SetUpSuite(c *C) {
s.dbName = "test_session_db"
s.dbNameBootstrap = "test_main_db_bootstrap"
s.createDBSQL = fmt.Sprintf("create database if not exists %s;", s.dbName)
s.dropDBSQL = fmt.Sprintf("drop database %s;", s.dbName)
s.useDBSQL = fmt.Sprintf("use %s;", s.dbName)
s.dropTableSQL = `Drop TABLE if exists t;`
s.createTableSQL = `CREATE TABLE t(id TEXT);`
s.selectSQL = `SELECT * from t;`
}
func (s *testSessionSuite) TearDownSuite(c *C) {
removeStore(c, s.dbName)
}
func (s *testSessionSuite) TestPrepare(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
// create table
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, s.createTableSQL)
// insert data
mustExecSQL(c, se, `INSERT INTO t VALUES ("id");`)
id, ps, _, err := se.PrepareStmt("select id+? from t")
c.Assert(err, IsNil)
c.Assert(id, Equals, uint32(1))
c.Assert(ps, Equals, 1)
mustExecSQL(c, se, `set @a=1`)
_, err = se.ExecutePreparedStmt(id, "1")
c.Assert(err, IsNil)
err = se.DropPreparedStmt(id)
c.Assert(err, IsNil)
mustExecSQL(c, se, s.dropDBSQL)
}
func (s *testSessionSuite) TestAffectedRows(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, s.createTableSQL)
mustExecSQL(c, se, `INSERT INTO t VALUES ("a");`)
c.Assert(int(se.AffectedRows()), Equals, 1)
mustExecSQL(c, se, `INSERT INTO t VALUES ("b");`)
c.Assert(int(se.AffectedRows()), Equals, 1)
mustExecSQL(c, se, `UPDATE t set id = 'c' where id = 'a';`)
c.Assert(int(se.AffectedRows()), Equals, 1)
mustExecSQL(c, se, `UPDATE t set id = 'a' where id = 'a';`)
c.Assert(int(se.AffectedRows()), Equals, 0)
mustExecSQL(c, se, `SELECT * from t;`)
c.Assert(int(se.AffectedRows()), Equals, 0)
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, "create table t (id int, data int)")
mustExecSQL(c, se, `INSERT INTO t VALUES (1, 0), (0, 0), (1, 1);`)
mustExecSQL(c, se, `UPDATE t set id = 1 where data = 0;`)
c.Assert(int(se.AffectedRows()), Equals, 1)
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, "create table t (id int, c1 timestamp);")
mustExecSQL(c, se, `insert t values(1, 0);`)
mustExecSQL(c, se, `UPDATE t set id = 1 where id = 1;`)
c.Assert(int(se.AffectedRows()), Equals, 0)
// With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row,
// 2 if an existing row is updated, and 0 if an existing row is set to its current values.
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, "create table t (c1 int PRIMARY KEY, c2 int);")
mustExecSQL(c, se, `insert t values(1, 1);`)
mustExecSQL(c, se, `insert into t values (1, 1) on duplicate key update c2=2;`)
c.Assert(int(se.AffectedRows()), Equals, 2)
mustExecSQL(c, se, `insert into t values (1, 1) on duplicate key update c2=2;`)
c.Assert(int(se.AffectedRows()), Equals, 0)
se.SetClientCapability(mysql.ClientFoundRows)
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, "create table t (id int, data int)")
mustExecSQL(c, se, `INSERT INTO t VALUES (1, 0), (0, 0), (1, 1);`)
mustExecSQL(c, se, `UPDATE t set id = 1 where data = 0;`)
c.Assert(int(se.AffectedRows()), Equals, 2)
sessionExec(c, se, s.dropDBSQL)
}
func (s *testSessionSuite) TestString(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
sessionExec(c, se, "select 1")
// here to check the panic bug in String() when txn is nil after committed.
c.Log(se.String())
}
func (s *testSessionSuite) TestResultField(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
// create table
mustExecSQL(c, se, s.dropTableSQL)
mustExecSQL(c, se, "create table t (id int);")
mustExecSQL(c, se, `INSERT INTO t VALUES (1);`)
mustExecSQL(c, se, `INSERT INTO t VALUES (2);`)
r := mustExecSQL(c, se, `SELECT count(*) from t;`)
c.Assert(r, NotNil)
_, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
fields, err := r.Fields()
c.Assert(err, IsNil)
c.Assert(len(fields), Equals, 1)
field := fields[0]
c.Assert(field.Tp, Equals, mysql.TypeLonglong)
c.Assert(field.Flen, Equals, 21)
mustExecSQL(c, se, s.dropDBSQL)
}
func (s *testSessionSuite) TestPrimaryKeyAutoincrement(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, name varchar(255) UNIQUE NOT NULL, status int)")
mustExecSQL(c, se, "insert t (name) values (?)", "abc")
id := se.LastInsertID()
c.Check(id != 0, IsTrue)
se2 := newSession(c, store, s.dbName)
rs := mustExecSQL(c, se2, "select * from t")
c.Assert(rs, NotNil)
row, err := rs.FirstRow()
c.Assert(err, IsNil)
match(c, row, id, []byte("abc"), nil)
mustExecSQL(c, se, "update t set name = 'abc', status = 1 where id = ?", id)
rs = mustExecSQL(c, se2, "select * from t")
c.Assert(rs, NotNil)
row, err = rs.FirstRow()
c.Assert(err, IsNil)
match(c, row, id, []byte("abc"), 1)
// Check for pass bool param to tidb prepared statement
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (id tiny)")
mustExecSQL(c, se, "insert t values (?)", true)
rs = mustExecSQL(c, se, "select * from t")
c.Assert(rs, NotNil)
row, err = rs.FirstRow()
c.Assert(err, IsNil)
match(c, row, int8(1))
mustExecSQL(c, se, s.dropDBSQL)
}
func (s *testSessionSuite) TestAutoincrementID(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)")
mustExecSQL(c, se, "insert t values ()")
mustExecSQL(c, se, "insert t values ()")
mustExecSQL(c, se, "insert t values ()")
se.Execute("drop table if exists t;")
mustExecSQL(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)")
mustExecSQL(c, se, "insert t values ()")
lastID := se.LastInsertID()
c.Assert(lastID, Less, uint64(4))
mustExecSQL(c, se, "insert t () values ()")
c.Assert(se.LastInsertID(), Greater, lastID)
mustExecSQL(c, se, "insert t () select 100")
mustExecSQL(c, se, s.dropDBSQL)
}
func checkTxn(c *C, se Session, stmt string, expect uint16) {
mustExecSQL(c, se, stmt)
if expect == 0 {
c.Assert(se.(*session).txn, IsNil)
return
}
c.Assert(se.(*session).txn, NotNil)
}
func checkAutocommit(c *C, se Session, expect uint16) {
ret := variable.GetSessionVars(se.(*session)).Status & mysql.ServerStatusAutocommit
c.Assert(ret, Equals, expect)
}
// See: https://dev.mysql.com/doc/internals/en/status-flags.html
func (s *testSessionSuite) TestAutocommit(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
checkTxn(c, se, "drop table if exists t;", 0)
checkAutocommit(c, se, 2)
checkTxn(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)", 0)
checkAutocommit(c, se, 2)
checkTxn(c, se, "insert t values ()", 0)
checkAutocommit(c, se, 2)
checkTxn(c, se, "begin", 1)
checkAutocommit(c, se, 2)
checkTxn(c, se, "insert t values ()", 1)
checkAutocommit(c, se, 2)
checkTxn(c, se, "drop table if exists t;", 0)
checkAutocommit(c, se, 2)
checkTxn(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)", 0)
checkAutocommit(c, se, 2)
checkTxn(c, se, "set autocommit=0;", 0)
checkAutocommit(c, se, 0)
checkTxn(c, se, "insert t values ()", 1)
checkAutocommit(c, se, 0)
checkTxn(c, se, "commit", 0)
checkAutocommit(c, se, 0)
checkTxn(c, se, "drop table if exists t;", 0)
checkAutocommit(c, se, 0)
checkTxn(c, se, "set autocommit=1;", 0)
checkAutocommit(c, se, 2)
mustExecSQL(c, se, s.dropDBSQL)
}
func checkInTrans(c *C, se Session, stmt string, expect uint16) {
checkTxn(c, se, stmt, expect)
ret := variable.GetSessionVars(se.(*session)).Status & mysql.ServerStatusInTrans
c.Assert(ret, Equals, expect)
}
// See: https://dev.mysql.com/doc/internals/en/status-flags.html
func (s *testSessionSuite) TestInTrans(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
checkInTrans(c, se, "drop table if exists t;", 0)
checkInTrans(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)", 0)
checkInTrans(c, se, "insert t values ()", 0)
checkInTrans(c, se, "begin", 1)
checkInTrans(c, se, "insert t values ()", 1)
checkInTrans(c, se, "drop table if exists t;", 0)
checkInTrans(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)", 0)
checkInTrans(c, se, "insert t values ()", 0)
checkInTrans(c, se, "commit", 0)
checkInTrans(c, se, "insert t values ()", 0)
checkInTrans(c, se, "set autocommit=O;", 0)
checkInTrans(c, se, "begin", 1)
checkInTrans(c, se, "insert t values ()", 1)
checkInTrans(c, se, "commit", 0)
checkInTrans(c, se, "insert t values ()", 1)
checkInTrans(c, se, "commit", 0)
checkInTrans(c, se, "set autocommit=1;", 0)
checkInTrans(c, se, "drop table if exists t;", 0)
checkInTrans(c, se, "create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)", 0)
checkInTrans(c, se, "begin", 1)
checkInTrans(c, se, "insert t values ()", 1)
checkInTrans(c, se, "rollback", 0)
mustExecSQL(c, se, s.dropDBSQL)
}
// See: http://dev.mysql.com/doc/refman/5.7/en/commit.html
func (s *testSessionSuite) TestRowLock(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
se1 := newSession(c, store, s.dbName)
se2 := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
c.Assert(se.(*session).txn, IsNil)
mustExecSQL(c, se, "create table t (c1 int, c2 int, c3 int)")
mustExecSQL(c, se, "insert t values (11, 2, 3)")
mustExecSQL(c, se, "insert t values (12, 2, 3)")
mustExecSQL(c, se, "insert t values (13, 2, 3)")
mustExecSQL(c, se1, "begin")
mustExecSQL(c, se1, "update t set c2=21 where c1=11")
mustExecSQL(c, se2, "begin")
mustExecSQL(c, se2, "update t set c2=211 where c1=11")
mustExecSQL(c, se2, "commit")
_, err := exec(c, se1, "commit")
// se1 will retry and the final value is 21
c.Assert(err, IsNil)
// Check the result is correct
se3 := newSession(c, store, s.dbName)
r := mustExecSQL(c, se3, "select c2 from t where c1=11")
rows, err := r.Rows(-1, 0)
fmt.Println(rows)
matches(c, rows, [][]interface{}{{21}})
mustExecSQL(c, se1, "begin")
mustExecSQL(c, se1, "update t set c2=21 where c1=11")
mustExecSQL(c, se2, "begin")
mustExecSQL(c, se2, "update t set c2=22 where c1=12")
mustExecSQL(c, se2, "commit")
mustExecSQL(c, se1, "commit")
mustExecSQL(c, se, s.dropDBSQL)
}
func (s *testSessionSuite) TestSelectForUpdate(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
se1 := newSession(c, store, s.dbName)
se2 := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
c.Assert(se.(*session).txn, IsNil)
mustExecSQL(c, se, "create table t (c1 int, c2 int, c3 int)")
mustExecSQL(c, se, "insert t values (11, 2, 3)")
mustExecSQL(c, se, "insert t values (12, 2, 3)")
mustExecSQL(c, se, "insert t values (13, 2, 3)")
// conflict
mustExecSQL(c, se1, "begin")
rs, err := exec(c, se1, "select * from t where c1=11 for update")
c.Assert(err, IsNil)
_, err = rs.Rows(-1, 0)
mustExecSQL(c, se2, "begin")
mustExecSQL(c, se2, "update t set c2=211 where c1=11")
mustExecSQL(c, se2, "commit")
_, err = exec(c, se1, "commit")
c.Assert(err, NotNil)
err = se1.Retry()
// retry should fail
c.Assert(err, NotNil)
// not conflict
mustExecSQL(c, se1, "begin")
rs, err = exec(c, se1, "select * from t where c1=11 for update")
_, err = rs.Rows(-1, 0)
mustExecSQL(c, se2, "begin")
mustExecSQL(c, se2, "update t set c2=22 where c1=12")
mustExecSQL(c, se2, "commit")
mustExecSQL(c, se1, "commit")
// not conflict, auto commit
mustExecSQL(c, se1, "set @@autocommit=1;")
rs, err = exec(c, se1, "select * from t where c1=11 for update")
_, err = rs.Rows(-1, 0)
mustExecSQL(c, se2, "begin")
mustExecSQL(c, se2, "update t set c2=211 where c1=11")
mustExecSQL(c, se2, "commit")
_, err = exec(c, se1, "commit")
c.Assert(err, IsNil)
mustExecSQL(c, se, s.dropDBSQL)
err = se.Close()
c.Assert(err, IsNil)
err = se1.Close()
c.Assert(err, IsNil)
err = se2.Close()
c.Assert(err, IsNil)
}
func (s *testSessionSuite) TestRow(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
r := mustExecSQL(c, se, "select row(1, 1) in (row(1, 1))")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1)
r = mustExecSQL(c, se, "select row(1, 1) in (row(1, 0))")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 0)
r = mustExecSQL(c, se, "select row(1, 1) in (select 1, 1)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1)
r = mustExecSQL(c, se, "select row(1, 1) > row(1, 0)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1)
r = mustExecSQL(c, se, "select row(1, 1) > (select 1, 0)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1)
r = mustExecSQL(c, se, "select 1 > (select 1)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 0)
r = mustExecSQL(c, se, "select (select 1)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1)
}
func (s *testSessionSuite) TestIndex(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "create table if not exists test_index (c1 int, c double, index(c1), index(c))")
mustExecSQL(c, se, "insert into test_index values (1, 2), (3, null)")
r := mustExecSQL(c, se, "select c1 from test_index where c > 0")
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 1)
match(c, rows[0], 1)
mustExecSQL(c, se, "drop table if exists t1, t2")
mustExecSQL(c, se, `
create table t1 (c1 int, primary key(c1));
create table t2 (c2 int, primary key(c2));
insert into t1 values (1), (2);
insert into t2 values (2);`)
r = mustExecSQL(c, se, "select * from t1 left join t2 on t1.c1 = t2.c2 order by t1.c1")
rows, err = r.Rows(-1, 0)
matches(c, rows, [][]interface{}{{1, nil}, {2, 2}})
r = mustExecSQL(c, se, "select * from t1 left join t2 on t1.c1 = t2.c2 where t2.c2 < 10")
rows, err = r.Rows(-1, 0)
matches(c, rows, [][]interface{}{{2, 2}})
}
func (s *testSessionSuite) TestMySQLTypes(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
r := mustExecSQL(c, se, `select 0x01 + 1, x'4D7953514C' = "MySQL"`)
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 2, 1)
r = mustExecSQL(c, se, `select 0b01 + 1, 0b01000001 = "A"`)
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 2, 1)
}
func (s *testSessionSuite) TestExpression(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
r := mustExecSQL(c, se, `select + (1 > 0), -(1 >0), + (1 < 0), - (1 < 0)`)
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, -1, 0, 0)
r = mustExecSQL(c, se, "select 1 <=> 1, 1 <=> null, null <=> null, null <=> (select null)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, 0, 1, 1)
}
func (s *testSessionSuite) TestSelect(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "create table if not exists t (c1 int, c2 int)")
mustExecSQL(c, se, "create table if not exists t1 (c1 int, c2 int)")
_, err := se.Execute("select * from t as a join t as a")
c.Assert(err, NotNil)
_, err = se.Execute("select * from t join t1 as t")
c.Assert(err, NotNil)
_, err = se.Execute("select * from t join test.t")
c.Assert(err, NotNil)
_, err = se.Execute("select * from t as a join (select 1) as a")
c.Assert(err, IsNil)
r := mustExecSQL(c, se, "select 1, 2 from dual")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, 2)
r = mustExecSQL(c, se, "select 1, 2 from dual where not exists (select * from t where c1=2)")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, 2)
r = mustExecSQL(c, se, "select 1, 2")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, 2)
r = mustExecSQL(c, se, `select '''a''', """a""", 'pingcap ''-->'' tidb'`)
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, `'a'`, `"a"`, `pingcap '-->' tidb`)
r = mustExecSQL(c, se, `select '\'a\'', "\"a\"";`)
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, `'a'`, `"a"`)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c varchar(20))")
mustExecSQL(c, se, `insert t values("pingcap '-->' tidb")`)
r = mustExecSQL(c, se, `select * from t where c like 'pingcap ''-->'' tidb'`)
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, []byte(`pingcap '-->' tidb`))
mustExecSQL(c, se, "drop table if exists t1")
mustExecSQL(c, se, "drop table if exists t2")
mustExecSQL(c, se, "drop table if exists t3")
mustExecSQL(c, se, "create table t1 (c1 int, c11 int)")
mustExecSQL(c, se, "create table t2 (c2 int)")
mustExecSQL(c, se, "create table t3 (c3 int)")
mustExecSQL(c, se, "insert into t1 values (1, 1), (2, 2), (3, 3)")
mustExecSQL(c, se, "insert into t2 values (1), (1), (2)")
mustExecSQL(c, se, "insert into t3 values (1), (3)")
r = mustExecSQL(c, se, "select * from t1 left join t2 on t1.c1 = t2.c2 left join t3 on t1.c1 = t3.c3 order by t1.c1, t2.c2, t3.c3")
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 4)
match(c, rows[0], 1, 1, 1, 1)
match(c, rows[1], 1, 1, 1, 1)
match(c, rows[2], 2, 2, 2, nil)
match(c, rows[3], 3, 3, nil, 3)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c float(8))")
mustExecSQL(c, se, "insert into t values (3.12)")
r = mustExecSQL(c, se, "select * from t")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 3.12)
mustExecSQL(c, se, `drop table if exists t;create table t (c int);insert into t values (1);`)
r = mustExecSQL(c, se, "select a.c from t as a where c between null and 2")
row, err = r.FirstRow()
c.Assert(err, IsNil)
c.Assert(row, IsNil)
mustExecSQL(c, se, "drop table if exists t1, t2, t3")
mustExecSQL(c, se, `
create table t1 (c1 int);
create table t2 (c2 int);
create table t3 (c3 int);
insert into t1 values (1), (2);
insert into t2 values (2);
insert into t3 values (3);`)
r = mustExecSQL(c, se, "select * from t1 left join t2 on t1.c1 = t2.c2 left join t3 on t1.c1 = t3.c3 order by t1.c1")
rows, err = r.Rows(-1, 0)
c.Assert(err, IsNil)
matches(c, rows, [][]interface{}{{1, nil, nil}, {2, 2, nil}})
mustExecFailed(c, se, "select * from t1 left join t2 on t1.c1 = t3.c3 left join on t3 on t1.c1 = t2.c2")
// For issue 393
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (b blob)")
mustExecSQL(c, se, `insert t values('\x01')`)
r = mustExecSQL(c, se, `select length(b) from t`)
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 3)
}
func (s *testSessionSuite) TestSubQuery(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "create table if not exists t1 (c1 int, c2 int)")
mustExecSQL(c, se, "create table if not exists t2 (c1 int, c2 int)")
mustExecSQL(c, se, "insert into t1 values (1, 1), (2, 2)")
mustExecSQL(c, se, "insert into t2 values (1, 1), (1, 2)")
r := mustExecSQL(c, se, `select c1 from t1 where c1 = (select c2 from t2 where t1.c2 = t2.c2)`)
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1)
r = mustExecSQL(c, se, `select (select count(c1) from t2 where t2.c1 != t1.c2) from t1`)
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 2)
match(c, rows[0], 0)
match(c, rows[1], 2)
mustExecMatch(c, se, "select a.c1, a.c2 from (select c1 as c1, c1 as c2 from t1) as a", [][]interface{}{{1, 1}, {2, 2}})
}
func (s *testSessionSuite) TestShow(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "set global autocommit=1")
r := mustExecSQL(c, se, "show global variables where variable_name = 'autocommit'")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "autocommit", 1)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table if not exists t (c int)")
r = mustExecSQL(c, se, `show columns from t`)
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 1)
match(c, rows[0], "c", "int(11)", "YES", "", nil, "")
r = mustExecSQL(c, se, "show collation where Charset = 'utf8' and Collation = 'utf8_bin'")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "utf8_bin", "utf8", 83, "", "Yes", 1)
r = mustExecSQL(c, se, "show tables")
row, err = r.FirstRow()
c.Assert(err, IsNil)
c.Assert(row, HasLen, 1)
r = mustExecSQL(c, se, "show full tables")
row, err = r.FirstRow()
c.Assert(err, IsNil)
c.Assert(row, HasLen, 2)
r = mustExecSQL(c, se, "show create table t")
row, err = r.FirstRow()
c.Assert(err, IsNil)
c.Assert(row, HasLen, 2)
c.Assert(row[0], Equals, "t")
r = mustExecSQL(c, se, "show databases like 'test'")
row, err = r.FirstRow()
c.Assert(err, IsNil)
c.Assert(row, HasLen, 1)
c.Assert(row[0], Equals, "test")
}
func (s *testSessionSuite) TestTimeFunc(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
last := time.Now().Format(mysql.TimeFormat)
r := mustExecSQL(c, se, "select now(), now(6), current_timestamp, current_timestamp(), current_timestamp(6), sysdate(), sysdate(6)")
row, err := r.FirstRow()
c.Assert(err, IsNil)
for _, t := range row {
n, ok := t.(mysql.Time)
c.Assert(ok, IsTrue)
c.Assert(n.String(), GreaterEqual, last)
}
last = time.Now().Format(mysql.DateFormat)
r = mustExecSQL(c, se, "select current_date, current_date(), curdate()")
row, err = r.FirstRow()
c.Assert(err, IsNil)
for _, t := range row {
n, ok := t.(mysql.Time)
c.Assert(ok, IsTrue)
c.Assert(n.String(), GreaterEqual, last)
}
}
func (s *testSessionSuite) TestBit(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c1 bit(2))")
mustExecSQL(c, se, "insert into t values (0), (1), (2), (3)")
_, err := exec(c, se, "insert into t values (4)")
c.Assert(err, NotNil)
r := mustExecSQL(c, se, "select * from t where c1 = 2")
row, err := r.FirstRow()
c.Assert(err, IsNil)
c.Assert(row[0], Equals, mysql.Bit{Value: 2, Width: 2})
}
func (s *testSessionSuite) TestBootstrap(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "USE mysql;")
r := mustExecSQL(c, se, `select * from user;`)
c.Assert(r, NotNil)
row, err := r.Next()
c.Assert(err, IsNil)
c.Assert(row, NotNil)
match(c, row.Data, []byte("%"), []byte("root"), []byte(""), "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y")
c.Assert(se.Auth("root@anyhost", []byte(""), []byte("")), IsTrue)
mustExecSQL(c, se, "USE test;")
// Check privilege tables.
mustExecSQL(c, se, "SELECT * from mysql.db;")
mustExecSQL(c, se, "SELECT * from mysql.tables_priv;")
mustExecSQL(c, se, "SELECT * from mysql.columns_priv;")
// Check privilege tables.
r = mustExecSQL(c, se, "SELECT COUNT(*) from mysql.global_variables;")
c.Assert(r, NotNil)
v, err := r.FirstRow()
c.Assert(err, IsNil)
c.Assert(v[0], Equals, int64(len(variable.SysVars)))
// Check a storage operations are default autocommit after the second start.
mustExecSQL(c, se, "USE test;")
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (id int)")
delete(storeBootstrapped, store.UUID())
se.Close()
se, err = CreateSession(store)
c.Assert(err, IsNil)
mustExecSQL(c, se, "USE test;")
mustExecSQL(c, se, "insert t values (?)", 3)
se, err = CreateSession(store)
c.Assert(err, IsNil)
mustExecSQL(c, se, "USE test;")
r = mustExecSQL(c, se, "select * from t")
c.Assert(r, NotNil)
v, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, v, 3)
mustExecSQL(c, se, "drop table if exists t")
se.Close()
}
// Create a new session on store but only do ddl works.
func (s *testSessionSuite) bootstrapWithError(store kv.Storage, c *C) {
ss := &session{
values: make(map[fmt.Stringer]interface{}),
store: store,
sid: atomic.AddInt64(&sessionID, 1),
}
domain, err := domap.Get(store)
c.Assert(err, IsNil)
sessionctx.BindDomain(ss, domain)
variable.BindSessionVars(ss)
variable.GetSessionVars(ss).SetStatusFlag(mysql.ServerStatusAutocommit, true)
// session implements autocommit.Checker. Bind it to ctx
autocommit.BindAutocommitChecker(ss, ss)
sessionMu.Lock()
defer sessionMu.Unlock()
b, err := checkBootstrapped(ss)
c.Assert(b, IsFalse)
c.Assert(err, IsNil)
doDDLWorks(ss)
// Leave dml unfinished.
}
func (s *testSessionSuite) TestBootstrapWithError(c *C) {
store := newStore(c, s.dbNameBootstrap)
s.bootstrapWithError(store, c)
se := newSession(c, store, s.dbNameBootstrap)
mustExecSQL(c, se, "USE mysql;")
r := mustExecSQL(c, se, `select * from user;`)
row, err := r.Next()
c.Assert(err, IsNil)
c.Assert(row, NotNil)
match(c, row.Data, []byte("%"), []byte("root"), []byte(""), "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y")
mustExecSQL(c, se, "USE test;")
// Check privilege tables.
mustExecSQL(c, se, "SELECT * from mysql.db;")
mustExecSQL(c, se, "SELECT * from mysql.tables_priv;")
mustExecSQL(c, se, "SELECT * from mysql.columns_priv;")
// Check global variables.
r = mustExecSQL(c, se, "SELECT COUNT(*) from mysql.global_variables;")
v, err := r.FirstRow()
c.Assert(err, IsNil)
c.Assert(v[0], Equals, int64(len(variable.SysVars)))
r = mustExecSQL(c, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="bootstrapped";`)
row, err = r.Next()
c.Assert(err, IsNil)
c.Assert(row, NotNil)
c.Assert(row.Data, HasLen, 1)
c.Assert(row.Data[0], BytesEquals, []byte("True"))
}
func (s *testSessionSuite) TestEnum(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c enum('a', 'b', 'c'))")
mustExecSQL(c, se, "insert into t values ('a'), (2), ('c')")
r := mustExecSQL(c, se, "select * from t where c = 'a'")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "a")
r = mustExecSQL(c, se, "select c + 1 from t where c = 2")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "3")
mustExecSQL(c, se, "delete from t")
mustExecSQL(c, se, "insert into t values ()")
mustExecSQL(c, se, "insert into t values (null), ('1')")
r = mustExecSQL(c, se, "select c + 1 from t where c = 1")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "2")
}
func (s *testSessionSuite) TestSet(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c set('a', 'b', 'c'))")
mustExecSQL(c, se, "insert into t values ('a'), (2), ('c'), ('a,b'), ('b,a')")
r := mustExecSQL(c, se, "select * from t where c = 'a'")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "a")
r = mustExecSQL(c, se, "select * from t where c = 'a,b'")
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 2)
r = mustExecSQL(c, se, "select c + 1 from t where c = 2")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "3")
mustExecSQL(c, se, "delete from t")
mustExecSQL(c, se, "insert into t values ()")
mustExecSQL(c, se, "insert into t values (null), ('1')")
r = mustExecSQL(c, se, "select c + 1 from t where c = 1")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, "2")
}
func (s *testSessionSuite) TestDatabase(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
// Test database.
mustExecSQL(c, se, "create database xxx")
mustExecSQL(c, se, "drop database xxx")
mustExecSQL(c, se, "drop database if exists xxx")
mustExecSQL(c, se, "create database xxx")
mustExecSQL(c, se, "create database if not exists xxx")
mustExecSQL(c, se, "drop database if exists xxx")
// Test schema.
mustExecSQL(c, se, "create schema xxx")
mustExecSQL(c, se, "drop schema xxx")
mustExecSQL(c, se, "drop schema if exists xxx")
mustExecSQL(c, se, "create schema xxx")
mustExecSQL(c, se, "create schema if not exists xxx")
mustExecSQL(c, se, "drop schema if exists xxx")
}
func (s *testSessionSuite) TestWhereLike(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t(c int)")
mustExecSQL(c, se, "insert into t values (1),(2),(3),(-11),(11),(123),(211),(210)")
mustExecSQL(c, se, "insert into t values ()")
r := mustExecSQL(c, se, "select c from t where c like '%1%'")
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 6)
}
func (s *testSessionSuite) TestDefaultFlenBug(c *C) {
// If set unspecified column flen to 0, it will cause bug in union.
// This test is used to prevent the bug reappear.
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "create table t1 (c double);")
mustExecSQL(c, se, "create table t2 (c double);")
mustExecSQL(c, se, "insert into t1 value (73);")
mustExecSQL(c, se, "insert into t2 value (930);")
// The data in the second src will be casted as the type of the first src.
// If use flen=0, it will be truncated.
r := mustExecSQL(c, se, "select c from t1 union select c from t2;")
rows, err := r.Rows(-1, 0)
c.Assert(err, IsNil)
c.Assert(rows, HasLen, 2)
c.Assert(rows[1][0], Equals, float64(930))
}
func (s *testSessionSuite) TestExecRestrictedSQL(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName).(*session)
r, err := se.ExecRestrictedSQL(se, "select 1;")
c.Assert(r, NotNil)
c.Assert(err, IsNil)
_, err = se.ExecRestrictedSQL(se, "select 1; select 2;")
c.Assert(err, NotNil)
_, err = se.ExecRestrictedSQL(se, "")
c.Assert(err, NotNil)
}
func (s *testSessionSuite) TestGroupBy(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c1 int, c2 int)")
mustExecSQL(c, se, "insert into t values (1,1), (2,2), (1,2), (1,3)")
mustExecMatch(c, se, "select nullif (count(*), 2);", [][]interface{}{{1}})
mustExecMatch(c, se, "select 1 as a, sum(c1) as a from t group by a", [][]interface{}{{1, 5}})
mustExecMatch(c, se, "select c1 as a, 1 as a, sum(c1) as a from t group by a", [][]interface{}{{1, 1, 5}})
mustExecMatch(c, se, "select c1 as a, 1 as a, c2 as a from t group by a;", [][]interface{}{{1, 1, 1}})
mustExecMatch(c, se, "select c1 as c2, sum(c1) as c2 from t group by c2;", [][]interface{}{{1, 1}, {2, 3}, {1, 1}})
mustExecMatch(c, se, "select c1 as c2, c2 from t group by c2 + 1", [][]interface{}{{1, 1}, {2, 2}, {1, 3}})
mustExecMatch(c, se, "select c1 as c2, count(c1) from t group by c2", [][]interface{}{{1, 1}, {2, 2}, {1, 1}})
mustExecMatch(c, se, "select t.c1, c1 from t group by c1", [][]interface{}{{1, 1}, {2, 2}})
mustExecMatch(c, se, "select t.c1 as a, c1 as a from t group by a", [][]interface{}{{1, 1}, {2, 2}})
mustExecFailed(c, se, "select c1 as a, c2 as a from t group by a")
mustExecFailed(c, se, "select c1 as c2, c2 from t group by c2")
mustExecFailed(c, se, "select sum(c1) as a from t group by a")
mustExecFailed(c, se, "select sum(c1) as a from t group by a + 1")
}
func (s *testSessionSuite) TestOrderBy(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c1 int, c2 int)")
mustExecSQL(c, se, "insert into t values (1,2), (2, 1)")
// Fix issue https://github.com/pingcap/tidb/issues/337
mustExecMatch(c, se, "select c1 as a, c1 as b from t order by c1", [][]interface{}{{1, 1}, {2, 2}})
mustExecMatch(c, se, "select c1 as a, t.c1 as a from t order by a desc", [][]interface{}{{2, 2}, {1, 1}})
mustExecMatch(c, se, "select c1 as c2 from t order by c2", [][]interface{}{{1}, {2}})
mustExecMatch(c, se, "select sum(c1) from t order by sum(c1)", [][]interface{}{{3}})
mustExecMatch(c, se, "select c1 as c2 from t order by c2 + 1", [][]interface{}{{2}, {1}})
// Order by position
mustExecMatch(c, se, "select * from t order by 1", [][]interface{}{{1, 2}, {2, 1}})
mustExecMatch(c, se, "select * from t order by 2", [][]interface{}{{2, 1}, {1, 2}})
mustExecFailed(c, se, "select * from t order by 0")
mustExecFailed(c, se, "select * from t order by 3")
mustExecFailed(c, se, "select c1 as a, c2 as a from t order by a")
mustExecFailed(c, se, "(select c1 as c2, c2 from t) union (select c1, c2 from t) order by c2")
mustExecFailed(c, se, "(select c1 as c2, c2 from t) union (select c1, c2 from t) order by c1")
}
func (s *testSessionSuite) TestHaving(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c1 int, c2 int, c3 int)")
mustExecSQL(c, se, "insert into t values (1,2,3), (2, 3, 1), (3, 1, 2)")
mustExecMatch(c, se, "select c1 as c2, c3 from t having c2 = 2", [][]interface{}{{2, 1}})
mustExecMatch(c, se, "select c1 as c2, c3 from t group by c2 having c2 = 2;", [][]interface{}{{1, 3}})
mustExecMatch(c, se, "select c1 as c2, c3 from t group by c2 having sum(c2) = 2;", [][]interface{}{{1, 3}})
mustExecMatch(c, se, "select c1 as c2, c3 from t group by c3 having sum(c2) = 2;", [][]interface{}{{1, 3}})
mustExecMatch(c, se, "select c1 as c2, c3 from t group by c3 having sum(0) + c2 = 2;", [][]interface{}{{2, 1}})
mustExecMatch(c, se, "select c1 as a from t having c1 = 1;", [][]interface{}{{1}})
mustExecMatch(c, se, "select t.c1 from t having c1 = 1;", [][]interface{}{{1}})
mustExecMatch(c, se, "select a.c1 from t as a having c1 = 1;", [][]interface{}{{1}})
mustExecMatch(c, se, "select c1 as a from t group by c3 having sum(a) = 1;", [][]interface{}{{1}})
mustExecMatch(c, se, "select c1 as a from t group by c3 having sum(a) + a = 2;", [][]interface{}{{1}})
mustExecMatch(c, se, "select a.c1 as c, a.c1 as d from t as a, t as b having c1 = 1 limit 1;", [][]interface{}{{1, 1}})
mustExecMatch(c, se, "select sum(c1) from t group by c1 having sum(c1)", [][]interface{}{{1}, {2}, {3}})
mustExecMatch(c, se, "select sum(c1) - 1 from t group by c1 having sum(c1) - 1", [][]interface{}{{1}, {2}})
mustExecMatch(c, se, "select 1 from t group by c1 having sum(abs(c2 + c3)) = c1", [][]interface{}{{1}})
mustExecFailed(c, se, "select c1 from t having c2")
mustExecFailed(c, se, "select c1 from t having c2 + 1")
mustExecFailed(c, se, "select c1 from t group by c2 + 1 having c2")
mustExecFailed(c, se, "select c1 from t group by c2 + 1 having c2 + 1")
mustExecFailed(c, se, "select c1 as c2, c2 from t having c2")
mustExecFailed(c, se, "select c1 as c2, c2 from t having c2 + 1")
mustExecFailed(c, se, "select c1 as a, c2 as a from t having a")
mustExecFailed(c, se, "select c1 as a, c2 as a from t having a + 1")
mustExecFailed(c, se, "select c1 + 1 from t having c1")
mustExecFailed(c, se, "select c1 + 1 from t having c1 + 1")
mustExecFailed(c, se, "select a.c1 as c, b.c1 as d from t as a, t as b having c1")
mustExecFailed(c, se, "select 1 from t having sum(avg(c1))")
}
func (s *testSessionSuite) TestResultType(c *C) {
// Testcase for https://github.com/pingcap/tidb/issues/325
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
rs := mustExecSQL(c, se, `select cast(null as char(30))`)
c.Assert(rs, NotNil)
row, err := rs.Next()
c.Assert(err, IsNil)
c.Assert(row.Data[0], IsNil)
fs, err := rs.Fields()
c.Assert(err, IsNil)
c.Assert(fs[0].Col.FieldType.Tp, Equals, mysql.TypeString)
}
func (s *testSessionSuite) TestIssue461(c *C) {
store := newStore(c, s.dbName)
se1 := newSession(c, store, s.dbName)
mustExecSQL(c, se1,
`CREATE TABLE test ( id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, val int UNIQUE, PRIMARY KEY (id)); `)
mustExecSQL(c, se1, "begin;")
mustExecSQL(c, se1, "insert into test(id, val) values(1, 1);")
se2 := newSession(c, store, s.dbName)
mustExecSQL(c, se2, "begin;")
mustExecSQL(c, se2, "insert into test(id, val) values(1, 1);")
mustExecSQL(c, se2, "commit;")
mustExecFailed(c, se1, "commit;")
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table test;")
}
func (s *testSessionSuite) TestIssue463(c *C) {
// Testcase for https://github.com/pingcap/tidb/issues/463
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se,
`CREATE TABLE test (
id int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
val int UNIQUE,
PRIMARY KEY (id)
);`)
mustExecSQL(c, se, "insert into test(id, val) values(1, 1);")
mustExecFailed(c, se, "insert into test(id, val) values(2, 1);")
mustExecSQL(c, se, "insert into test(id, val) values(2, 2);")
mustExecSQL(c, se, "begin;")
mustExecSQL(c, se, "insert into test(id, val) values(3, 3);")
mustExecFailed(c, se, "insert into test(id, val) values(4, 3);")
mustExecSQL(c, se, "insert into test(id, val) values(4, 4);")
mustExecSQL(c, se, "commit;")
se1 := newSession(c, store, s.dbName)
mustExecSQL(c, se1, "begin;")
mustExecSQL(c, se1, "insert into test(id, val) values(5, 6);")
mustExecSQL(c, se, "begin;")
mustExecSQL(c, se, "insert into test(id, val) values(20, 6);")
mustExecSQL(c, se, "commit;")
mustExecFailed(c, se1, "commit;")
mustExecSQL(c, se1, "insert into test(id, val) values(5, 5);")
mustExecSQL(c, se, "drop table test;")
mustExecSQL(c, se,
`CREATE TABLE test (
id int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
val1 int UNIQUE,
val2 int UNIQUE,
PRIMARY KEY (id)
);`)
mustExecSQL(c, se, "insert into test(id, val1, val2) values(1, 1, 1);")
mustExecSQL(c, se, "insert into test(id, val1, val2) values(2, 2, 2);")
mustExecFailed(c, se, "update test set val1 = 3, val2 = 2 where id = 1;")
mustExecSQL(c, se, "insert into test(id, val1, val2) values(3, 3, 3);")
mustExecSQL(c, se, "drop table test;")
}
func (s *testSessionSuite) TestIssue177(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, `drop table if exists t1;`)
mustExecSQL(c, se, `drop table if exists t2;`)
mustExecSQL(c, se, "CREATE TABLE `t1` ( `a` char(3) NOT NULL default '', `b` char(3) NOT NULL default '', `c` char(3) NOT NULL default '', PRIMARY KEY (`a`,`b`,`c`)) ENGINE=InnoDB;")
mustExecSQL(c, se, "CREATE TABLE `t2` ( `a` char(3) NOT NULL default '', `b` char(3) NOT NULL default '', `c` char(3) NOT NULL default '', PRIMARY KEY (`a`,`b`,`c`)) ENGINE=InnoDB;")
mustExecSQL(c, se, `INSERT INTO t1 VALUES (1,1,1);`)
mustExecSQL(c, se, `INSERT INTO t2 VALUES (1,1,1);`)
mustExecSQL(c, se, `PREPARE my_stmt FROM "SELECT t1.b, count(*) FROM t1 group by t1.b having count(*) > ALL (SELECT COUNT(*) FROM t2 WHERE t2.a=1 GROUP By t2.b)";`)
mustExecSQL(c, se, `EXECUTE my_stmt;`)
mustExecSQL(c, se, `EXECUTE my_stmt;`)
mustExecSQL(c, se, `deallocate prepare my_stmt;`)
mustExecSQL(c, se, `drop table t1,t2;`)
}
func (s *testSessionSuite) TestBuiltin(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
// Testcase for https://github.com/pingcap/tidb/issues/382
mustExecFailed(c, se, `select cast("xxx 10:10:10" as datetime)`)
}
func (s *testSessionSuite) TestFieldText(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (a int)")
cases := []struct {
sql string
field string
}{
{"select distinct(a) from t", "a"},
{"select (1)", "1"},
{"select (1+1)", "(1+1)"},
{"select a from t", "a"},
{"select ((a+1)) from t", "((a+1))"},
}
for _, v := range cases {
results, err := se.Execute(v.sql)
c.Assert(err, IsNil)
result := results[0]
fields, err := result.Fields()
c.Assert(err, IsNil)
c.Assert(fields[0].Name, Equals, v.field)
}
}
func (s *testSessionSuite) TestIndexPointLookup(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (a int)")
mustExecSQL(c, se, "insert t values (1), (2), (3)")
mustExecMatch(c, se, "select * from t where a = '1.1';", [][]interface{}{})
mustExecMatch(c, se, "select * from t where a > '1.1';", [][]interface{}{{2}, {3}})
mustExecMatch(c, se, "select * from t where a = '2';", [][]interface{}{{2}})
mustExecMatch(c, se, "select * from t where a = 3;", [][]interface{}{{3}})
mustExecMatch(c, se, "select * from t where a = 4;", [][]interface{}{})
mustExecSQL(c, se, "drop table t")
}
func (s *testSessionSuite) TestIssue454(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "drop table if exists t1")
mustExecSQL(c, se, "create table t1 (c1 int, c2 int, c3 int);")
mustExecSQL(c, se, "insert into t1 set c1=1, c2=2, c3=1;")
mustExecSQL(c, se, "create table t (c1 int, c2 int, c3 int, primary key (c1));")
mustExecSQL(c, se, "insert into t set c1=1, c2=4;")
mustExecSQL(c, se, "insert into t select * from t1 limit 1 on duplicate key update c3=3333;")
}
func (s *testSessionSuite) TestIssue456(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "drop table if exists t1")
mustExecSQL(c, se, "create table t1 (c1 int, c2 int, c3 int);")
mustExecSQL(c, se, "replace into t1 set c1=1, c2=2, c3=1;")
mustExecSQL(c, se, "create table t (c1 int, c2 int, c3 int, primary key (c1));")
mustExecSQL(c, se, "replace into t set c1=1, c2=4;")
mustExecSQL(c, se, "replace into t select * from t1 limit 1;")
}
// For https://github.com/pingcap/tidb/issues/571
func (s *testSessionSuite) TestIssue571(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "begin")
mustExecSQL(c, se, "drop table if exists t")
mustExecSQL(c, se, "create table t (c int)")
mustExecSQL(c, se, "insert t values (1), (2), (3)")
mustExecSQL(c, se, "commit")
se1 := newSession(c, store, s.dbName)
se1.(*session).maxRetryCnt = unlimitedRetryCnt
mustExecSQL(c, se1, "SET SESSION autocommit=1;")
se2 := newSession(c, store, s.dbName)
se2.(*session).maxRetryCnt = unlimitedRetryCnt
mustExecSQL(c, se2, "SET SESSION autocommit=1;")
se3 := newSession(c, store, s.dbName)
se3.(*session).maxRetryCnt = unlimitedRetryCnt
mustExecSQL(c, se3, "SET SESSION autocommit=0;")
var wg sync.WaitGroup
wg.Add(3)
f1 := func() {
defer wg.Done()
for i := 0; i < 30; i++ {
mustExecSQL(c, se1, "update t set c = 1;")
}
}
f2 := func() {
defer wg.Done()
for i := 0; i < 30; i++ {
mustExecSQL(c, se2, "update t set c = ?;", 1)
}
}
f3 := func() {
defer wg.Done()
for i := 0; i < 30; i++ {
mustExecSQL(c, se3, "begin")
mustExecSQL(c, se3, "update t set c = 1;")
mustExecSQL(c, se3, "commit")
}
}
go f1()
go f2()
go f3()
wg.Wait()
}
func (s *testSessionSuite) TestIssue620(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t1")
mustExecSQL(c, se, "drop table if exists t2")
mustExecSQL(c, se, "drop table if exists t3")
mustExecSQL(c, se, "create table t1(id int primary key auto_increment, c int);")
mustExecSQL(c, se, "create table t2(c int); insert into t2 values (1);")
mustExecSQL(c, se, "create table t3(id int, c int); insert into t3 values (2,2);")
mustExecSQL(c, se, "insert into t1(c) select * from t2; insert into t1 select * from t3;")
mustExecMatch(c, se, "select * from t1;", [][]interface{}{{1, 1}, {2, 2}})
}
func (s *testSessionSuite) TestRetryPreparedStmt(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
se1 := newSession(c, store, s.dbName)
se2 := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t")
c.Assert(se.(*session).txn, IsNil)
mustExecSQL(c, se, "create table t (c1 int, c2 int, c3 int)")
mustExecSQL(c, se, "insert t values (11, 2, 3)")
mustExecSQL(c, se1, "begin")
mustExecSQL(c, se1, "update t set c2=? where c1=11;", 21)
mustExecSQL(c, se2, "begin")
mustExecSQL(c, se2, "update t set c2=? where c1=11", 22)
mustExecSQL(c, se2, "commit")
mustExecSQL(c, se1, "commit")
se3 := newSession(c, store, s.dbName)
r := mustExecSQL(c, se3, "select c2 from t where c1=11")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 21)
}
// Testcase for session
func (s *testSessionSuite) TestSession(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "ROLLBACK;")
err := se.Close()
c.Assert(err, IsNil)
}
func (s *testSessionSuite) TestErrorRollback(c *C) {
store := newStore(c, s.dbName)
s1 := newSession(c, store, s.dbName)
defer s1.Close()
mustExecSQL(c, s1, "drop table if exists t_rollback")
mustExecSQL(c, s1, "create table t_rollback (c1 int, c2 int, primary key(c1))")
_, err := s1.Execute("insert into t_rollback values (0, 0)")
c.Assert(err, IsNil)
var wg sync.WaitGroup
cnt := 4
wg.Add(cnt)
num := 100
for i := 0; i < cnt; i++ {
go func() {
defer wg.Done()
se := newSession(c, store, s.dbName)
// retry forever
se.(*session).maxRetryCnt = unlimitedRetryCnt
defer se.Close()
for j := 0; j < num; j++ {
// force generate a txn in session for later insert use.
se.(*session).GetTxn(false)
se.Execute("insert into t_rollback values (1, 1, 1)")
_, err := se.Execute("update t_rollback set c2 = c2 + 1 where c1 = 0")
c.Assert(err, IsNil)
}
}()
}
wg.Wait()
mustExecMatch(c, s1, "select c2 from t_rollback where c1 = 0", [][]interface{}{{cnt * num}})
}
func (s *testSessionSuite) TestMultiColumnIndex(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t;")
mustExecSQL(c, se, "create table t (c1 int, c2 int);")
mustExecSQL(c, se, "create index idx_c1_c2 on t (c1, c2)")
mustExecSQL(c, se, "insert into t values (1, 5)")
sql := "select c1 from t where c1 in (1) and c2 < 10"
expectedExplain := "Index(t.idx_c1_c2)->Filter->Fields"
checkPlan(c, se, sql, expectedExplain)
mustExecMatch(c, se, sql, [][]interface{}{{1}})
sql = "select c1 from t where c1 in (1) and c2 > 3"
checkPlan(c, se, sql, expectedExplain)
mustExecMatch(c, se, sql, [][]interface{}{{1}})
sql = "select c1 from t where c1 in (1.1) and c2 > 3"
checkPlan(c, se, sql, expectedExplain)
mustExecMatch(c, se, sql, [][]interface{}{})
sql = "select c1 from t where c1 in (1) and c2 < 5.1"
checkPlan(c, se, sql, expectedExplain)
mustExecMatch(c, se, sql, [][]interface{}{{1}})
err := se.Close()
c.Assert(err, IsNil)
}
func (s *testSessionSuite) TestSubstringIndexExpr(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
mustExecSQL(c, se, "drop table if exists t;")
mustExecSQL(c, se, "create table t (c varchar(128));")
mustExecSQL(c, se, `insert into t values ("www.pingcap.com");`)
mustExecMatch(c, se, "SELECT DISTINCT SUBSTRING_INDEX(c, '.', 2) from t;", [][]interface{}{{"www.pingcap"}})
}
func (s *testSessionSuite) TestGlobalVarAccessor(c *C) {
varName := "max_allowed_packet"
varValue := "4194304" // This is the default value for max_allowed_packet
varValue1 := "4194305"
varValue2 := "4194306"
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName).(*session)
// Get globalSysVar twice and get the same value
v, err := se.GetGlobalSysVar(se, varName)
c.Assert(err, IsNil)
c.Assert(v, Equals, varValue)
v, err = se.GetGlobalSysVar(se, varName)
c.Assert(err, IsNil)
c.Assert(v, Equals, varValue)
// Set global var to another value
err = se.SetGlobalSysVar(se, varName, varValue1)
c.Assert(err, IsNil)
v, err = se.GetGlobalSysVar(se, varName)
c.Assert(err, IsNil)
c.Assert(v, Equals, varValue1)
c.Assert(se.FinishTxn(false), IsNil)
// Change global variable value in another session
se1 := newSession(c, store, s.dbName).(*session)
v, err = se1.GetGlobalSysVar(se1, varName)
c.Assert(err, IsNil)
c.Assert(v, Equals, varValue1)
err = se1.SetGlobalSysVar(se1, varName, varValue2)
c.Assert(err, IsNil)
v, err = se1.GetGlobalSysVar(se1, varName)
c.Assert(err, IsNil)
c.Assert(v, Equals, varValue2)
c.Assert(se1.FinishTxn(false), IsNil)
// Make sure the change is visible to any client that accesses that global variable.
v, err = se.GetGlobalSysVar(se, varName)
c.Assert(err, IsNil)
c.Assert(v, Equals, varValue2)
}
func checkPlan(c *C, se Session, sql, explain string) {
ctx := se.(context.Context)
stmts, err := Parse(ctx, sql)
c.Assert(err, IsNil)
stmt := stmts[0]
c.Assert(optimizer.IsSupported(stmt), IsTrue)
is := sessionctx.GetDomain(ctx).InfoSchema()
p, err := optimizer.Optimize(is, ctx, stmt)
c.Assert(err, IsNil)
planStr, err := plan.Explain(p)
c.Assert(err, IsNil)
c.Assert(planStr, Equals, explain)
}