734 lines
22 KiB
Go
734 lines
22 KiB
Go
// Copyright 2017 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 kvenc
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pingcap/tidb/meta/autoid"
|
|
"github.com/pingcap/tidb/store/mockstore"
|
|
|
|
. "github.com/pingcap/check"
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/tidb/domain"
|
|
"github.com/pingcap/tidb/kv"
|
|
"github.com/pingcap/tidb/session"
|
|
"github.com/pingcap/tidb/sessionctx/stmtctx"
|
|
"github.com/pingcap/tidb/structure"
|
|
"github.com/pingcap/tidb/tablecodec"
|
|
"github.com/pingcap/tidb/types"
|
|
"github.com/pingcap/tidb/util/codec"
|
|
"github.com/pingcap/tidb/util/testkit"
|
|
"github.com/pingcap/tidb/util/testleak"
|
|
)
|
|
|
|
var _ = Suite(&testKvEncoderSuite{})
|
|
|
|
func TestT(t *testing.T) {
|
|
TestingT(t)
|
|
}
|
|
|
|
func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) {
|
|
store, err := mockstore.NewMockTikvStore()
|
|
if err != nil {
|
|
return nil, nil, errors.Trace(err)
|
|
}
|
|
session.SetSchemaLease(0)
|
|
session.DisableStats4Test()
|
|
dom, err := session.BootstrapSession(store)
|
|
return store, dom, errors.Trace(err)
|
|
}
|
|
|
|
type testKvEncoderSuite struct {
|
|
store kv.Storage
|
|
dom *domain.Domain
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) cleanEnv(c *C) {
|
|
tk := testkit.NewTestKit(c, s.store)
|
|
tk.MustExec("use test")
|
|
r := tk.MustQuery("show tables")
|
|
for _, tb := range r.Rows() {
|
|
tableName := tb[0]
|
|
tk.MustExec(fmt.Sprintf("drop table %v", tableName))
|
|
}
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) SetUpSuite(c *C) {
|
|
testleak.BeforeTest()
|
|
store, dom, err := newStoreWithBootstrap()
|
|
c.Assert(err, IsNil)
|
|
s.store = store
|
|
s.dom = dom
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TearDownSuite(c *C) {
|
|
s.dom.Close()
|
|
s.store.Close()
|
|
testleak.AfterTest(c)()
|
|
}
|
|
|
|
func getExpectKvPairs(tkExpect *testkit.TestKit, sql string) []KvPair {
|
|
tkExpect.MustExec("begin")
|
|
tkExpect.MustExec(sql)
|
|
kvPairsExpect := make([]KvPair, 0)
|
|
txn, err := tkExpect.Se.Txn(true)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
kv.WalkMemBuffer(txn.GetMemBuffer(), func(k kv.Key, v []byte) error {
|
|
kvPairsExpect = append(kvPairsExpect, KvPair{Key: k, Val: v})
|
|
return nil
|
|
})
|
|
tkExpect.MustExec("rollback")
|
|
return kvPairsExpect
|
|
}
|
|
|
|
type testCase struct {
|
|
sql string
|
|
// expectEmptyValCnt only check if expectEmptyValCnt > 0
|
|
expectEmptyValCnt int
|
|
// expectKvCnt only check if expectKvCnt > 0
|
|
expectKvCnt int
|
|
expectAffectedRows int
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) runTestSQL(c *C, tkExpect *testkit.TestKit, encoder KvEncoder, cases []testCase, tableID int64) {
|
|
for _, ca := range cases {
|
|
comment := fmt.Sprintf("sql:%v", ca.sql)
|
|
kvPairs, affectedRows, err := encoder.Encode(ca.sql, tableID)
|
|
c.Assert(err, IsNil, Commentf(comment))
|
|
|
|
if ca.expectAffectedRows > 0 {
|
|
c.Assert(affectedRows, Equals, uint64(ca.expectAffectedRows))
|
|
}
|
|
|
|
kvPairsExpect := getExpectKvPairs(tkExpect, ca.sql)
|
|
c.Assert(len(kvPairs), Equals, len(kvPairsExpect), Commentf(comment))
|
|
if ca.expectKvCnt > 0 {
|
|
c.Assert(len(kvPairs), Equals, ca.expectKvCnt, Commentf(comment))
|
|
}
|
|
|
|
emptyValCount := 0
|
|
for i, row := range kvPairs {
|
|
expectKey := kvPairsExpect[i].Key
|
|
if bytes.HasPrefix(row.Key, tablecodec.TablePrefix()) {
|
|
expectKey = tablecodec.ReplaceRecordKeyTableID(expectKey, tableID)
|
|
}
|
|
c.Assert(bytes.Compare(row.Key, expectKey), Equals, 0, Commentf(comment))
|
|
c.Assert(bytes.Compare(row.Val, kvPairsExpect[i].Val), Equals, 0, Commentf(comment))
|
|
|
|
if len(row.Val) == 0 {
|
|
emptyValCount++
|
|
}
|
|
}
|
|
if ca.expectEmptyValCnt > 0 {
|
|
c.Assert(emptyValCount, Equals, ca.expectEmptyValCnt, Commentf(comment))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestCustomDatabaseHandle(c *C) {
|
|
dbname := "tidb"
|
|
|
|
tkExpect := testkit.NewTestKit(c, s.store)
|
|
tkExpect.MustExec("create database if not exists " + dbname)
|
|
tkExpect.MustExec("use " + dbname)
|
|
|
|
encoder, err := New(dbname, nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
var tableID int64 = 123
|
|
|
|
schemaSQL := "create table tis (id int auto_increment, a char(10), primary key(id))"
|
|
tkExpect.MustExec(schemaSQL)
|
|
err = encoder.ExecDDLSQL(schemaSQL)
|
|
c.Assert(err, IsNil)
|
|
|
|
sqls := []testCase{
|
|
{"insert into tis (a) values('test')", 0, 1, 1},
|
|
{"insert into tis (a) values('test'), ('test1')", 0, 2, 2},
|
|
}
|
|
|
|
s.runTestSQL(c, tkExpect, encoder, sqls, tableID)
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestInsertPkIsHandle(c *C) {
|
|
tkExpect := testkit.NewTestKit(c, s.store)
|
|
tkExpect.MustExec("use test")
|
|
var tableID int64 = 1
|
|
encoder, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
tkExpect.MustExec("drop table if exists t")
|
|
schemaSQL := "create table t(id int auto_increment, a char(10), primary key(id))"
|
|
tkExpect.MustExec(schemaSQL)
|
|
err = encoder.ExecDDLSQL(schemaSQL)
|
|
c.Assert(err, IsNil)
|
|
|
|
sqls := []testCase{
|
|
{"insert into t values(1, 'test');", 0, 1, 1},
|
|
{"insert into t(a) values('test')", 0, 1, 1},
|
|
{"insert into t(a) values('test'), ('test1')", 0, 2, 2},
|
|
{"insert into t(id, a) values(3, 'test')", 0, 1, 1},
|
|
{"insert into t values(1000000, 'test')", 0, 1, 1},
|
|
{"insert into t(a) values('test')", 0, 1, 1},
|
|
{"insert into t(id, a) values(4, 'test')", 0, 1, 1},
|
|
}
|
|
|
|
s.runTestSQL(c, tkExpect, encoder, sqls, tableID)
|
|
|
|
tkExpect.MustExec("drop table if exists t1")
|
|
schemaSQL = "create table t1(id int auto_increment, a char(10), primary key(id), key a_idx(a))"
|
|
tkExpect.MustExec(schemaSQL)
|
|
err = encoder.ExecDDLSQL(schemaSQL)
|
|
c.Assert(err, IsNil)
|
|
|
|
tableID = 2
|
|
sqls = []testCase{
|
|
{"insert into t1 values(1, 'test');", 0, 2, 1},
|
|
{"insert into t1(a) values('test')", 0, 2, 1},
|
|
{"insert into t1(id, a) values(3, 'test')", 0, 2, 1},
|
|
{"insert into t1 values(1000000, 'test')", 0, 2, 1},
|
|
{"insert into t1(a) values('test')", 0, 2, 1},
|
|
{"insert into t1(id, a) values(4, 'test')", 0, 2, 1},
|
|
}
|
|
|
|
s.runTestSQL(c, tkExpect, encoder, sqls, tableID)
|
|
|
|
schemaSQL = `create table t2(
|
|
id int auto_increment,
|
|
a char(10),
|
|
b datetime default NULL,
|
|
primary key(id),
|
|
key a_idx(a),
|
|
unique b_idx(b))
|
|
`
|
|
|
|
tableID = 3
|
|
tkExpect.MustExec("drop table if exists t2")
|
|
tkExpect.MustExec(schemaSQL)
|
|
err = encoder.ExecDDLSQL(schemaSQL)
|
|
c.Assert(err, IsNil)
|
|
|
|
sqls = []testCase{
|
|
{"insert into t2(id, a) values(1, 'test')", 0, 3, 1},
|
|
{"insert into t2 values(2, 'test', '2017-11-27 23:59:59')", 0, 3, 1},
|
|
{"insert into t2 values(3, 'test', '2017-11-28 23:59:59')", 0, 3, 1},
|
|
}
|
|
s.runTestSQL(c, tkExpect, encoder, sqls, tableID)
|
|
}
|
|
|
|
type prepareTestCase struct {
|
|
sql string
|
|
formatSQL string
|
|
param []interface{}
|
|
}
|
|
|
|
func makePrepareTestCase(sql, formatSQL string, param ...interface{}) prepareTestCase {
|
|
return prepareTestCase{sql, formatSQL, param}
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestPrepareEncode(c *C) {
|
|
alloc := NewAllocator()
|
|
encoder, err := New("test", alloc)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
schemaSQL := "create table t(id int auto_increment, a char(10), primary key(id))"
|
|
err = encoder.ExecDDLSQL(schemaSQL)
|
|
c.Assert(err, IsNil)
|
|
|
|
cases := []prepareTestCase{
|
|
makePrepareTestCase("insert into t values(1, 'test')", "insert into t values(?, ?)", 1, "test"),
|
|
makePrepareTestCase("insert into t(a) values('test')", "insert into t(a) values(?)", "test"),
|
|
makePrepareTestCase("insert into t(a) values('test'), ('test1')", "insert into t(a) values(?), (?)", "test", "test1"),
|
|
makePrepareTestCase("insert into t(id, a) values(3, 'test')", "insert into t(id, a) values(?, ?)", 3, "test"),
|
|
}
|
|
tableID := int64(1)
|
|
|
|
for _, ca := range cases {
|
|
s.comparePrepareAndNormalEncode(c, alloc, tableID, encoder, ca.sql, ca.formatSQL, ca.param...)
|
|
}
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) comparePrepareAndNormalEncode(c *C, alloc autoid.Allocator, tableID int64, encoder KvEncoder, sql, prepareFormat string, param ...interface{}) {
|
|
comment := fmt.Sprintf("sql:%v", sql)
|
|
baseID := alloc.Base()
|
|
kvPairsExpect, affectedRowsExpect, err := encoder.Encode(sql, tableID)
|
|
c.Assert(err, IsNil, Commentf(comment))
|
|
|
|
stmtID, err := encoder.PrepareStmt(prepareFormat)
|
|
c.Assert(err, IsNil, Commentf(comment))
|
|
alloc.(*Allocator).Reset(baseID)
|
|
kvPairs, affectedRows, err := encoder.EncodePrepareStmt(tableID, stmtID, param...)
|
|
c.Assert(err, IsNil, Commentf(comment))
|
|
c.Assert(affectedRows, Equals, affectedRowsExpect, Commentf(comment))
|
|
c.Assert(len(kvPairs), Equals, len(kvPairsExpect), Commentf(comment))
|
|
|
|
for i, kvPair := range kvPairs {
|
|
kvPairExpect := kvPairsExpect[i]
|
|
c.Assert(bytes.Compare(kvPair.Key, kvPairExpect.Key), Equals, 0, Commentf(comment))
|
|
c.Assert(bytes.Compare(kvPair.Val, kvPairExpect.Val), Equals, 0, Commentf(comment))
|
|
}
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestInsertPkIsNotHandle(c *C) {
|
|
tkExpect := testkit.NewTestKit(c, s.store)
|
|
tkExpect.MustExec("use test")
|
|
|
|
var tableID int64 = 1
|
|
encoder, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
tkExpect.MustExec("drop table if exists t")
|
|
schemaSQL := `create table t(
|
|
id varchar(20),
|
|
a char(10),
|
|
primary key(id))`
|
|
tkExpect.MustExec(schemaSQL)
|
|
c.Assert(encoder.ExecDDLSQL(schemaSQL), IsNil)
|
|
|
|
sqls := []testCase{
|
|
{"insert into t values(1, 'test');", 0, 2, 1},
|
|
{"insert into t(id, a) values(2, 'test')", 0, 2, 1},
|
|
{"insert into t(id, a) values(3, 'test')", 0, 2, 1},
|
|
{"insert into t values(1000000, 'test')", 0, 2, 1},
|
|
{"insert into t(id, a) values(5, 'test')", 0, 2, 1},
|
|
{"insert into t(id, a) values(4, 'test')", 0, 2, 1},
|
|
{"insert into t(id, a) values(6, 'test'), (7, 'test'), (8, 'test')", 0, 6, 3},
|
|
}
|
|
|
|
s.runTestSQL(c, tkExpect, encoder, sqls, tableID)
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestRetryWithAllocator(c *C) {
|
|
tk := testkit.NewTestKit(c, s.store)
|
|
|
|
tk.MustExec("use test")
|
|
alloc := NewAllocator()
|
|
var tableID int64 = 1
|
|
encoder, err := New("test", alloc)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
tk.MustExec("drop table if exists t")
|
|
schemaSQL := `create table t(
|
|
id int auto_increment,
|
|
a char(10),
|
|
primary key(id))`
|
|
tk.MustExec(schemaSQL)
|
|
c.Assert(encoder.ExecDDLSQL(schemaSQL), IsNil)
|
|
|
|
sqls := []string{
|
|
"insert into t(a) values('test');",
|
|
"insert into t(a) values('test'), ('1'), ('2'), ('3');",
|
|
"insert into t(id, a) values(1000000, 'test')",
|
|
"insert into t(id, a) values(5, 'test')",
|
|
"insert into t(id, a) values(4, 'test')",
|
|
}
|
|
|
|
for _, sql := range sqls {
|
|
baseID := alloc.Base()
|
|
kvPairs, _, err1 := encoder.Encode(sql, tableID)
|
|
c.Assert(err1, IsNil, Commentf("sql:%s", sql))
|
|
alloc.Reset(baseID)
|
|
retryKvPairs, _, err1 := encoder.Encode(sql, tableID)
|
|
c.Assert(err1, IsNil, Commentf("sql:%s", sql))
|
|
c.Assert(len(kvPairs), Equals, len(retryKvPairs))
|
|
for i, row := range kvPairs {
|
|
c.Assert(bytes.Compare(row.Key, retryKvPairs[i].Key), Equals, 0, Commentf(sql))
|
|
c.Assert(bytes.Compare(row.Val, retryKvPairs[i].Val), Equals, 0, Commentf(sql))
|
|
}
|
|
}
|
|
|
|
// specify id, it must be the same row
|
|
sql := "insert into t(id, a) values(5, 'test')"
|
|
kvPairs, _, err := encoder.Encode(sql, tableID)
|
|
c.Assert(err, IsNil, Commentf("sql:%s", sql))
|
|
retryKvPairs, _, err := encoder.Encode(sql, tableID)
|
|
c.Assert(err, IsNil, Commentf("sql:%s", sql))
|
|
c.Assert(len(kvPairs), Equals, len(retryKvPairs))
|
|
for i, row := range kvPairs {
|
|
c.Assert(bytes.Compare(row.Key, retryKvPairs[i].Key), Equals, 0, Commentf(sql))
|
|
c.Assert(bytes.Compare(row.Val, retryKvPairs[i].Val), Equals, 0, Commentf(sql))
|
|
}
|
|
|
|
tk.MustExec("drop table if exists t1")
|
|
schemaSQL = `create table t1(
|
|
id int auto_increment,
|
|
a char(10),
|
|
b char(10),
|
|
primary key(id),
|
|
KEY idx_a(a),
|
|
unique b_idx(b))`
|
|
tk.MustExec(schemaSQL)
|
|
c.Assert(encoder.ExecDDLSQL(schemaSQL), IsNil)
|
|
tableID = 2
|
|
|
|
sqls = []string{
|
|
"insert into t1(a, b) values('test', 'b1');",
|
|
"insert into t1(a, b) values('test', 'b2'), ('1', 'b3'), ('2', 'b4'), ('3', 'b5');",
|
|
"insert into t1(id, a, b) values(1000000, 'test', 'b6')",
|
|
"insert into t1(id, a, b) values(5, 'test', 'b7')",
|
|
"insert into t1(id, a, b) values(4, 'test', 'b8')",
|
|
"insert into t1(a, b) values('test', 'b9');",
|
|
}
|
|
|
|
for _, sql := range sqls {
|
|
baseID := alloc.Base()
|
|
kvPairs, _, err1 := encoder.Encode(sql, tableID)
|
|
c.Assert(err1, IsNil, Commentf("sql:%s", sql))
|
|
alloc.Reset(baseID)
|
|
retryKvPairs, _, err1 := encoder.Encode(sql, tableID)
|
|
c.Assert(err1, IsNil, Commentf("sql:%s", sql))
|
|
c.Assert(len(kvPairs), Equals, len(retryKvPairs))
|
|
for i, row := range kvPairs {
|
|
c.Assert(bytes.Compare(row.Key, retryKvPairs[i].Key), Equals, 0, Commentf(sql))
|
|
c.Assert(bytes.Compare(row.Val, retryKvPairs[i].Val), Equals, 0, Commentf(sql))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestAllocatorRebase(c *C) {
|
|
tk := testkit.NewTestKit(c, s.store)
|
|
tk.MustExec("use test")
|
|
alloc := NewAllocator()
|
|
var tableID int64 = 1
|
|
encoder, err := New("test", alloc)
|
|
c.Assert(err, IsNil)
|
|
err = alloc.Rebase(tableID, 100, false)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
c.Assert(alloc.Base(), Equals, int64(100))
|
|
|
|
schemaSQL := `create table t(
|
|
id int auto_increment,
|
|
a char(10),
|
|
primary key(id))`
|
|
tk.MustExec(schemaSQL)
|
|
c.Assert(encoder.ExecDDLSQL(schemaSQL), IsNil)
|
|
|
|
sql := "insert into t(id, a) values(1000, 'test')"
|
|
encoder.Encode(sql, tableID)
|
|
c.Assert(alloc.Base(), Equals, int64(1000))
|
|
|
|
sql = "insert into t(a) values('test')"
|
|
encoder.Encode(sql, tableID)
|
|
c.Assert(alloc.Base(), Equals, int64(1001))
|
|
|
|
sql = "insert into t(id, a) values(2000, 'test')"
|
|
encoder.Encode(sql, tableID)
|
|
c.Assert(alloc.Base(), Equals, int64(2000))
|
|
c.Assert(alloc.End(), Equals, int64(2000)+step)
|
|
nextID, err := alloc.NextGlobalAutoID(tableID)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(nextID, Equals, int64(2000)+step+1)
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestError(c *C) {
|
|
tk := testkit.NewTestKit(c, s.store)
|
|
tk.MustExec("use test")
|
|
alloc := NewAllocator()
|
|
encoder, err := New("test", alloc)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
_, err = New("", alloc)
|
|
c.Assert(err, NotNil)
|
|
c.Assert(err, ErrorMatches, "*Incorrect database name ''")
|
|
|
|
err = encoder.ExecDDLSQL("x")
|
|
c.Assert(err, NotNil)
|
|
c.Assert(err, ErrorMatches, "*You have an error in your SQL syntax.*")
|
|
|
|
_, err = encoder.PrepareStmt("")
|
|
c.Assert(err, NotNil)
|
|
c.Assert(err, ErrorMatches, "*Can not prepare multiple statements")
|
|
_, _, err = encoder.EncodePrepareStmt(0, 0, 0)
|
|
c.Assert(err, NotNil)
|
|
c.Assert(err, ErrorMatches, "*Prepared statement not found")
|
|
|
|
encoder = &kvEncoder{}
|
|
err = encoder.SetSystemVariable("", "")
|
|
c.Assert(err, NotNil)
|
|
c.Assert(err, ErrorMatches, ".*please new KvEncoder by kvencoder.New.*")
|
|
_, ok := encoder.GetSystemVariable("")
|
|
c.Assert(ok, IsFalse)
|
|
c.Assert(err, ErrorMatches, ".*please new KvEncoder by kvencoder.New.*")
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestAllocatorRebaseSmaller(c *C) {
|
|
alloc := NewAllocator()
|
|
alloc.Rebase(1, 10, false)
|
|
c.Assert(alloc.Base(), Equals, int64(10))
|
|
alloc.Rebase(1, 100, false)
|
|
c.Assert(alloc.Base(), Equals, int64(100))
|
|
alloc.Rebase(1, 1, false)
|
|
c.Assert(alloc.Base(), Equals, int64(100))
|
|
alloc.Reset(1)
|
|
c.Assert(alloc.Base(), Equals, int64(1))
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestSimpleKeyEncode(c *C) {
|
|
encoder, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
schemaSQL := `create table t(
|
|
id int auto_increment,
|
|
a char(10),
|
|
b char(10) default NULL,
|
|
primary key(id),
|
|
key a_idx(a))
|
|
`
|
|
encoder.ExecDDLSQL(schemaSQL)
|
|
tableID := int64(1)
|
|
indexID := int64(1)
|
|
|
|
sql := "insert into t values(1, 'a', 'b')"
|
|
kvPairs, affectedRows, err := encoder.Encode(sql, tableID)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(len(kvPairs), Equals, 2)
|
|
c.Assert(affectedRows, Equals, uint64(1))
|
|
tablePrefix := tablecodec.GenTableRecordPrefix(tableID)
|
|
handle := int64(1)
|
|
expectRecordKey := tablecodec.EncodeRecordKey(tablePrefix, handle)
|
|
|
|
sc := &stmtctx.StatementContext{TimeZone: time.Local}
|
|
indexPrefix := tablecodec.EncodeTableIndexPrefix(tableID, indexID)
|
|
expectIdxKey := make([]byte, 0)
|
|
expectIdxKey = append(expectIdxKey, []byte(indexPrefix)...)
|
|
expectIdxKey, err = codec.EncodeKey(sc, expectIdxKey, types.NewDatum([]byte("a")))
|
|
c.Assert(err, IsNil)
|
|
expectIdxKey, err = codec.EncodeKey(sc, expectIdxKey, types.NewDatum(handle))
|
|
c.Assert(err, IsNil)
|
|
|
|
for _, row := range kvPairs {
|
|
tID, iID, isRecordKey, err1 := tablecodec.DecodeKeyHead(row.Key)
|
|
c.Assert(err1, IsNil)
|
|
c.Assert(tID, Equals, tableID)
|
|
if isRecordKey {
|
|
c.Assert(bytes.Compare(row.Key, expectRecordKey), Equals, 0)
|
|
} else {
|
|
c.Assert(iID, Equals, indexID)
|
|
c.Assert(bytes.Compare(row.Key, expectIdxKey), Equals, 0)
|
|
}
|
|
}
|
|
|
|
// unique index key
|
|
schemaSQL = `create table t1(
|
|
id int auto_increment,
|
|
a char(10),
|
|
primary key(id),
|
|
unique a_idx(a))
|
|
`
|
|
encoder.ExecDDLSQL(schemaSQL)
|
|
tableID = int64(2)
|
|
sql = "insert into t1 values(1, 'a')"
|
|
kvPairs, affectedRows, err = encoder.Encode(sql, tableID)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(len(kvPairs), Equals, 2)
|
|
c.Assert(affectedRows, Equals, uint64(1))
|
|
|
|
tablePrefix = tablecodec.GenTableRecordPrefix(tableID)
|
|
handle = int64(1)
|
|
expectRecordKey = tablecodec.EncodeRecordKey(tablePrefix, handle)
|
|
|
|
indexPrefix = tablecodec.EncodeTableIndexPrefix(tableID, indexID)
|
|
expectIdxKey = []byte{}
|
|
expectIdxKey = append(expectIdxKey, []byte(indexPrefix)...)
|
|
expectIdxKey, err = codec.EncodeKey(sc, expectIdxKey, types.NewDatum([]byte("a")))
|
|
c.Assert(err, IsNil)
|
|
|
|
for _, row := range kvPairs {
|
|
tID, iID, isRecordKey, err1 := tablecodec.DecodeKeyHead(row.Key)
|
|
c.Assert(err1, IsNil)
|
|
c.Assert(tID, Equals, tableID)
|
|
if isRecordKey {
|
|
c.Assert(bytes.Compare(row.Key, expectRecordKey), Equals, 0)
|
|
} else {
|
|
c.Assert(iID, Equals, indexID)
|
|
c.Assert(bytes.Compare(row.Key, expectIdxKey), Equals, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
var (
|
|
mMetaPrefix = []byte("m")
|
|
mDBPrefix = "DB"
|
|
mTableIDPrefix = "TID"
|
|
)
|
|
|
|
func dbKey(dbID int64) []byte {
|
|
return []byte(fmt.Sprintf("%s:%d", mDBPrefix, dbID))
|
|
}
|
|
|
|
func autoTableIDKey(tableID int64) []byte {
|
|
return []byte(fmt.Sprintf("%s:%d", mTableIDPrefix, tableID))
|
|
}
|
|
|
|
func encodeHashDataKey(key []byte, field []byte) kv.Key {
|
|
ek := make([]byte, 0, len(mMetaPrefix)+len(key)+len(field)+30)
|
|
ek = append(ek, mMetaPrefix...)
|
|
ek = codec.EncodeBytes(ek, key)
|
|
ek = codec.EncodeUint(ek, uint64(structure.HashData))
|
|
return codec.EncodeBytes(ek, field)
|
|
}
|
|
|
|
func hashFieldIntegerVal(val int64) []byte {
|
|
return []byte(strconv.FormatInt(val, 10))
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestEncodeMetaAutoID(c *C) {
|
|
encoder, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
dbID := int64(1)
|
|
tableID := int64(10)
|
|
autoID := int64(10000000111)
|
|
kvPair, err := encoder.EncodeMetaAutoID(dbID, tableID, autoID)
|
|
c.Assert(err, IsNil)
|
|
|
|
expectKey := encodeHashDataKey(dbKey(dbID), autoTableIDKey(tableID))
|
|
expectVal := hashFieldIntegerVal(autoID)
|
|
|
|
c.Assert(bytes.Compare(kvPair.Key, expectKey), Equals, 0)
|
|
c.Assert(bytes.Compare(kvPair.Val, expectVal), Equals, 0)
|
|
|
|
dbID = 10
|
|
tableID = 1
|
|
autoID = -1
|
|
kvPair, err = encoder.EncodeMetaAutoID(dbID, tableID, autoID)
|
|
c.Assert(err, IsNil)
|
|
|
|
expectKey = encodeHashDataKey(dbKey(dbID), autoTableIDKey(tableID))
|
|
expectVal = hashFieldIntegerVal(autoID)
|
|
|
|
c.Assert(bytes.Compare(kvPair.Key, expectKey), Equals, 0)
|
|
c.Assert(bytes.Compare(kvPair.Val, expectVal), Equals, 0)
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestGetSetSystemVariable(c *C) {
|
|
encoder, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
err = encoder.SetSystemVariable("sql_mode", "")
|
|
c.Assert(err, IsNil)
|
|
|
|
val, ok := encoder.GetSystemVariable("sql_mode")
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(val, Equals, "")
|
|
|
|
sqlMode := "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
|
|
err = encoder.SetSystemVariable("sql_mode", sqlMode)
|
|
c.Assert(err, IsNil)
|
|
|
|
val, ok = encoder.GetSystemVariable("sql_mode")
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(val, Equals, sqlMode)
|
|
|
|
val, ok = encoder.GetSystemVariable("SQL_MODE")
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(val, Equals, sqlMode)
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestDisableStrictSQLMode(c *C) {
|
|
sql := "create table `ORDER-LINE` (" +
|
|
" ol_w_id integer not null," +
|
|
" ol_d_id integer not null," +
|
|
" ol_o_id integer not null," +
|
|
" ol_number integer not null," +
|
|
" ol_i_id integer not null," +
|
|
" ol_delivery_d timestamp DEFAULT CURRENT_TIMESTAMP," +
|
|
" ol_amount decimal(6,2)," +
|
|
" ol_supply_w_id integer," +
|
|
" ol_quantity decimal(2,0)," +
|
|
" ol_dist_info char(24)," +
|
|
" primary key (ol_w_id, ol_d_id, ol_o_id, ol_number)" +
|
|
");"
|
|
|
|
encoder, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
defer encoder.Close()
|
|
|
|
err = encoder.ExecDDLSQL(sql)
|
|
c.Assert(err, IsNil)
|
|
tableID := int64(1)
|
|
sql = "insert into `ORDER-LINE` values(2, 1, 1, 1, 1, 'NULL', 1, 1, 1, '1');"
|
|
_, _, err = encoder.Encode(sql, tableID)
|
|
c.Assert(err, NotNil)
|
|
|
|
err = encoder.SetSystemVariable("sql_mode", "")
|
|
c.Assert(err, IsNil)
|
|
|
|
sql = "insert into `ORDER-LINE` values(2, 1, 1, 1, 1, 'NULL', 1, 1, 1, '1');"
|
|
_, _, err = encoder.Encode(sql, tableID)
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *testKvEncoderSuite) TestRefCount(c *C) {
|
|
var err error
|
|
var a [10]KvEncoder
|
|
for i := 0; i < 10; i++ {
|
|
a[i], err = New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
}
|
|
dom1 := domGlobal
|
|
c.Assert(refCount, Equals, int64(10))
|
|
a[0].Close()
|
|
a[1].Close()
|
|
dom2 := domGlobal
|
|
c.Assert(refCount, Equals, int64(8))
|
|
c.Assert(dom1, Equals, dom2)
|
|
|
|
for i := 2; i < 9; i++ {
|
|
a[i].Close()
|
|
}
|
|
dom3 := domGlobal
|
|
c.Assert(refCount, Equals, int64(1))
|
|
c.Assert(dom3, Equals, dom2)
|
|
|
|
a[9].Close()
|
|
c.Assert(refCount, Equals, int64(0))
|
|
|
|
tmp, err := New("test", nil)
|
|
c.Assert(err, IsNil)
|
|
dom4 := domGlobal
|
|
c.Assert(dom4 == dom3, IsFalse)
|
|
c.Assert(refCount, Equals, int64(1))
|
|
tmp.Close()
|
|
c.Assert(refCount, Equals, int64(0))
|
|
}
|
|
|
|
// TestExoticDatabaseName checks if https://github.com/pingcap/tidb/issues/9532
|
|
// is fixed.
|
|
func (s *testKvEncoderSuite) TestExoticDatabaseName(c *C) {
|
|
encoder1, err := New("pay-service_micro_db", nil)
|
|
c.Assert(err, IsNil)
|
|
encoder1.Close()
|
|
|
|
encoder2, err := New("㎩¥`𝕊ℯ®Ⅵ₠—🎤肉 ㏈", nil)
|
|
c.Assert(err, IsNil)
|
|
encoder2.Close()
|
|
}
|