diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2637686dc9dc1..3e23f56c60f38 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,7 +61,7 @@ TiDB is written in [Go](http://golang.org). If you don't have a Go development environment, please [set one up](http://golang.org/doc/code.html). -The version of GO should be **1.10** or above. +The version of GO should be **1.11** or above. After installation, you'll need `GOPATH` defined, and `PATH` modified to access your Go binaries. @@ -75,11 +75,7 @@ export PATH=$PATH:$GOPATH/bin #### Dependency management -TiDB uses [`dep`](https://github.com/golang/dep) to manage dependencies. - -```sh -go get -u github.com/golang/dep/cmd/dep -``` +TiDB uses [`Go Modules`](https://github.com/golang/go/wiki/Modules) to manage dependencies. ## Workflow diff --git a/Makefile b/Makefile index 5f05a45bb047e..abf14e3d33e21 100644 --- a/Makefile +++ b/Makefile @@ -101,6 +101,7 @@ vet: clean: $(GO) clean -i ./... rm -rf *.out + rm -rf parser test: checklist gotest explaintest @@ -144,13 +145,13 @@ tikv_integration_test: $(GOTEST) ./store/tikv/. -with-tikv=true || { $(GOFAIL_DISABLE); exit 1; } @$(GOFAIL_DISABLE) -RACE_FLAG = +RACE_FLAG = ifeq ("$(WITH_RACE)", "1") RACE_FLAG = -race GOBUILD = GOPATH=$(GOPATH) CGO_ENABLED=1 $(GO) build endif -CHECK_FLAG = +CHECK_FLAG = ifeq ("$(WITH_CHECK)", "1") CHECK_FLAG = $(TEST_LDFLAGS) endif diff --git a/cmd/explaintest/r/explain_easy.result b/cmd/explaintest/r/explain_easy.result index e4b1cefa8f265..e54a6bd6ed05f 100644 --- a/cmd/explaintest/r/explain_easy.result +++ b/cmd/explaintest/r/explain_easy.result @@ -159,18 +159,18 @@ TableReader_5 10000.00 root data:TableScan_4 explain select c1 from t2 union select c1 from t2 union all select c1 from t2; id count task operator info Union_17 26000.00 root -├─TableReader_20 10000.00 root data:TableScan_19 -│ └─TableScan_19 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo -└─HashAgg_24 16000.00 root group by:t2.c1, funcs:firstrow(join_agg_0) - └─Union_25 16000.00 root - ├─StreamAgg_38 8000.00 root group by:col_2, funcs:firstrow(col_0), firstrow(col_1) - │ └─IndexReader_39 8000.00 root index:StreamAgg_29 - │ └─StreamAgg_29 8000.00 cop group by:test.t2.c1, funcs:firstrow(test.t2.c1), firstrow(test.t2.c1) - │ └─IndexScan_37 10000.00 cop table:t2, index:c1, range:[NULL,+inf], keep order:true, stats:pseudo - └─StreamAgg_55 8000.00 root group by:col_2, funcs:firstrow(col_0), firstrow(col_1) - └─IndexReader_56 8000.00 root index:StreamAgg_46 - └─StreamAgg_46 8000.00 cop group by:test.t2.c1, funcs:firstrow(test.t2.c1), firstrow(test.t2.c1) - └─IndexScan_54 10000.00 cop table:t2, index:c1, range:[NULL,+inf], keep order:true, stats:pseudo +├─HashAgg_21 16000.00 root group by:t2.c1, funcs:firstrow(join_agg_0) +│ └─Union_22 16000.00 root +│ ├─StreamAgg_35 8000.00 root group by:col_2, funcs:firstrow(col_0), firstrow(col_1) +│ │ └─IndexReader_36 8000.00 root index:StreamAgg_26 +│ │ └─StreamAgg_26 8000.00 cop group by:test.t2.c1, funcs:firstrow(test.t2.c1), firstrow(test.t2.c1) +│ │ └─IndexScan_34 10000.00 cop table:t2, index:c1, range:[NULL,+inf], keep order:true, stats:pseudo +│ └─StreamAgg_52 8000.00 root group by:col_2, funcs:firstrow(col_0), firstrow(col_1) +│ └─IndexReader_53 8000.00 root index:StreamAgg_43 +│ └─StreamAgg_43 8000.00 cop group by:test.t2.c1, funcs:firstrow(test.t2.c1), firstrow(test.t2.c1) +│ └─IndexScan_51 10000.00 cop table:t2, index:c1, range:[NULL,+inf], keep order:true, stats:pseudo +└─TableReader_59 10000.00 root data:TableScan_58 + └─TableScan_58 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo explain select c1 from t2 union all select c1 from t2 union select c1 from t2; id count task operator info HashAgg_18 24000.00 root group by:t2.c1, funcs:firstrow(join_agg_0) @@ -393,6 +393,19 @@ Projection_5 8000.00 root test.ta.a └─TableReader_9 10000.00 root data:TableScan_8 └─TableScan_8 10000.00 cop table:ta, range:[-inf,+inf], keep order:false, stats:pseudo rollback; +drop table if exists t1, t2; +create table t1(a int, b int, c int, primary key(a, b)); +create table t2(a int, b int, c int, primary key(a)); +explain select t1.a, t1.b from t1 left outer join t2 on t1.a = t2.a; +id count task operator info +TableReader_7 10000.00 root data:TableScan_6 +└─TableScan_6 10000.00 cop table:t1, range:[-inf,+inf], keep order:false, stats:pseudo +explain select distinct t1.a, t1.b from t1 left outer join t2 on t1.a = t2.a; +id count task operator info +StreamAgg_19 8000.00 root group by:col_2, col_3, funcs:firstrow(col_0), firstrow(col_1) +└─IndexReader_20 8000.00 root index:StreamAgg_10 + └─StreamAgg_10 8000.00 cop group by:test.t1.a, test.t1.b, funcs:firstrow(test.t1.a), firstrow(test.t1.b) + └─IndexScan_18 10000.00 cop table:t1, index:a, b, range:[NULL,+inf], keep order:true, stats:pseudo drop table if exists t; create table t(a int, nb int not null, nc int not null); explain select ifnull(a, 0) from t; diff --git a/cmd/explaintest/t/explain_easy.test b/cmd/explaintest/t/explain_easy.test index f71291193664b..5ae5a1764c906 100644 --- a/cmd/explaintest/t/explain_easy.test +++ b/cmd/explaintest/t/explain_easy.test @@ -82,6 +82,13 @@ insert tb values ('1'); explain select * from ta where a = 1; rollback; +# outer join elimination +drop table if exists t1, t2; +create table t1(a int, b int, c int, primary key(a, b)); +create table t2(a int, b int, c int, primary key(a)); +explain select t1.a, t1.b from t1 left outer join t2 on t1.a = t2.a; +explain select distinct t1.a, t1.b from t1 left outer join t2 on t1.a = t2.a; + # https://github.com/pingcap/tidb/issues/7918 drop table if exists t; create table t(a int, nb int not null, nc int not null); diff --git a/config/config.go b/config/config.go index feb0a48914519..2cd801ceefac4 100644 --- a/config/config.go +++ b/config/config.go @@ -90,7 +90,7 @@ type Log struct { SlowQueryFile string `toml:"slow-query-file" json:"slow-query-file"` SlowThreshold uint64 `toml:"slow-threshold" json:"slow-threshold"` ExpensiveThreshold uint `toml:"expensive-threshold" json:"expensive-threshold"` - QueryLogMaxLen uint `toml:"query-log-max-len" json:"query-log-max-len"` + QueryLogMaxLen uint64 `toml:"query-log-max-len" json:"query-log-max-len"` } // Security is the security section of the config. @@ -277,7 +277,7 @@ var defaultConf = Config{ }, SlowThreshold: logutil.DefaultSlowThreshold, ExpensiveThreshold: 10000, - QueryLogMaxLen: 2048, + QueryLogMaxLen: logutil.DefaultQueryLogMaxLen, }, Status: Status{ ReportStatus: true, diff --git a/ddl/db_change_test.go b/ddl/db_change_test.go index 1e3454a62aa39..58d49cfd388fb 100644 --- a/ddl/db_change_test.go +++ b/ddl/db_change_test.go @@ -96,9 +96,9 @@ func (s *testStateChangeSuite) TestShowCreateTable(c *C) { got := result.Rows()[0][1] var expected string if job.Type == model.ActionAddIndex { - expected = "CREATE TABLE `t` (\n `id` int(11) DEFAULT NULL,\n KEY `idx` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected = "CREATE TABLE `t` (\n `id` int(11) DEFAULT NULL,\n KEY `idx` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" } else if job.Type == model.ActionAddColumn { - expected = "CREATE TABLE `t` (\n `id` int(11) DEFAULT NULL,\n KEY `idx` (`id`),\n KEY `idx1` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected = "CREATE TABLE `t` (\n `id` int(11) DEFAULT NULL,\n KEY `idx` (`id`),\n KEY `idx1` (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" } if got != expected { checkErr = errors.Errorf("got %s, expected %s", got, expected) diff --git a/ddl/db_test.go b/ddl/db_test.go index 604402f459480..5d441589d5e12 100644 --- a/ddl/db_test.go +++ b/ddl/db_test.go @@ -1322,7 +1322,7 @@ func (s *testDBSuite) TestAlterColumn(c *C) { c.Assert(err, NotNil) result := s.tk.MustQuery("show create table mc") createSQL := result.Rows()[0][1] - expected := "CREATE TABLE `mc` (\n `a` int(11) NOT NULL,\n `b` int(11) DEFAULT NULL,\n `c` int(11) DEFAULT NULL,\n PRIMARY KEY (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected := "CREATE TABLE `mc` (\n `a` int(11) NOT NULL,\n `b` int(11) DEFAULT NULL,\n `c` int(11) DEFAULT NULL,\n PRIMARY KEY (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" c.Assert(createSQL, Equals, expected) // Change / modify column should preserve index options. @@ -1333,7 +1333,7 @@ func (s *testDBSuite) TestAlterColumn(c *C) { s.mustExec(c, "alter table mc modify column c bigint") // Unique should be preserved result = s.tk.MustQuery("show create table mc") createSQL = result.Rows()[0][1] - expected = "CREATE TABLE `mc` (\n `a` bigint(20) NOT NULL,\n `b` bigint(20) DEFAULT NULL,\n `c` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`a`),\n UNIQUE KEY `c` (`c`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected = "CREATE TABLE `mc` (\n `a` bigint(20) NOT NULL,\n `b` bigint(20) DEFAULT NULL,\n `c` bigint(20) DEFAULT NULL,\n PRIMARY KEY (`a`),\n UNIQUE KEY `c` (`c`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" c.Assert(createSQL, Equals, expected) // Dropping or keeping auto_increment is allowed, however adding is not allowed. @@ -1342,11 +1342,11 @@ func (s *testDBSuite) TestAlterColumn(c *C) { s.mustExec(c, "alter table mc modify column a bigint auto_increment") // Keeps auto_increment result = s.tk.MustQuery("show create table mc") createSQL = result.Rows()[0][1] - expected = "CREATE TABLE `mc` (\n `a` bigint(20) NOT NULL AUTO_INCREMENT,\n `b` int(11) DEFAULT NULL,\n PRIMARY KEY (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected = "CREATE TABLE `mc` (\n `a` bigint(20) NOT NULL AUTO_INCREMENT,\n `b` int(11) DEFAULT NULL,\n PRIMARY KEY (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" s.mustExec(c, "alter table mc modify column a bigint") // Drops auto_increment result = s.tk.MustQuery("show create table mc") createSQL = result.Rows()[0][1] - expected = "CREATE TABLE `mc` (\n `a` bigint(20) NOT NULL,\n `b` int(11) DEFAULT NULL,\n PRIMARY KEY (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected = "CREATE TABLE `mc` (\n `a` bigint(20) NOT NULL,\n `b` int(11) DEFAULT NULL,\n PRIMARY KEY (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" c.Assert(createSQL, Equals, expected) _, err = s.tk.Exec("alter table mc modify column a bigint auto_increment") // Adds auto_increment should throw error c.Assert(err, NotNil) @@ -1762,7 +1762,7 @@ func (s *testDBSuite) TestCreateTableWithPartition(c *C) { partition p0 values less than (to_seconds('2004-01-01')), partition p1 values less than (to_seconds('2005-01-01')));`) s.tk.MustQuery("show create table t26").Check( - testkit.Rows("t26 CREATE TABLE `t26` (\n `a` date DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin\nPARTITION BY RANGE ( to_seconds(`a`) ) (\n PARTITION p0 VALUES LESS THAN (63240134400),\n PARTITION p1 VALUES LESS THAN (63271756800)\n)")) + testkit.Rows("t26 CREATE TABLE `t26` (\n `a` date DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\nPARTITION BY RANGE ( to_seconds(`a`) ) (\n PARTITION p0 VALUES LESS THAN (63240134400),\n PARTITION p1 VALUES LESS THAN (63271756800)\n)")) s.tk.MustExec(`create table t27 (a bigint unsigned not null) partition by range(a) ( partition p0 values less than (10), @@ -1892,7 +1892,7 @@ func (s *testDBSuite) TestTruncateTable(c *C) { hasOldTableData := true for i := 0; i < waitForCleanDataRound; i++ { err = kv.RunInNewTxn(s.store, false, func(txn kv.Transaction) error { - it, err1 := txn.Seek(tablePrefix) + it, err1 := txn.Iter(tablePrefix, nil) if err1 != nil { return err1 } @@ -2112,7 +2112,7 @@ func (s *testDBSuite) TestChangeColumnPosition(c *C) { " `c` int(11) DEFAULT NULL,", " `a` int(11) DEFAULT NULL,", " KEY `t` (`c`)", - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", } c.Assert(createSQL, Equals, strings.Join(exceptedSQL, "\n")) } @@ -2131,7 +2131,7 @@ func (s *testDBSuite) TestGeneratedColumnDDL(c *C) { // Check show create table with virtual generated column. result = s.tk.MustQuery(`show create table test_gv_ddl`) result.Check(testkit.Rows( - "test_gv_ddl CREATE TABLE `test_gv_ddl` (\n `a` int(11) DEFAULT NULL,\n `b` int(11) GENERATED ALWAYS AS (`a` + 8) VIRTUAL DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + "test_gv_ddl CREATE TABLE `test_gv_ddl` (\n `a` int(11) DEFAULT NULL,\n `b` int(11) GENERATED ALWAYS AS (`a` + 8) VIRTUAL DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", )) // Check alter table add a stored generated column. @@ -2145,7 +2145,7 @@ func (s *testDBSuite) TestGeneratedColumnDDL(c *C) { result.Check(testkit.Rows("table_with_gen_col_blanks CREATE TABLE `table_with_gen_col_blanks` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` char(20) GENERATED ALWAYS AS (CAST(`a` AS CHAR)) VIRTUAL DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin")) + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) genExprTests := []struct { stmt string @@ -2296,7 +2296,7 @@ func (s *testDBSuite) TestCheckColumnDefaultValue(c *C) { s.tk.MustQuery(`show create table text_default_text`).Check(testutil.RowsWithSep("|", "text_default_text CREATE TABLE `text_default_text` (\n"+ " `c1` text NOT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", )) ctx := s.tk.Se.(sessionctx.Context) is := domain.GetDomain(ctx).InfoSchema() @@ -2309,7 +2309,7 @@ func (s *testDBSuite) TestCheckColumnDefaultValue(c *C) { s.tk.MustQuery(`show create table text_default_blob`).Check(testutil.RowsWithSep("|", "text_default_blob CREATE TABLE `text_default_blob` (\n"+ " `c1` blob NOT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", )) is = domain.GetDomain(ctx).InfoSchema() tblInfo, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("text_default_blob")) @@ -2321,7 +2321,7 @@ func (s *testDBSuite) TestCheckColumnDefaultValue(c *C) { s.tk.MustQuery(`show create table text_default_json`).Check(testutil.RowsWithSep("|", "text_default_json CREATE TABLE `text_default_json` (\n"+ " `c1` json NOT NULL DEFAULT 'null'\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", )) is = domain.GetDomain(ctx).InfoSchema() tblInfo, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("text_default_json")) @@ -2336,8 +2336,8 @@ func (s *testDBSuite) TestCharacterSetInColumns(c *C) { s.tk.MustExec("use varchar_test") s.tk.MustExec("drop table if exists t") s.tk.MustExec("create table t (c1 int, s1 varchar(10), s2 text)") - s.tk.MustQuery("select count(*) from information_schema.columns where table_schema = 'varchar_test' and character_set_name != 'utf8'").Check(testkit.Rows("0")) - s.tk.MustQuery("select count(*) from information_schema.columns where table_schema = 'varchar_test' and character_set_name = 'utf8'").Check(testkit.Rows("2")) + s.tk.MustQuery("select count(*) from information_schema.columns where table_schema = 'varchar_test' and character_set_name != 'utf8mb4'").Check(testkit.Rows("0")) + s.tk.MustQuery("select count(*) from information_schema.columns where table_schema = 'varchar_test' and character_set_name = 'utf8mb4'").Check(testkit.Rows("2")) s.tk.MustExec("drop table if exists t5") s.tk.MustExec("create table t5(id int) charset=UTF8;") @@ -2818,7 +2818,7 @@ func (s *testDBSuite) TestAlterTableDropPartition(c *C) { s.tk.MustExec("drop table if exists tr;") s.tk.MustExec(` create table tr( - id int, name varchar(50), + id int, name varchar(50), purchased date ) partition by range( year(purchased) ) ( @@ -2908,7 +2908,7 @@ func checkPartitionDelRangeDone(c *C, s *testDBSuite, partitionPrefix kv.Key) bo hasOldPartitionData := true for i := 0; i < waitForCleanDataRound; i++ { err := kv.RunInNewTxn(s.store, false, func(txn kv.Transaction) error { - it, err := txn.Seek(partitionPrefix) + it, err := txn.Iter(partitionPrefix, nil) if err != nil { return err } @@ -2958,7 +2958,7 @@ func (s *testDBSuite) TestTruncatePartitionAndDropTable(c *C) { // Test truncate table partition. s.tk.MustExec("drop table if exists t3;") s.tk.MustExec(`create table t3( - id int, name varchar(50), + id int, name varchar(50), purchased date ) partition by range( year(purchased) ) ( @@ -2996,7 +2996,7 @@ func (s *testDBSuite) TestTruncatePartitionAndDropTable(c *C) { // Test drop table partition. s.tk.MustExec("drop table if exists t4;") s.tk.MustExec(`create table t4( - id int, name varchar(50), + id int, name varchar(50), purchased date ) partition by range( year(purchased) ) ( diff --git a/ddl/ddl_api.go b/ddl/ddl_api.go index b210ae3ec4660..7064c9a29e1d6 100644 --- a/ddl/ddl_api.go +++ b/ddl/ddl_api.go @@ -65,7 +65,7 @@ func (d *ddl) CreateSchema(ctx sessionctx.Context, schema model.CIStr, charsetIn dbInfo.Charset = charsetInfo.Chs dbInfo.Collate = charsetInfo.Col } else { - dbInfo.Charset, dbInfo.Collate = getDefaultCharsetAndCollate() + dbInfo.Charset, dbInfo.Collate = charset.GetDefaultCharsetAndCollate() } job := &model.Job{ @@ -118,13 +118,6 @@ func checkTooLongIndex(index model.CIStr) error { return nil } -func getDefaultCharsetAndCollate() (string, string) { - // TODO: TableDefaultCharset-->DatabaseDefaultCharset-->SystemDefaultCharset. - // TODO: Change TableOption parser to parse collate. - // This is a tmp solution. - return "utf8", "utf8_bin" -} - func setColumnFlagWithConstraint(colMap map[string]*table.Column, v *ast.Constraint) { switch v.Tp { case ast.ConstraintPrimaryKey: @@ -204,7 +197,7 @@ func setCharsetCollationFlenDecimal(tp *types.FieldType) error { if len(tp.Charset) == 0 { switch tp.Tp { case mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeEnum, mysql.TypeSet: - tp.Charset, tp.Collate = getDefaultCharsetAndCollate() + tp.Charset, tp.Collate = charset.GetDefaultCharsetAndCollate() default: tp.Charset = charset.CharsetBin tp.Collate = charset.CharsetBin diff --git a/ddl/delete_range.go b/ddl/delete_range.go index afe30274ee33a..2eeb9b7321ae3 100644 --- a/ddl/delete_range.go +++ b/ddl/delete_range.go @@ -14,7 +14,6 @@ package ddl import ( - "bytes" "encoding/hex" "fmt" "math" @@ -154,7 +153,7 @@ func (dr *delRange) doTask(ctx sessionctx.Context, r util.DelRangeTask) error { finish := true dr.keys = dr.keys[:0] err := kv.RunInNewTxn(dr.store, false, func(txn kv.Transaction) error { - iter, err := txn.Seek(oldStartKey) + iter, err := txn.Iter(oldStartKey, r.EndKey) if err != nil { return errors.Trace(err) } @@ -164,10 +163,7 @@ func (dr *delRange) doTask(ctx sessionctx.Context, r util.DelRangeTask) error { if !iter.Valid() { break } - finish = bytes.Compare(iter.Key(), r.EndKey) >= 0 - if finish { - break - } + finish = false dr.keys = append(dr.keys, iter.Key().Clone()) newStartKey = iter.Key().Next() diff --git a/ddl/index.go b/ddl/index.go index b8454dff94e9e..a8fa89e39e36e 100644 --- a/ddl/index.go +++ b/ddl/index.go @@ -622,7 +622,7 @@ func (w *addIndexWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgInde // taskDone means that the added handle is out of taskRange.endHandle. taskDone := false oprStartTime := startTime - err := iterateSnapshotRows(w.sessCtx.GetStore(), w.priority, w.table, txn.StartTS(), taskRange.startHandle, + err := iterateSnapshotRows(w.sessCtx.GetStore(), w.priority, w.table, txn.StartTS(), taskRange.startHandle, taskRange.endHandle, taskRange.endIncluded, func(handle int64, recordKey kv.Key, rawRow []byte) (bool, error) { oprEndTime := time.Now() w.logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotRows in fetchRowColVals", 0) @@ -1207,7 +1207,7 @@ func allocateIndexID(tblInfo *model.TableInfo) int64 { // recordIterFunc is used for low-level record iteration. type recordIterFunc func(h int64, rowKey kv.Key, rawRecord []byte) (more bool, err error) -func iterateSnapshotRows(store kv.Storage, priority int, t table.Table, version uint64, seekHandle int64, fn recordIterFunc) error { +func iterateSnapshotRows(store kv.Storage, priority int, t table.Table, version uint64, startHandle int64, endHandle int64, endIncluded bool, fn recordIterFunc) error { ver := kv.Version{Ver: version} snap, err := store.GetSnapshot(ver) @@ -1215,8 +1215,22 @@ func iterateSnapshotRows(store kv.Storage, priority int, t table.Table, version if err != nil { return errors.Trace(err) } - firstKey := t.RecordKey(seekHandle) - it, err := snap.Seek(firstKey) + firstKey := t.RecordKey(startHandle) + + // Calculate the exclusive upper bound + var upperBound kv.Key + if endIncluded { + if endHandle == math.MaxInt64 { + upperBound = t.RecordKey(endHandle).PrefixNext() + } else { + // PrefixNext is time costing. Try to avoid it if possible. + upperBound = t.RecordKey(endHandle + 1) + } + } else { + upperBound = t.RecordKey(endHandle) + } + + it, err := snap.Iter(firstKey, upperBound) if err != nil { return errors.Trace(err) } diff --git a/ddl/reorg.go b/ddl/reorg.go index a793e0c402c71..d69bb257277b4 100644 --- a/ddl/reorg.go +++ b/ddl/reorg.go @@ -304,7 +304,7 @@ func getTableRange(d *ddlCtx, tbl table.PhysicalTable, snapshotVer uint64, prior startHandle = math.MinInt64 endHandle = math.MaxInt64 // Get the start handle of this partition. - err = iterateSnapshotRows(d.store, priority, tbl, snapshotVer, math.MinInt64, + err = iterateSnapshotRows(d.store, priority, tbl, snapshotVer, math.MinInt64, math.MaxInt64, true, func(h int64, rowKey kv.Key, rawRecord []byte) (bool, error) { startHandle = h return false, nil diff --git a/docs/design/2018-10-24-view-support.md b/docs/design/2018-10-24-view-support.md new file mode 100644 index 0000000000000..4cf42208dc39f --- /dev/null +++ b/docs/design/2018-10-24-view-support.md @@ -0,0 +1,69 @@ +# Proposal: Implement View Feature + +- Author(s): AndrewDi +- Last updated: 2018-10-24 +- Discussion at: https://github.com/pingcap/tidb/issues/7974 + +## Abstract +This proposal proposes to implement VIEW feature in TiDB, aimed to make SQL easier to write. + +## Background +A database view is a searchable object in a database that is defined by a query. Though a view doesn’t store data, some refer to a VIEW as "virtual tables", and you can query a view like you can query a table. A view can combine data from two or more tables, using joins, and also just contain a subset of information. This makes them convenient to abstract, or hide, complicated queries. + +Below is a visual depiction of a view: + ![AnatomyOfAview](imgs/view.png) + +A view is created from a query using the "`CREATE OR REPLACE VIEW`" command. In the example below we are creating a PopularBooks view based on a query which selects all Books that have the IsPopular field checked. Following is the query: +`CREATE OR REPLACE VIEW PopularBooks AS SELECT ISBN, Title, Author,PublishDate FROM Books WHERE IsPopular=1` + +Once a view is created, you can use it as you query any table in a `SELECT` statement. For example, to list all the popular book titles ordered by an author, you could write: +`SELECT Author, Title FROM PopularBooks ORDER BY Author` + +In general you can use any of the SELECT clauses, such as GROUP BY, in a select statement containing a view. + +## Proposal +I implement the "`CREATE OR REPLACE`" command to create a view with the `SELECT` statement. Currently, I reuse "`DROP TABLE`" to drop a view, and "`SHOW FULL TABLES`" can also display the view list with different table types. The "`SHOW CREATE TABlE`" command can also regenerate a view create command. + +## Rationale +`VIEW` is just a tableinfo object store with a `SELECT` statement, and only supports two types of DDL operations, which are "`CREATE OR REPLACE VIEW`" and "`DROP`". Currently, view can only be used within the `SELECT` statement. +Let me describe more details about the view DDL operation: +1. Create a VIEW + 1.1 Parse the create view statement and build a logical plan for select cause part. If any grammar error occurs, return errors to parser. + 1.2 Examine view definer's privileges. Definer should own both `CREATE_VIEW` and base table's `SELECT` privileges. + 1.3 If `ViewFieldList` cause is empty, then we should generate view column name from `SelectStmt` cause. + 1.4 Replace any wildcard with column name in `SELECT` cause. + 1.5 Test `SELECT` cause with some rules to see if this view is updateable. + 1.6 Store the view definition into following tables + * `INFORMATION_SCHEMA.VIEW_COLUMN_USAGE` lists one row for each column in a view including the base table of the column where possible + * `INFORMATION_SCHEMA.VIEW_TABLE_USAGE` lists one row for each table used in a view + * `INFORMATION_SCHEMA.VIEWS` lists one row for each view + +2. Drop a view + Implement DROP VIEW grammar, and delete the existing view tableinfo object. +3. Select from a view + 3.1 In function `func (b *PlanBuilder) buildDataSource(tn *ast.TableName) (LogicalPlan, error)`, if `tn *ast.TableName` is a view, then we build a select logicalplan from view's view_select string. +4. Insert/Update into a view + I do not have any idea now, and will implement later. +5. Describe a view + Generate the view select logicalplan. If any error occurs, return the error to parser. Otherwise, return `SELECT` cause's column info. + +## Compatibility +It makes TiDB more compatible with MySQL. + +## Implementation +First, we need to introduce `TableType` for `TableInfo`. Currently, I define two kinds of tables, which are `BaseTable` and `View`. By default if we use the "`CREATE TABLE`" command to create a table, TiDB creates a BaseTable `TableInfo`; if we use the "`CREATE VIEW`" command to create a table, TiDB creates a View `TableInfo`. + +In order to store the VIEW's select text, we also need to add attribute `ViewSelectStmt string json:"view_select_stmt"` store select statement. + +Following is the main implementation details: +1. ast/ddl.go +I do check both the view name and the SELECT statement, viewname is a tablename object. +2. planner/core/planbuilder.go +The code executes SELECT and returns `expression.columns` within `plan.Schema()`. +3. ddl_api.go +We convert `expression.columns` into `table.Column` and reuse function `buildTableInfo` to build view +4. logical_plan_builder.go +Every time we make a `SELECT` statement within a view, modify the `buildDataSource` function to rewrite the view table to select `LogicalPlan`. + +## Open issues (if applicable) +https://github.com/pingcap/tidb/issues/7974 diff --git a/docs/design/README.md b/docs/design/README.md index 43839faff0d17..6da93dd750a36 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -1,12 +1,28 @@ # TiDB Design Documents -A proposal template: [TEMPLATE.md](./TEMPLATE.md) +## Why We Need Design Documents -## Proposed +The design document provides a high-level description of the architecture and important details of what we want to do. It is the most powerful tool to ensure that our work is correctly performed. + +Writing a design document can promote us to think deliberately and gather knowledge from others to get our job done better. An excellent design document is closely related to the success of our task. + +## Proprosal Process + +1. Before starting to write a design document, please [create a new issue](https://github.com/pingcap/tidb/issues/new/choose) for tracing the process of your design. +2. Create a new [Google Doc](https://docs.google.com/document/u/0/) to write the design document. Pelase refer to this [proposal template](./TEMPLATE.md). +3. If the design document is ready to be reviewed, please put the shared link with the `can comment` permission in the issue you've created, and one of our engineers will follow up this issue and keep it updated. +4. If needed, we may organize a communications seminar through [Google Hangouts](https://hangouts.google.com/) to discuss your design with you and the interested friends from the community. +5. When your design is finalized, please submit a pull request (PR) to add your new file under this directory, and put the link of your PR in the issue you've created. +6. Once your PR has been merged, please close the old issue. +7. Start the implementation referring to the proposal, and create a new issue to trace the process. + +## Proposal Status + +### Proposed - [Proposal: A new command to restore dropped table](./2018-08-10-restore-dropped-table.md) -## In Progress +### In Progress - [Proposal: A new storage row format for efficient decoding](./2018-07-19-row-format.md) - [Proposal: Enhance constraint propagation in TiDB logical plan](./2018-07-22-enhance-propagations.md) @@ -17,7 +33,7 @@ A proposal template: [TEMPLATE.md](./TEMPLATE.md) - [Proposal: Join Reorder Design v1](./2018-10-20-join-reorder-dp-v1.md) - [Proposal: Support Window Functions](./2018-10-31-window-functions.md) -## Completed +### Completed - [Proposal: A new aggregate function execution framework](./2018-07-01-refactor-aggregate-framework.md) - [Proposal: Infer the System Timezone of a TiDB cluster via TZ environment variable](./2018-09-10-adding-tz-env.md) diff --git a/docs/design/imgs/view.png b/docs/design/imgs/view.png new file mode 100644 index 0000000000000..ebd37cdea159b Binary files /dev/null and b/docs/design/imgs/view.png differ diff --git a/executor/adapter.go b/executor/adapter.go index 73685ba37129c..abf3161449dee 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -344,8 +344,8 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool) { return } sql := a.Text - if len(sql) > int(cfg.Log.QueryLogMaxLen) { - sql = fmt.Sprintf("%.*q(len:%d)", cfg.Log.QueryLogMaxLen, sql, len(a.Text)) + if maxQueryLen := atomic.LoadUint64(&cfg.Log.QueryLogMaxLen); uint64(len(sql)) > maxQueryLen { + sql = fmt.Sprintf("%.*q(len:%d)", maxQueryLen, sql, len(a.Text)) } sessVars := a.Ctx.GetSessionVars() sql = QueryReplacer.Replace(sql) + sessVars.GetExecuteArgumentsInfo() diff --git a/executor/admin_test.go b/executor/admin_test.go index 40220e8aee3cc..b60ef73e30184 100644 --- a/executor/admin_test.go +++ b/executor/admin_test.go @@ -19,6 +19,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/executor" + "github.com/pingcap/tidb/meta/autoid" "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/mock" @@ -514,3 +515,45 @@ func (s *testSuite) TestAdminCheckPrimaryIndex(c *C) { tk.MustExec("insert into t values(1, 1, 1), (9223372036854775807, 2, 2);") tk.MustExec("admin check index t idx;") } + +func (s *testSuite) TestAdminShowNextID(c *C) { + step := int64(10) + autoIDStep := autoid.GetStep() + autoid.SetStep(step) + defer autoid.SetStep(autoIDStep) + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("create table t(id int, c int)") + // Start handle is 1. + r := tk.MustQuery("admin show t next_row_id") + r.Check(testkit.Rows("test t _tidb_rowid 1")) + // Row ID is step + 1. + tk.MustExec("insert into t values(1, 1)") + r = tk.MustQuery("admin show t next_row_id") + r.Check(testkit.Rows("test t _tidb_rowid 11")) + // Row ID is original + step. + for i := 0; i < int(step); i++ { + tk.MustExec("insert into t values(10000, 1)") + } + r = tk.MustQuery("admin show t next_row_id") + r.Check(testkit.Rows("test t _tidb_rowid 21")) + + // test for a table with the primary key + tk.MustExec("create table tt(id int primary key auto_increment, c int)") + // Start handle is 1. + r = tk.MustQuery("admin show tt next_row_id") + r.Check(testkit.Rows("test tt id 1")) + // After rebasing auto ID, row ID is 20 + step + 1. + tk.MustExec("insert into tt values(20, 1)") + r = tk.MustQuery("admin show tt next_row_id") + r.Check(testkit.Rows("test tt id 31")) + // test for renaming the table + tk.MustExec("create database test1") + tk.MustExec("rename table test.tt to test1.tt") + tk.MustExec("use test1") + r = tk.MustQuery("admin show tt next_row_id") + r.Check(testkit.Rows("test1 tt id 31")) + tk.MustExec("insert test1.tt values ()") + r = tk.MustQuery("admin show tt next_row_id") + r.Check(testkit.Rows("test1 tt id 41")) +} diff --git a/executor/aggregate.go b/executor/aggregate.go index 4063d9263a8e1..bc59fd801dbfa 100644 --- a/executor/aggregate.go +++ b/executor/aggregate.go @@ -24,9 +24,11 @@ import ( "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/chunk" "github.com/pingcap/tidb/util/codec" "github.com/pingcap/tidb/util/set" + log "github.com/sirupsen/logrus" "github.com/spaolacci/murmur3" "golang.org/x/net/context" ) @@ -314,9 +316,18 @@ func (w *HashAggPartialWorker) getChildInput() bool { return true } +func recoveryHashAgg(output chan *AfFinalResult, r interface{}) { + output <- &AfFinalResult{err: errors.Errorf("%v", r)} + buf := util.GetStack() + log.Errorf("panic in the recoverable goroutine: %v, stack trace:\n%s", r, buf) +} + func (w *HashAggPartialWorker) run(ctx sessionctx.Context, waitGroup *sync.WaitGroup, finalConcurrency int) { needShuffle, sc := false, ctx.GetSessionVars().StmtCtx defer func() { + if r := recover(); r != nil { + recoveryHashAgg(w.globalOutputCh, r) + } if needShuffle { w.shuffleIntermData(sc, finalConcurrency) } @@ -492,6 +503,9 @@ func (w *HashAggFinalWorker) receiveFinalResultHolder() (*chunk.Chunk, bool) { func (w *HashAggFinalWorker) run(ctx sessionctx.Context, waitGroup *sync.WaitGroup) { defer func() { + if r := recover(); r != nil { + recoveryHashAgg(w.outputCh, r) + } waitGroup.Done() }() if err := w.consumeIntermData(ctx); err != nil { @@ -521,6 +535,9 @@ func (e *HashAggExec) fetchChildData(ctx context.Context) { err error ) defer func() { + if r := recover(); r != nil { + recoveryHashAgg(e.finalOutputCh, r) + } for i := range e.partialInputChs { close(e.partialInputChs[i]) } diff --git a/executor/aggregate_test.go b/executor/aggregate_test.go index c0c7d7ffb4a6f..fe453a659c3e9 100644 --- a/executor/aggregate_test.go +++ b/executor/aggregate_test.go @@ -240,7 +240,7 @@ func (s *testSuite) TestAggregation(c *C) { result = tk.MustQuery("select count(*) from information_schema.columns") // When adding new memory columns in information_schema, please update this variable. - columnCountOfAllInformationSchemaTables := "757" + columnCountOfAllInformationSchemaTables := "759" result.Check(testkit.Rows(columnCountOfAllInformationSchemaTables)) tk.MustExec("drop table if exists t1") diff --git a/executor/builder.go b/executor/builder.go index b74806a3ae419..ba530a4993f33 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -112,6 +112,8 @@ func (b *executorBuilder) build(p plannercore.Plan) Executor { return b.buildSelectLock(v) case *plannercore.CancelDDLJobs: return b.buildCancelDDLJobs(v) + case *plannercore.ShowNextRowID: + return b.buildShowNextRowID(v) case *plannercore.ShowDDL: return b.buildShowDDL(v) case *plannercore.ShowDDLJobs: @@ -185,6 +187,14 @@ func (b *executorBuilder) buildCancelDDLJobs(v *plannercore.CancelDDLJobs) Execu return e } +func (b *executorBuilder) buildShowNextRowID(v *plannercore.ShowNextRowID) Executor { + e := &ShowNextRowIDExec{ + baseExecutor: newBaseExecutor(b.ctx, v.Schema(), v.ExplainID()), + tblName: v.TableName, + } + return e +} + func (b *executorBuilder) buildShowDDL(v *plannercore.ShowDDL) Executor { // We get DDLInfo here because for Executors that returns result set, // next will be called after transaction has been committed. diff --git a/executor/ddl_test.go b/executor/ddl_test.go index 9c9a6e42be2e6..627fc758dc863 100644 --- a/executor/ddl_test.go +++ b/executor/ddl_test.go @@ -231,7 +231,7 @@ func (s *testSuite) TestAlterTableModifyColumn(c *C) { tk.MustExec("alter table mc modify column c2 text") result := tk.MustQuery("show create table mc") createSQL := result.Rows()[0][1] - expected := "CREATE TABLE `mc` (\n `c1` bigint(20) DEFAULT NULL,\n `c2` text DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + expected := "CREATE TABLE `mc` (\n `c1` bigint(20) DEFAULT NULL,\n `c2` text DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" c.Assert(createSQL, Equals, expected) } diff --git a/executor/executor.go b/executor/executor.go index b03af3671ddea..b1d3dc8793986 100644 --- a/executor/executor.go +++ b/executor/executor.go @@ -55,6 +55,7 @@ var ( _ Executor = &ProjectionExec{} _ Executor = &SelectionExec{} _ Executor = &SelectLockExec{} + _ Executor = &ShowNextRowIDExec{} _ Executor = &ShowDDLExec{} _ Executor = &ShowDDLJobsExec{} _ Executor = &ShowDDLJobQueriesExec{} @@ -200,6 +201,43 @@ func (e *CancelDDLJobsExec) Next(ctx context.Context, chk *chunk.Chunk) error { return nil } +// ShowNextRowIDExec represents a show the next row ID executor. +type ShowNextRowIDExec struct { + baseExecutor + tblName *ast.TableName + done bool +} + +// Next implements the Executor Next interface. +func (e *ShowNextRowIDExec) Next(ctx context.Context, chk *chunk.Chunk) error { + chk.Reset() + if e.done { + return nil + } + is := domain.GetDomain(e.ctx).InfoSchema() + tbl, err := is.TableByName(e.tblName.Schema, e.tblName.Name) + if err != nil { + return errors.Trace(err) + } + colName := model.ExtraHandleName + for _, col := range tbl.Meta().Columns { + if mysql.HasAutoIncrementFlag(col.Flag) { + colName = col.Name + break + } + } + nextGlobalID, err := tbl.Allocator(e.ctx).NextGlobalAutoID(tbl.Meta().ID) + if err != nil { + return errors.Trace(err) + } + chk.AppendString(0, e.tblName.Schema.O) + chk.AppendString(1, e.tblName.Name.O) + chk.AppendString(2, colName.O) + chk.AppendInt64(3, nextGlobalID) + e.done = true + return nil +} + // ShowDDLExec represents a show DDL executor. type ShowDDLExec struct { baseExecutor diff --git a/executor/executor_test.go b/executor/executor_test.go index e331631641c91..f40cf0060b026 100644 --- a/executor/executor_test.go +++ b/executor/executor_test.go @@ -1005,7 +1005,7 @@ func (s *testSuite) TestUnion(c *C) { tk.MustExec("CREATE TABLE t1 (a date)") tk.MustExec("CREATE TABLE t2 (a date)") tk.MustExec("SELECT a from t1 UNION select a FROM t2") - tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + " `a` date DEFAULT NULL\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin")) + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + " `a` date DEFAULT NULL\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) // Move from session test. tk.MustExec("drop table if exists t1, t2") @@ -1767,8 +1767,8 @@ func (s *testSuite) TestTableScan(c *C) { c.Assert(len(result.Rows()), GreaterEqual, 4) tk.MustExec("use test") tk.MustExec("create database mytest") - rowStr1 := fmt.Sprintf("%s %s %s %s %v", "def", "mysql", "utf8", "utf8_bin", nil) - rowStr2 := fmt.Sprintf("%s %s %s %s %v", "def", "mytest", "utf8", "utf8_bin", nil) + rowStr1 := fmt.Sprintf("%s %s %s %s %v", "def", "mysql", "utf8mb4", "utf8mb4_bin", nil) + rowStr2 := fmt.Sprintf("%s %s %s %s %v", "def", "mytest", "utf8mb4", "utf8mb4_bin", nil) tk.MustExec("use information_schema") result = tk.MustQuery("select * from schemata where schema_name = 'mysql'") result.Check(testkit.Rows(rowStr1)) diff --git a/executor/insert_common.go b/executor/insert_common.go index fc935a1030f64..d04adf4c9a343 100644 --- a/executor/insert_common.go +++ b/executor/insert_common.go @@ -112,6 +112,9 @@ func (e *InsertValues) initInsertColumns() error { } for _, col := range cols { if col.Name.L == model.ExtraHandleName.L { + if !e.ctx.GetSessionVars().AllowWriteRowID { + return errors.Errorf("insert, update and replace statements for _tidb_rowid are not supported.") + } e.hasExtraHandle = true break } diff --git a/executor/insert_test.go b/executor/insert_test.go index ef379bd4868c2..ffb23a9e12db8 100644 --- a/executor/insert_test.go +++ b/executor/insert_test.go @@ -28,24 +28,32 @@ func (s *testSuite) TestInsertOnDuplicateKey(c *C) { tk.MustExec(`create table t1(a1 bigint primary key, b1 bigint);`) tk.MustExec(`create table t2(a2 bigint primary key, b2 bigint);`) tk.MustExec(`insert into t1 values(1, 100);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t2 values(1, 200);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t1 select a2, b2 from t2 on duplicate key update b1 = a2;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t1;`).Check(testkit.Rows("1 1")) tk.MustExec(`insert into t1 select a2, b2 from t2 on duplicate key update b1 = b2;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t1;`).Check(testkit.Rows("1 200")) tk.MustExec(`insert into t1 select a2, b2 from t2 on duplicate key update a1 = a2;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(0)) tk.MustQuery(`select * from t1;`).Check(testkit.Rows("1 200")) tk.MustExec(`insert into t1 select a2, b2 from t2 on duplicate key update b1 = 300;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t1;`).Check(testkit.Rows("1 300")) tk.MustExec(`insert into t1 values(1, 1) on duplicate key update b1 = 400;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t1;`).Check(testkit.Rows("1 400")) tk.MustExec(`insert into t1 select 1, 500 from t2 on duplicate key update b1 = 400;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(0)) tk.MustQuery(`select * from t1;`).Check(testkit.Rows("1 400")) tk.MustExec(`drop table if exists t1, t2;`) @@ -76,42 +84,58 @@ func (s *testSuite) TestInsertOnDuplicateKey(c *C) { tk.MustExec(`create table t1(a1 bigint primary key, b1 bigint);`) tk.MustExec(`create table t2(a2 bigint primary key, b2 bigint);`) tk.MustExec(`insert into t1 values(1, 100);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t2 values(1, 200);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t1 select * from t2 on duplicate key update b1 = values(b1) + b2;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t1`).Check(testkit.Rows("1 400")) tk.MustExec(`insert into t1 select * from t2 on duplicate key update b1 = values(b1) + b2;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(0)) tk.MustQuery(`select * from t1`).Check(testkit.Rows("1 400")) tk.MustExec(`drop table if exists t;`) tk.MustExec(`create table t(k1 bigint, k2 bigint, val bigint, primary key(k1, k2));`) tk.MustExec(`insert into t (val, k1, k2) values (3, 1, 2);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustQuery(`select * from t;`).Check(testkit.Rows(`1 2 3`)) tk.MustExec(`insert into t (val, k1, k2) select c, a, b from (select 1 as a, 2 as b, 4 as c) tmp on duplicate key update val = tmp.c;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t;`).Check(testkit.Rows(`1 2 4`)) tk.MustExec(`drop table if exists t;`) tk.MustExec(`create table t(k1 double, k2 double, v double, primary key(k1, k2));`) tk.MustExec(`insert into t (v, k1, k2) select c, a, b from (select "3" c, "1" a, "2" b) tmp on duplicate key update v=c;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustQuery(`select * from t;`).Check(testkit.Rows(`1 2 3`)) tk.MustExec(`insert into t (v, k1, k2) select c, a, b from (select "3" c, "1" a, "2" b) tmp on duplicate key update v=c;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(0)) tk.MustQuery(`select * from t;`).Check(testkit.Rows(`1 2 3`)) tk.MustExec(`drop table if exists t1, t2;`) tk.MustExec(`create table t1(id int, a int, b int);`) tk.MustExec(`insert into t1 values (1, 1, 1);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t1 values (2, 2, 1);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t1 values (3, 3, 1);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`create table t2(a int primary key, b int, unique(b));`) tk.MustExec(`insert into t2 select a, b from t1 order by id on duplicate key update a=t1.a, b=t1.b;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(5)) tk.MustQuery(`select * from t2 order by a;`).Check(testkit.Rows(`3 1`)) tk.MustExec(`drop table if exists t1, t2;`) tk.MustExec(`create table t1(id int, a int, b int);`) tk.MustExec(`insert into t1 values (1, 1, 1);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t1 values (2, 1, 2);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`insert into t1 values (3, 3, 1);`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(1)) tk.MustExec(`create table t2(a int primary key, b int, unique(b));`) tk.MustExec(`insert into t2 select a, b from t1 order by id on duplicate key update a=t1.a, b=t1.b;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(4)) tk.MustQuery(`select * from t2 order by a;`).Check(testkit.Rows(`1 2`, `3 1`)) tk.MustExec(`drop table if exists t1, t2;`) @@ -122,6 +146,7 @@ func (s *testSuite) TestInsertOnDuplicateKey(c *C) { tk.MustExec(`insert into t1 values (4, 4, 2, 2);`) tk.MustExec(`create table t2(a int primary key, b int, c int, unique(b), unique(c));`) tk.MustExec(`insert into t2 select a, b, c from t1 order by id on duplicate key update b=t2.b, c=t2.c;`) + c.Assert(tk.Se.AffectedRows(), Equals, uint64(2)) tk.MustQuery(`select * from t2 order by a;`).Check(testkit.Rows(`1 1 1`, `3 2 2`)) } diff --git a/executor/projection.go b/executor/projection.go index 5474fcfb98d15..9dfaee07b10be 100644 --- a/executor/projection.go +++ b/executor/projection.go @@ -19,7 +19,9 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/util" "github.com/pingcap/tidb/util/chunk" + log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) @@ -264,7 +266,11 @@ type projectionInputFetcher struct { // a. There is no more input from child. // b. "ProjectionExec" close the "globalFinishCh" func (f *projectionInputFetcher) run(ctx context.Context) { + var output *projectionOutput defer func() { + if r := recover(); r != nil { + recoveryProjection(output, r) + } close(f.globalOutputCh) }() @@ -275,7 +281,7 @@ func (f *projectionInputFetcher) run(ctx context.Context) { } targetWorker := input.targetWorker - output := readProjectionOutput(f.outputCh, f.globalFinishCh) + output = readProjectionOutput(f.outputCh, f.globalFinishCh) if output == nil { return } @@ -317,13 +323,19 @@ type projectionWorker struct { // It is finished and exited once: // a. "ProjectionExec" closes the "globalFinishCh". func (w *projectionWorker) run(ctx context.Context) { + var output *projectionOutput + defer func() { + if r := recover(); r != nil { + recoveryProjection(output, r) + } + }() for { input := readProjectionInput(w.inputCh, w.globalFinishCh) if input == nil { return } - output := readProjectionOutput(w.outputCh, w.globalFinishCh) + output = readProjectionOutput(w.outputCh, w.globalFinishCh) if output == nil { return } @@ -339,6 +351,14 @@ func (w *projectionWorker) run(ctx context.Context) { } } +func recoveryProjection(output *projectionOutput, r interface{}) { + if output != nil { + output.done <- errors.Errorf("%v", r) + } + buf := util.GetStack() + log.Errorf("panic in the recoverable goroutine: %v, stack trace:\n%s", r, buf) +} + func readProjectionInput(inputCh <-chan *projectionInput, finishCh <-chan struct{}) *projectionInput { select { case <-finishCh: diff --git a/executor/rowid_test.go b/executor/rowid_test.go index 56a9ac155792f..0e7d8a3d101d8 100644 --- a/executor/rowid_test.go +++ b/executor/rowid_test.go @@ -19,8 +19,12 @@ import ( ) func (s *testSuite) TestExportRowID(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") + tk := testkit.NewTestKitWithInit(c, s.store) + tk.Se.GetSessionVars().AllowWriteRowID = true + defer func() { + tk.Se.GetSessionVars().AllowWriteRowID = false + }() + tk.MustExec("drop table if exists t") tk.MustExec("create table t (a int, b int)") tk.MustExec("insert t values (1, 7), (1, 8), (1, 9)") @@ -49,4 +53,36 @@ func (s *testSuite) TestExportRowID(c *C) { c.Assert(err, NotNil) _, err = tk.Exec("delete from s where _tidb_rowid = 1") c.Assert(err, NotNil) + + // Make sure "AllowWriteRowID" is a session variable. + tk1 := testkit.NewTestKit(c, s.store) + tk1.MustExec("use test") + _, err = tk1.Exec("insert into t (a, _tidb_rowid) values(10, 1);") + c.Assert(err.Error(), Equals, "insert, update and replace statements for _tidb_rowid are not supported.") +} + +func (s *testSuite) TestNotAllowWriteRowID(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("create table tt(id binary(10), c int, primary key(id));") + tk.MustExec("insert tt values (1, 10);") + // select statement + tk.MustQuery("select *, _tidb_rowid from tt"). + Check(testkit.Rows("1\x00\x00\x00\x00\x00\x00\x00\x00\x00 10 1")) + // insert statement + _, err := tk.Exec("insert into tt (id, c, _tidb_rowid) values(30000,10,1);") + c.Assert(err.Error(), Equals, "insert, update and replace statements for _tidb_rowid are not supported.") + // replace statement + _, err = tk.Exec("replace into tt (id, c, _tidb_rowid) values(30000,10,1);") + c.Assert(err.Error(), Equals, "insert, update and replace statements for _tidb_rowid are not supported.") + // update statement + _, err = tk.Exec("update tt set id = 2, _tidb_rowid = 1 where _tidb_rowid = 1") + c.Assert(err.Error(), Equals, "insert, update and replace statements for _tidb_rowid are not supported.") + tk.MustExec("update tt set id = 2 where _tidb_rowid = 1") + tk.MustExec("admin check table tt;") + tk.MustExec("drop table tt") + // There is currently no real support for inserting, updating, and replacing _tidb_rowid statements. + // After we support it, the following operations must be passed. + // tk.MustExec("insert into tt (id, c, _tidb_rowid) values(30000,10,1);") + // tk.MustExec("admin check table tt;") } diff --git a/executor/set_test.go b/executor/set_test.go index 3a564cc0b2ab7..92dd5ded418c5 100644 --- a/executor/set_test.go +++ b/executor/set_test.go @@ -246,6 +246,13 @@ func (s *testSuite) TestSetVar(c *C) { tk.MustQuery("select @@session.tidb_slow_log_threshold;").Check(testkit.Rows("1")) _, err = tk.Exec("set global tidb_slow_log_threshold = 0") c.Assert(err, NotNil) + + tk.MustExec("set tidb_query_log_max_len = 0") + tk.MustQuery("select @@session.tidb_query_log_max_len;").Check(testkit.Rows("0")) + tk.MustExec("set tidb_query_log_max_len = 20") + tk.MustQuery("select @@session.tidb_query_log_max_len;").Check(testkit.Rows("20")) + _, err = tk.Exec("set global tidb_query_log_max_len = 20") + c.Assert(err, NotNil) } func (s *testSuite) TestSetCharset(c *C) { diff --git a/executor/show.go b/executor/show.go index e7d4a64d65dde..e0679d0b82197 100644 --- a/executor/show.go +++ b/executor/show.go @@ -618,7 +618,7 @@ func (e *ShowExec) fetchShowCreateTable() error { buf.WriteString(") ENGINE=InnoDB") charsetName := tb.Meta().Charset if len(charsetName) == 0 { - charsetName = charset.CharsetUTF8 + charsetName = mysql.DefaultCharset } collate := tb.Meta().Collate // Set default collate if collate is not specified. diff --git a/executor/show_test.go b/executor/show_test.go index ce396ec8995c3..ee9ef7f9bb81d 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -73,7 +73,7 @@ func (s *testSuite) TestShow(c *C) { c.Check(result.Rows(), HasLen, 1) row = result.Rows()[0] expectedRow = []interface{}{ - "ptest", "CREATE TABLE `ptest` (\n `a` int(11) NOT NULL,\n `b` double NOT NULL DEFAULT '2.0',\n `c` varchar(10) NOT NULL,\n `d` time DEFAULT NULL,\n `e` timestamp NULL DEFAULT NULL,\n `f` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`a`),\n UNIQUE KEY `d` (`d`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"} + "ptest", "CREATE TABLE `ptest` (\n `a` int(11) NOT NULL,\n `b` double NOT NULL DEFAULT '2.0',\n `c` varchar(10) NOT NULL,\n `d` time DEFAULT NULL,\n `e` timestamp NULL DEFAULT NULL,\n `f` timestamp NULL DEFAULT NULL,\n PRIMARY KEY (`a`),\n UNIQUE KEY `d` (`d`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -99,7 +99,7 @@ func (s *testSuite) TestShow(c *C) { " `c3` mediumint(8) UNSIGNED DEFAULT NULL,\n" + " `c4` int(10) UNSIGNED DEFAULT NULL,\n" + " `c5` bigint(20) UNSIGNED DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"} + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -115,7 +115,7 @@ func (s *testSuite) TestShow(c *C) { expectedRow = []interface{}{ "decimalschema", "CREATE TABLE `decimalschema` (\n" + " `c1` decimal(11,0) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"} + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -130,7 +130,7 @@ func (s *testSuite) TestShow(c *C) { expectedRow = []interface{}{ "decimalschema", "CREATE TABLE `decimalschema` (\n" + " `c1` decimal(15,0) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"} + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -199,7 +199,7 @@ func (s *testSuite) TestShow(c *C) { tk.MustExec(testSQL) testSQL = "show create database show_test_DB;" tk.MustQuery(testSQL).Check(testutil.RowsWithSep("|", - "show_test_DB|CREATE DATABASE `show_test_DB` /* !40100 DEFAULT CHARACTER SET utf8 */", + "show_test_DB|CREATE DATABASE `show_test_DB` /* !40100 DEFAULT CHARACTER SET utf8mb4 */", )) tk.MustExec("use show_test_DB") @@ -223,7 +223,7 @@ func (s *testSuite) TestShow(c *C) { "show_auto_increment CREATE TABLE `show_auto_increment` (\n"+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ " PRIMARY KEY (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=4", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=4", )) // for issue https://github.com/pingcap/tidb/issues/4678 autoIDStep := autoid.GetStep() @@ -234,7 +234,7 @@ func (s *testSuite) TestShow(c *C) { "show_auto_increment CREATE TABLE `show_auto_increment` (\n"+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ " PRIMARY KEY (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT="+strconv.Itoa(int(autoID)), + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT="+strconv.Itoa(int(autoID)), )) tk.MustExec(`drop table show_auto_increment`) tk.MustExec(`create table show_auto_increment (id int primary key auto_increment)`) @@ -243,7 +243,7 @@ func (s *testSuite) TestShow(c *C) { "show_auto_increment CREATE TABLE `show_auto_increment` (\n"+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ " PRIMARY KEY (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", )) tk.MustExec("insert into show_auto_increment values(10)") autoID = autoIDStep + 11 @@ -252,7 +252,7 @@ func (s *testSuite) TestShow(c *C) { "show_auto_increment CREATE TABLE `show_auto_increment` (\n"+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ " PRIMARY KEY (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT="+strconv.Itoa(int(autoID)), + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT="+strconv.Itoa(int(autoID)), )) // Test show table with column's comment contain escape character @@ -263,7 +263,7 @@ func (s *testSuite) TestShow(c *C) { ""+ "show_escape_character CREATE TABLE `show_escape_character` (\n"+ " `id` int(11) DEFAULT NULL COMMENT 'a\\rb\\nc d\\0ef'\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", )) // for issue https://github.com/pingcap/tidb/issues/4424 @@ -277,7 +277,7 @@ func (s *testSuite) TestShow(c *C) { c.Check(result.Rows(), HasLen, 1) row = result.Rows()[0] expectedRow = []interface{}{ - "show_test", "CREATE TABLE `show_test` (\n `a` varchar(10) DEFAULT NULL COMMENT 'a\\nb\\rc d\\0e'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='a\\nb\\rc d\\0e'"} + "show_test", "CREATE TABLE `show_test` (\n `a` varchar(10) DEFAULT NULL COMMENT 'a\\nb\\rc d\\0e'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='a\\nb\\rc d\\0e'"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -293,7 +293,7 @@ func (s *testSuite) TestShow(c *C) { c.Check(result.Rows(), HasLen, 1) row = result.Rows()[0] expectedRow = []interface{}{ - "show_test", "CREATE TABLE `show_test` (\n `a` varchar(10) DEFAULT 'a\\nb\\rc d\\0e'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"} + "show_test", "CREATE TABLE `show_test` (\n `a` varchar(10) DEFAULT 'a\\nb\\rc d\\0e'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -312,7 +312,7 @@ func (s *testSuite) TestShow(c *C) { c.Check(result.Rows(), HasLen, 1) row = result.Rows()[0] expectedRow = []interface{}{ - "show_test", "CREATE TABLE `show_test` (\n `a` bit(1) DEFAULT NULL,\n `b` bit(32) DEFAULT b'0',\n `c` bit(1) DEFAULT b'1',\n `d` bit(10) DEFAULT b'1010'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"} + "show_test", "CREATE TABLE `show_test` (\n `a` bit(1) DEFAULT NULL,\n `b` bit(32) DEFAULT b'0',\n `c` bit(1) DEFAULT b'1',\n `d` bit(10) DEFAULT b'1010'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"} for i, r := range row { c.Check(r, Equals, expectedRow[i]) } @@ -340,7 +340,7 @@ func (s *testSuite) TestShow(c *C) { " `c` mediumint(9) DEFAULT NULL,\n" + " `d` int(11) DEFAULT NULL,\n" + " `e` bigint(20) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", } for i, r := range row { c.Check(r, Equals, expectedRow[i]) @@ -364,7 +364,7 @@ func (s *testSuite) TestShow(c *C) { tk.MustQuery("show create table t").Check(testutil.RowsWithSep("|", "t CREATE TABLE `t` (\n"+ " `a` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"+"\nPARTITION BY RANGE ( `a` ) (\n PARTITION p0 VALUES LESS THAN (10),\n PARTITION p1 VALUES LESS THAN (20),\n PARTITION p2 VALUES LESS THAN (MAXVALUE)\n)", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"+"\nPARTITION BY RANGE ( `a` ) (\n PARTITION p0 VALUES LESS THAN (10),\n PARTITION p1 VALUES LESS THAN (20),\n PARTITION p2 VALUES LESS THAN (MAXVALUE)\n)", )) tk.MustExec(`drop table if exists t`) @@ -387,7 +387,7 @@ func (s *testSuite) TestShow(c *C) { " `b` int(11) DEFAULT NULL,\n"+ " `c` char(1) DEFAULT NULL,\n"+ " `d` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin"+"\nPARTITION BY RANGE COLUMNS(a,d,c) (\n PARTITION p0 VALUES LESS THAN (5,10,\"ggg\"),\n PARTITION p1 VALUES LESS THAN (10,20,\"mmm\"),\n PARTITION p2 VALUES LESS THAN (15,30,\"sss\"),\n PARTITION p3 VALUES LESS THAN (50,MAXVALUE,MAXVALUE)\n)", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin"+"\nPARTITION BY RANGE COLUMNS(a,d,c) (\n PARTITION p0 VALUES LESS THAN (5,10,\"ggg\"),\n PARTITION p1 VALUES LESS THAN (10,20,\"mmm\"),\n PARTITION p2 VALUES LESS THAN (15,30,\"sss\"),\n PARTITION p3 VALUES LESS THAN (50,MAXVALUE,MAXVALUE)\n)", )) // Test show create table compression type. @@ -396,7 +396,7 @@ func (s *testSuite) TestShow(c *C) { tk.MustQuery("show create table t1").Check(testutil.RowsWithSep("|", "t1 CREATE TABLE `t1` (\n"+ " `c1` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMPRESSION='zlib'", + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMPRESSION='zlib'", )) // Test show create table year type @@ -407,7 +407,7 @@ func (s *testSuite) TestShow(c *C) { " `y` year NOT NULL,\n"+ " `x` int(11) DEFAULT NULL,\n"+ " PRIMARY KEY (`y`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin")) + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) // Test show create table with zerofill flag tk.MustExec(`drop table if exists t`) @@ -417,7 +417,7 @@ func (s *testSuite) TestShow(c *C) { " `id` int(11) NOT NULL,\n"+ " `val` tinyint(10) UNSIGNED ZEROFILL DEFAULT NULL,\n"+ " PRIMARY KEY (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin")) + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) // Test show columns with different types of default value tk.MustExec(`drop table if exists t`) diff --git a/executor/update.go b/executor/update.go index 0f1db7b120646..d589a05c00570 100644 --- a/executor/update.go +++ b/executor/update.go @@ -18,6 +18,7 @@ import ( "github.com/pingcap/parser/model" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" @@ -46,7 +47,7 @@ type UpdateExec struct { } func (e *UpdateExec) exec(schema *expression.Schema) ([]types.Datum, error) { - assignFlag, err := e.getUpdateColumns(schema.Len()) + assignFlag, err := e.getUpdateColumns(e.ctx, schema.Len()) if err != nil { return nil, errors.Trace(err) } @@ -230,9 +231,12 @@ func (e *UpdateExec) Open(ctx context.Context) error { return e.SelectExec.Open(ctx) } -func (e *UpdateExec) getUpdateColumns(schemaLen int) ([]bool, error) { +func (e *UpdateExec) getUpdateColumns(ctx sessionctx.Context, schemaLen int) ([]bool, error) { assignFlag := make([]bool, schemaLen) for _, v := range e.OrderedList { + if !ctx.GetSessionVars().AllowWriteRowID && v.Col.ColName.L == model.ExtraHandleName.L { + return nil, errors.Errorf("insert, update and replace statements for _tidb_rowid are not supported.") + } idx := v.Col.Index assignFlag[idx] = true } diff --git a/executor/write.go b/executor/write.go index 54d510e4d98f2..c3c0ba73fd91e 100644 --- a/executor/write.go +++ b/executor/write.go @@ -141,22 +141,22 @@ func updateRecord(ctx sessionctx.Context, h int64, oldData, newData []types.Datu if err = t.RemoveRecord(ctx, h, oldData); err != nil { return false, false, 0, errors.Trace(err) } + // the `affectedRows` is increased when adding new record. newHandle, err = t.AddRecord(ctx, newData, skipHandleCheck) if err != nil { return false, false, 0, errors.Trace(err) } + if onDup { + sc.AddAffectedRows(1) + } } else { // Update record to new value and update index. if err = t.UpdateRecord(ctx, h, oldData, newData, modified); err != nil { return false, false, 0, errors.Trace(err) } - } - - if onDup { - sc.AddAffectedRows(2) - } else { - // if handleChanged == true, the `affectedRows` is calculated when add new record. - if !handleChanged { + if onDup { + sc.AddAffectedRows(2) + } else { sc.AddAffectedRows(1) } } diff --git a/expression/aggregation/agg_to_pb_test.go b/expression/aggregation/agg_to_pb_test.go index af8ca6d26f8a2..7d7c948807796 100644 --- a/expression/aggregation/agg_to_pb_test.go +++ b/expression/aggregation/agg_to_pb_test.go @@ -88,13 +88,13 @@ func (s *testEvaluatorSuite) TestAggFunc2Pb(c *C) { } jsons := []string{ - `{"tp":3002,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}`, - `{"tp":3001,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}],"sig":0,"field_type":{"tp":8,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}`, - `{"tp":3003,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}`, + `{"tp":3002,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}`, + `{"tp":3001,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}],"sig":0,"field_type":{"tp":8,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}`, + `{"tp":3003,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}`, "null", - `{"tp":3005,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}`, - `{"tp":3004,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}`, - `{"tp":3006,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":83,"charset":""}}`, + `{"tp":3005,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}`, + `{"tp":3004,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}`, + `{"tp":3006,"children":[{"tp":201,"val":"gAAAAAAAAAE=","sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}],"sig":0,"field_type":{"tp":5,"flag":0,"flen":-1,"decimal":-1,"collate":46,"charset":""}}`, } for i, funcName := range funcNames { aggFunc := &AggFuncDesc{ diff --git a/expression/aggregation/descriptor.go b/expression/aggregation/descriptor.go index e631c9cd6629d..f4648fffab533 100644 --- a/expression/aggregation/descriptor.go +++ b/expression/aggregation/descriptor.go @@ -334,8 +334,8 @@ func (a *AggFuncDesc) typeInfer4Avg(ctx sessionctx.Context) { func (a *AggFuncDesc) typeInfer4GroupConcat(ctx sessionctx.Context) { a.RetTp = types.NewFieldType(mysql.TypeVarString) - a.RetTp.Charset = charset.CharsetUTF8 - a.RetTp.Collate = charset.CollationUTF8 + a.RetTp.Charset, a.RetTp.Collate = charset.GetDefaultCharsetAndCollate() + a.RetTp.Flen, a.RetTp.Decimal = mysql.MaxBlobWidth, 0 // TODO: a.Args[i] = expression.WrapWithCastAsString(ctx, a.Args[i]) } diff --git a/expression/builtin.go b/expression/builtin.go index 51379440e7e28..cb7549f41b587 100644 --- a/expression/builtin.go +++ b/expression/builtin.go @@ -141,15 +141,15 @@ func newBaseBuiltinFuncWithTp(ctx sessionctx.Context, args []Expression, retType Tp: mysql.TypeJSON, Flen: mysql.MaxBlobWidth, Decimal: 0, - Charset: charset.CharsetUTF8, - Collate: charset.CollationUTF8, + Charset: mysql.DefaultCharset, + Collate: mysql.DefaultCollationName, Flag: mysql.BinaryFlag, } } if mysql.HasBinaryFlag(fieldType.Flag) && fieldType.Tp != mysql.TypeJSON { fieldType.Charset, fieldType.Collate = charset.CharsetBin, charset.CollationBin } else { - fieldType.Charset, fieldType.Collate = charset.CharsetUTF8, charset.CharsetUTF8 + fieldType.Charset, fieldType.Collate = charset.GetDefaultCharsetAndCollate() } return baseBuiltinFunc{ args: args, @@ -199,7 +199,7 @@ func (b *baseBuiltinFunc) getRetTp() *types.FieldType { b.tp.Tp = mysql.TypeMediumBlob } if len(b.tp.Charset) <= 0 { - b.tp.Charset, b.tp.Collate = charset.CharsetUTF8, charset.CollationUTF8 + b.tp.Charset, b.tp.Collate = charset.GetDefaultCharsetAndCollate() } } return b.tp diff --git a/expression/builtin_cast.go b/expression/builtin_cast.go index dd8067d14d908..7b2b993952459 100644 --- a/expression/builtin_cast.go +++ b/expression/builtin_cast.go @@ -1741,7 +1741,7 @@ func WrapWithCastAsString(ctx sessionctx.Context, expr Expression) Expression { argLen = mysql.MaxIntWidth } tp := types.NewFieldType(mysql.TypeVarString) - tp.Charset, tp.Collate = charset.CharsetUTF8, charset.CollationUTF8 + tp.Charset, tp.Collate = charset.GetDefaultCharsetAndCollate() tp.Flen, tp.Decimal = argLen, types.UnspecifiedLength return BuildCastFunction(ctx, expr, tp) } @@ -1804,8 +1804,8 @@ func WrapWithCastAsJSON(ctx sessionctx.Context, expr Expression) Expression { Tp: mysql.TypeJSON, Flen: 12582912, // FIXME: Here the Flen is not trusted. Decimal: 0, - Charset: charset.CharsetUTF8, - Collate: charset.CollationUTF8, + Charset: mysql.DefaultCharset, + Collate: mysql.DefaultCollationName, Flag: mysql.BinaryFlag, } return BuildCastFunction(ctx, expr, tp) diff --git a/expression/builtin_control.go b/expression/builtin_control.go index bf588a058ae44..15ad0d736ef40 100644 --- a/expression/builtin_control.go +++ b/expression/builtin_control.go @@ -81,19 +81,19 @@ func inferType4ControlFuncs(lhs, rhs *types.FieldType) *types.FieldType { } } if types.IsNonBinaryStr(lhs) && !types.IsBinaryStr(rhs) { - resultFieldType.Charset, resultFieldType.Collate, resultFieldType.Flag = charset.CharsetUTF8, charset.CollationUTF8, 0 + resultFieldType.Charset, resultFieldType.Collate, resultFieldType.Flag = charset.CharsetUTF8MB4, charset.CollationUTF8MB4, 0 if mysql.HasBinaryFlag(lhs.Flag) || !types.IsNonBinaryStr(rhs) { resultFieldType.Flag |= mysql.BinaryFlag } } else if types.IsNonBinaryStr(rhs) && !types.IsBinaryStr(lhs) { - resultFieldType.Charset, resultFieldType.Collate, resultFieldType.Flag = charset.CharsetUTF8, charset.CollationUTF8, 0 + resultFieldType.Charset, resultFieldType.Collate, resultFieldType.Flag = charset.CharsetUTF8MB4, charset.CollationUTF8MB4, 0 if mysql.HasBinaryFlag(rhs.Flag) || !types.IsNonBinaryStr(lhs) { resultFieldType.Flag |= mysql.BinaryFlag } } else if types.IsBinaryStr(lhs) || types.IsBinaryStr(rhs) || !evalType.IsStringKind() { types.SetBinChsClnFlag(resultFieldType) } else { - resultFieldType.Charset, resultFieldType.Collate, resultFieldType.Flag = charset.CharsetUTF8, charset.CollationUTF8, 0 + resultFieldType.Charset, resultFieldType.Collate, resultFieldType.Flag = mysql.DefaultCharset, mysql.DefaultCollationName, 0 } if evalType == types.ETDecimal || evalType == types.ETInt { lhsUnsignedFlag, rhsUnsignedFlag := mysql.HasUnsignedFlag(lhs.Flag), mysql.HasUnsignedFlag(rhs.Flag) @@ -164,7 +164,7 @@ func (c *caseWhenFunctionClass) getFunction(ctx sessionctx.Context, args []Expre } fieldTp.Decimal, fieldTp.Flen = decimal, flen if fieldTp.EvalType().IsStringKind() && !isBinaryStr { - fieldTp.Charset, fieldTp.Collate = mysql.DefaultCharset, mysql.DefaultCollationName + fieldTp.Charset, fieldTp.Collate = charset.CharsetUTF8MB4, charset.CollationUTF8MB4 } if isBinaryFlag { fieldTp.Flag |= mysql.BinaryFlag diff --git a/expression/builtin_math_test.go b/expression/builtin_math_test.go index 4a557302bba65..d71a677ba9397 100644 --- a/expression/builtin_math_test.go +++ b/expression/builtin_math_test.go @@ -559,8 +559,8 @@ func (s *testEvaluatorSuite) TestConv(c *C) { c.Assert(err, IsNil) tp := f.GetType() c.Assert(tp.Tp, Equals, mysql.TypeVarString) - c.Assert(tp.Charset, Equals, charset.CharsetUTF8) - c.Assert(tp.Collate, Equals, charset.CharsetUTF8) + c.Assert(tp.Charset, Equals, charset.CharsetUTF8MB4) + c.Assert(tp.Collate, Equals, charset.CollationUTF8MB4) c.Assert(tp.Flag, Equals, uint(0)) d, err := f.Eval(chunk.Row{}) diff --git a/expression/builtin_miscellaneous.go b/expression/builtin_miscellaneous.go index 440d7778cbdc0..46239c7802f95 100644 --- a/expression/builtin_miscellaneous.go +++ b/expression/builtin_miscellaneous.go @@ -22,7 +22,6 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/parser/charset" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" @@ -227,7 +226,7 @@ func (c *anyValueFunctionClass) getFunction(ctx sessionctx.Context, args []Expre bf.tp.Decimal = types.UnspecifiedLength sig = &builtinStringAnyValueSig{bf} case types.ETDatetime, types.ETTimestamp: - bf.tp.Charset, bf.tp.Collate, bf.tp.Flag = charset.CharsetUTF8, charset.CollationUTF8, 0 + bf.tp.Charset, bf.tp.Collate, bf.tp.Flag = mysql.DefaultCharset, mysql.DefaultCollationName, 0 sig = &builtinTimeAnyValueSig{bf} default: panic("unexpected types.EvalType of builtin function ANY_VALUE") diff --git a/expression/column.go b/expression/column.go index 551ad98a76a2e..3ef13aac613f1 100644 --- a/expression/column.go +++ b/expression/column.go @@ -153,9 +153,10 @@ type Column struct { ID int64 // UniqueID is the unique id of this column. UniqueID int64 - // IsAggOrSubq means if this column is referenced to a Aggregation column or a Subquery column. + // IsReferenced means if this column is referenced to an Aggregation column, or a Subquery column, + // or an argument column of function IfNull. // If so, this column's name will be the plain sql text. - IsAggOrSubq bool + IsReferenced bool // Index is used for execution, to tell the column's position in the given row. Index int diff --git a/expression/expr_to_pb_test.go b/expression/expr_to_pb_test.go index edd06d9b3dcd3..95349ff9db95f 100644 --- a/expression/expr_to_pb_test.go +++ b/expression/expr_to_pb_test.go @@ -173,31 +173,31 @@ func (s *testEvaluatorSuite) TestColumn2Pb(c *C) { c.Assert(len(remained), Equals, 0) js, err := json.Marshal(pbExpr) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":2,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAQ=\",\"sig\":0,\"field_type\":{\"tp\":4,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAU=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAY=\",\"sig\":0,\"field_type\":{\"tp\":6,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAc=\",\"sig\":0,\"field_type\":{\"tp\":7,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAg=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAk=\",\"sig\":0,\"field_type\":{\"tp\":9,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAo=\",\"sig\":0,\"field_type\":{\"tp\":10,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAs=\",\"sig\":0,\"field_type\":{\"tp\":11,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAw=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAA0=\",\"sig\":0,\"field_type\":{\"tp\":13,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAA8=\",\"sig\":0,\"field_type\":{\"tp\":15,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABA=\",\"sig\":0,\"field_type\":{\"tp\":245,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABE=\",\"sig\":0,\"field_type\":{\"tp\":246,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABI=\",\"sig\":0,\"field_type\":{\"tp\":249,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABM=\",\"sig\":0,\"field_type\":{\"tp\":250,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABQ=\",\"sig\":0,\"field_type\":{\"tp\":251,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABU=\",\"sig\":0,\"field_type\":{\"tp\":252,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABY=\",\"sig\":0,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABc=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}") + c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":2,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAQ=\",\"sig\":0,\"field_type\":{\"tp\":4,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAU=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAY=\",\"sig\":0,\"field_type\":{\"tp\":6,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAc=\",\"sig\":0,\"field_type\":{\"tp\":7,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAg=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAk=\",\"sig\":0,\"field_type\":{\"tp\":9,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAo=\",\"sig\":0,\"field_type\":{\"tp\":10,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAs=\",\"sig\":0,\"field_type\":{\"tp\":11,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAw=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAA0=\",\"sig\":0,\"field_type\":{\"tp\":13,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAA8=\",\"sig\":0,\"field_type\":{\"tp\":15,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABA=\",\"sig\":0,\"field_type\":{\"tp\":245,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABE=\",\"sig\":0,\"field_type\":{\"tp\":246,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABI=\",\"sig\":0,\"field_type\":{\"tp\":249,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABM=\",\"sig\":0,\"field_type\":{\"tp\":250,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABQ=\",\"sig\":0,\"field_type\":{\"tp\":251,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABU=\",\"sig\":0,\"field_type\":{\"tp\":252,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABY=\",\"sig\":0,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAABc=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}") pbExprs = ExpressionsToPBList(sc, colExprs, client) jsons := []string{ - "{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":2,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAQ=\",\"sig\":0,\"field_type\":{\"tp\":4,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAU=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAY=\",\"sig\":0,\"field_type\":{\"tp\":6,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAc=\",\"sig\":0,\"field_type\":{\"tp\":7,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAg=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAk=\",\"sig\":0,\"field_type\":{\"tp\":9,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAo=\",\"sig\":0,\"field_type\":{\"tp\":10,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAs=\",\"sig\":0,\"field_type\":{\"tp\":11,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAAw=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAA0=\",\"sig\":0,\"field_type\":{\"tp\":13,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAAA8=\",\"sig\":0,\"field_type\":{\"tp\":15,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABA=\",\"sig\":0,\"field_type\":{\"tp\":245,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABE=\",\"sig\":0,\"field_type\":{\"tp\":246,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABI=\",\"sig\":0,\"field_type\":{\"tp\":249,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABM=\",\"sig\":0,\"field_type\":{\"tp\":250,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABQ=\",\"sig\":0,\"field_type\":{\"tp\":251,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABU=\",\"sig\":0,\"field_type\":{\"tp\":252,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABY=\",\"sig\":0,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":201,\"val\":\"gAAAAAAAABc=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":2,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAQ=\",\"sig\":0,\"field_type\":{\"tp\":4,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAU=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAY=\",\"sig\":0,\"field_type\":{\"tp\":6,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAc=\",\"sig\":0,\"field_type\":{\"tp\":7,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAg=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAk=\",\"sig\":0,\"field_type\":{\"tp\":9,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAo=\",\"sig\":0,\"field_type\":{\"tp\":10,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAs=\",\"sig\":0,\"field_type\":{\"tp\":11,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAAw=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAA0=\",\"sig\":0,\"field_type\":{\"tp\":13,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAAA8=\",\"sig\":0,\"field_type\":{\"tp\":15,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABA=\",\"sig\":0,\"field_type\":{\"tp\":245,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABE=\",\"sig\":0,\"field_type\":{\"tp\":246,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABI=\",\"sig\":0,\"field_type\":{\"tp\":249,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABM=\",\"sig\":0,\"field_type\":{\"tp\":250,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABQ=\",\"sig\":0,\"field_type\":{\"tp\":251,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABU=\",\"sig\":0,\"field_type\":{\"tp\":252,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABY=\",\"sig\":0,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":201,\"val\":\"gAAAAAAAABc=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", } for i, pbExpr := range pbExprs { c.Assert(pbExprs, NotNil) @@ -214,7 +214,7 @@ func (s *testEvaluatorSuite) TestColumn2Pb(c *C) { c.Assert(pbExpr, NotNil) js, err = json.Marshal(pbExpr) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":2,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":4,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":6,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":7,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":9,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":10,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":11,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":13,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":15,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":245,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":246,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":249,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":250,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":251,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":252,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}") + c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":2,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":4,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":6,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":7,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":9,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":10,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":11,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":13,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":15,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":245,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":246,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":249,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":250,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":251,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":252,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}") c.Assert(len(pushed), Equals, len(colExprs)) c.Assert(len(remained), Equals, 0) } @@ -238,18 +238,18 @@ func (s *testEvaluatorSuite) TestCompareFunc2Pb(c *C) { c.Assert(len(remained), Equals, 0) js, err := json.Marshal(pbExpr) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":100,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":110,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":120,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":130,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":140,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":150,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":160,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}") + c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":100,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":110,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":120,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":130,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":140,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":150,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}},{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":160,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}") pbExprs := ExpressionsToPBList(sc, compareExprs, client) c.Assert(len(pbExprs), Equals, len(compareExprs)) jsons := []string{ - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":100,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":110,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":120,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":130,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":140,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":150,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":160,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":100,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":110,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":120,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":130,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":140,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":150,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":8,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":160,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", } for i, pbExpr := range pbExprs { c.Assert(pbExprs, NotNil) @@ -310,10 +310,10 @@ func (s *testEvaluatorSuite) TestArithmeticalFunc2Pb(c *C) { } jsons := make(map[string]string) - jsons[ast.Plus] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":200,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":-1,\"decimal\":-1,\"collate\":63,\"charset\":\"binary\"}}" - jsons[ast.Minus] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":204,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":-1,\"decimal\":-1,\"collate\":63,\"charset\":\"binary\"}}" - jsons[ast.Mul] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":208,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":-1,\"decimal\":-1,\"collate\":63,\"charset\":\"binary\"}}" - jsons[ast.Div] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":211,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":23,\"decimal\":31,\"collate\":63,\"charset\":\"binary\"}}" + jsons[ast.Plus] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":200,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":-1,\"decimal\":-1,\"collate\":63,\"charset\":\"binary\"}}" + jsons[ast.Minus] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":204,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":-1,\"decimal\":-1,\"collate\":63,\"charset\":\"binary\"}}" + jsons[ast.Mul] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":208,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":-1,\"decimal\":-1,\"collate\":63,\"charset\":\"binary\"}}" + jsons[ast.Div] = "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":211,\"field_type\":{\"tp\":5,\"flag\":128,\"flen\":23,\"decimal\":31,\"collate\":63,\"charset\":\"binary\"}}" pbExprs := ExpressionsToPBList(sc, arithmeticalFuncs, client) for i, pbExpr := range pbExprs { @@ -345,7 +345,7 @@ func (s *testEvaluatorSuite) TestDateFunc2Pb(c *C) { c.Assert(pbExprs[0], NotNil) js, err := json.Marshal(pbExprs[0]) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":6001,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":0,\"decimal\":-1,\"collate\":83,\"charset\":\"utf8\"}}") + c.Assert(string(js), Equals, "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":12,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":254,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":6001,\"field_type\":{\"tp\":253,\"flag\":0,\"flen\":0,\"decimal\":-1,\"collate\":46,\"charset\":\"utf8mb4\"}}") } func (s *testEvaluatorSuite) TestLogicalFunc2Pb(c *C) { @@ -372,10 +372,10 @@ func (s *testEvaluatorSuite) TestLogicalFunc2Pb(c *C) { pbExprs := ExpressionsToPBList(sc, logicalFuncs, client) jsons := []string{ - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3102,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3101,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3102,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", "null", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3104,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":1,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3104,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", } for i, pbExpr := range pbExprs { js, err := json.Marshal(pbExpr) @@ -443,9 +443,9 @@ func (s *testEvaluatorSuite) TestControlFunc2Pb(c *C) { pbExprs := ExpressionsToPBList(sc, controlFuncs, client) jsons := []string{ - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":4208,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":0,\"decimal\":0,\"collate\":83,\"charset\":\"\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":4107,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":-1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", - "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":4101,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":-1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":4208,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":0,\"decimal\":0,\"collate\":46,\"charset\":\"\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAM=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":4107,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":-1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},{\"tp\":201,\"val\":\"gAAAAAAAAAI=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":4101,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":-1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", "null", } for i, pbExpr := range pbExprs { @@ -475,8 +475,8 @@ func (s *testEvaluatorSuite) TestOtherFunc2Pb(c *C) { pbExprs := ExpressionsToPBList(sc, otherFuncs, client) jsons := map[string]string{ - ast.Coalesce: "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":4201,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":0,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}", - ast.IsNull: "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}}],\"sig\":3116,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", + ast.Coalesce: "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":4201,\"field_type\":{\"tp\":3,\"flag\":128,\"flen\":0,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}", + ast.IsNull: "{\"tp\":10000,\"children\":[{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":3,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}}],\"sig\":3116,\"field_type\":{\"tp\":8,\"flag\":128,\"flen\":1,\"decimal\":0,\"collate\":63,\"charset\":\"binary\"}}", } for i, pbExpr := range pbExprs { js, err := json.Marshal(pbExpr) @@ -493,13 +493,13 @@ func (s *testEvaluatorSuite) TestGroupByItem2Pb(c *C) { pbByItem := GroupByItemToPB(sc, client, item) js, err := json.Marshal(pbByItem) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},\"desc\":false}") + c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},\"desc\":false}") item = dg.genColumn(mysql.TypeDouble, 1) pbByItem = GroupByItemToPB(sc, client, item) js, err = json.Marshal(pbByItem) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},\"desc\":false}") + c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},\"desc\":false}") } func (s *testEvaluatorSuite) TestSortByItem2Pb(c *C) { @@ -510,17 +510,17 @@ func (s *testEvaluatorSuite) TestSortByItem2Pb(c *C) { pbByItem := SortByItemToPB(sc, client, item, false) js, err := json.Marshal(pbByItem) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},\"desc\":false}") + c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAA=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},\"desc\":false}") item = dg.genColumn(mysql.TypeDouble, 1) pbByItem = SortByItemToPB(sc, client, item, false) js, err = json.Marshal(pbByItem) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},\"desc\":false}") + c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},\"desc\":false}") item = dg.genColumn(mysql.TypeDouble, 1) pbByItem = SortByItemToPB(sc, client, item, true) js, err = json.Marshal(pbByItem) c.Assert(err, IsNil) - c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":83,\"charset\":\"\"}},\"desc\":true}") + c.Assert(string(js), Equals, "{\"expr\":{\"tp\":201,\"val\":\"gAAAAAAAAAE=\",\"sig\":0,\"field_type\":{\"tp\":5,\"flag\":0,\"flen\":-1,\"decimal\":-1,\"collate\":46,\"charset\":\"\"}},\"desc\":true}") } diff --git a/expression/integration_test.go b/expression/integration_test.go index 5f6ae70352171..d485a6847d994 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -2887,6 +2887,18 @@ func (s *testIntegrationSuite) TestCompareBuiltin(c *C) { tk.MustExec("create table t(a bigint unsigned)") tk.MustExec("insert into t value(17666000000000000000)") tk.MustQuery("select * from t where a = 17666000000000000000").Check(testkit.Rows("17666000000000000000")) + + // test for compare row + result = tk.MustQuery(`select row(1,2,3)=row(1,2,3)`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select row(1,2,3)=row(1+3,2,3)`) + result.Check(testkit.Rows("0")) + result = tk.MustQuery(`select row(1,2,3)<>row(1,2,3)`) + result.Check(testkit.Rows("0")) + result = tk.MustQuery(`select row(1,2,3)<>row(1+3,2,3)`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select row(1+3,2,3)<>row(1+3,2,3)`) + result.Check(testkit.Rows("0")) } func (s *testIntegrationSuite) TestAggregationBuiltin(c *C) { diff --git a/expression/schema.go b/expression/schema.go index c6a7fce83b69e..a6f4accd5e3a8 100644 --- a/expression/schema.go +++ b/expression/schema.go @@ -119,7 +119,7 @@ func (s *Schema) FindColumnAndIndex(astCol *ast.ColumnName) (*Column, int, error // we will get an Apply operator whose schema is [test.t1.a, test.t2.d, test.t2.d], // we check whether the column of the schema comes from a subquery to avoid // causing the ambiguous error when resolve the column `d` in the Selection. - if !col.IsAggOrSubq { + if !col.IsReferenced { return nil, -1, errors.Errorf("Column %s is ambiguous", col.String()) } } diff --git a/expression/simple_rewriter.go b/expression/simple_rewriter.go index 19bf512ab890f..46bdac88a329c 100644 --- a/expression/simple_rewriter.go +++ b/expression/simple_rewriter.go @@ -275,6 +275,9 @@ func (sr *simpleRewriter) constructBinaryOpFunction(l Expression, r Expression, return nil, errors.Trace(err) } } + if op == ast.NE { + return ComposeDNFCondition(sr.ctx, funcs...), nil + } return ComposeCNFCondition(sr.ctx, funcs...), nil default: larg0, rarg0 := GetFuncArg(l, 0), GetFuncArg(r, 0) diff --git a/expression/typeinfer_test.go b/expression/typeinfer_test.go index 1517cc674443f..c0226639c76bc 100644 --- a/expression/typeinfer_test.go +++ b/expression/typeinfer_test.go @@ -163,7 +163,7 @@ func (s *testInferTypeSuite) createTestCase4Constants() []typeInferTestCase { {"NULL", mysql.TypeNull, charset.CharsetBin, mysql.BinaryFlag, 0, 0}, {"TRUE", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.IsBooleanFlag, 1, 0}, {"FALSE", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.IsBooleanFlag, 1, 0}, - {"'1234'", mysql.TypeVarString, charset.CharsetUTF8, 0, 4, types.UnspecifiedLength}, + {"'1234'", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 4, types.UnspecifiedLength}, {"_utf8'1234'", mysql.TypeVarString, charset.CharsetUTF8, 0, 4, types.UnspecifiedLength}, {"_binary'1234'", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 4, types.UnspecifiedLength}, {"b'0001'", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 1, 0}, @@ -179,14 +179,14 @@ func (s *testInferTypeSuite) createTestCase4Cast() []typeInferTestCase { return []typeInferTestCase{ {"CAST(c_int_d AS BINARY)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, -1, -1}, // TODO: Flen should be 11. {"CAST(c_int_d AS BINARY(5))", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 5, -1}, - {"CAST(c_int_d AS CHAR)", mysql.TypeVarString, charset.CharsetUTF8, 0, -1, -1}, // TODO: Flen should be 11. - {"CAST(c_int_d AS CHAR(5))", mysql.TypeVarString, charset.CharsetUTF8, 0, 5, -1}, + {"CAST(c_int_d AS CHAR)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, -1, -1}, // TODO: Flen should be 11. + {"CAST(c_int_d AS CHAR(5))", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 5, -1}, {"CAST(c_int_d AS DATE)", mysql.TypeDate, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, {"CAST(c_int_d AS DATETIME)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 19, 0}, {"CAST(c_int_d AS DECIMAL)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, {"CAST(c_int_d AS DECIMAL(10))", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, {"CAST(c_int_d AS DECIMAL(10,3))", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 10, 3}, // TODO: Flen should be 12 - {"CAST(c_int_d AS JSON)", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag | mysql.ParseToJSONFlag, 12582912 / 3, 0}, + {"CAST(c_int_d AS JSON)", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag | mysql.ParseToJSONFlag, 12582912 / 3, 0}, {"CAST(c_int_d AS SIGNED)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 22, 0}, // TODO: Flen should be 11. {"CAST(c_int_d AS SIGNED INTEGER)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 22, 0}, // TODO: Flen should be 11. {"CAST(c_int_d AS TIME)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, @@ -217,65 +217,65 @@ func (s *testInferTypeSuite) createTestCase4Columns() []typeInferTestCase { {"c_time_d ", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, {"c_timestamp ", mysql.TypeTimestamp, charset.CharsetBin, mysql.BinaryFlag, 24, 4}, {"c_timestamp_d", mysql.TypeTimestamp, charset.CharsetBin, mysql.BinaryFlag, 19, 0}, - {"c_char ", mysql.TypeString, charset.CharsetUTF8, 0, 20, 0}, // TODO: flag should be BinaryFlag - {"c_bchar ", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 20, 0}, - {"c_varchar ", mysql.TypeVarchar, charset.CharsetUTF8, 0, 20, 0}, // TODO: BinaryFlag, tp should be TypeVarString - {"c_bvarchar ", mysql.TypeVarchar, charset.CharsetUTF8, mysql.BinaryFlag, 20, 0}, // TODO: BinaryFlag, tp should be TypeVarString - {"c_text_d ", mysql.TypeBlob, charset.CharsetUTF8, 0, 65535, 0}, // TODO: BlobFlag, BinaryFlag - {"c_btext_d ", mysql.TypeBlob, charset.CharsetUTF8, mysql.BinaryFlag, 65535, 0}, // TODO: BlobFlag, BinaryFlag - {"c_binary ", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, // TODO: BinaryFlag - {"c_varbinary ", mysql.TypeVarchar, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, // TODO: BinaryFlag, tp should be TypeVarString - {"c_blob_d ", mysql.TypeBlob, charset.CharsetBin, mysql.BinaryFlag, 65535, 0}, // TODO: BlobFlag, BinaryFlag - {"c_set ", mysql.TypeSet, charset.CharsetUTF8, 0, types.UnspecifiedLength, 0}, // TODO: SetFlag, BinaryFlag, Flen should be 5 - {"c_enum ", mysql.TypeEnum, charset.CharsetUTF8, 0, types.UnspecifiedLength, 0}, // TODO: EnumFlag, BinaryFlag, Flen should be 1 + {"c_char ", mysql.TypeString, charset.CharsetUTF8MB4, 0, 20, 0}, // TODO: flag should be BinaryFlag + {"c_bchar ", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, 0}, + {"c_varchar ", mysql.TypeVarchar, charset.CharsetUTF8MB4, 0, 20, 0}, // TODO: BinaryFlag, tp should be TypeVarString + {"c_bvarchar ", mysql.TypeVarchar, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, 0}, // TODO: BinaryFlag, tp should be TypeVarString + {"c_text_d ", mysql.TypeBlob, charset.CharsetUTF8MB4, 0, 65535, 0}, // TODO: BlobFlag, BinaryFlag + {"c_btext_d ", mysql.TypeBlob, charset.CharsetUTF8MB4, mysql.BinaryFlag, 65535, 0}, // TODO: BlobFlag, BinaryFlag + {"c_binary ", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, // TODO: BinaryFlag + {"c_varbinary ", mysql.TypeVarchar, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, // TODO: BinaryFlag, tp should be TypeVarString + {"c_blob_d ", mysql.TypeBlob, charset.CharsetBin, mysql.BinaryFlag, 65535, 0}, // TODO: BlobFlag, BinaryFlag + {"c_set ", mysql.TypeSet, charset.CharsetUTF8MB4, 0, types.UnspecifiedLength, 0}, // TODO: SetFlag, BinaryFlag, Flen should be 5 + {"c_enum ", mysql.TypeEnum, charset.CharsetUTF8MB4, 0, types.UnspecifiedLength, 0}, // TODO: EnumFlag, BinaryFlag, Flen should be 1 } } func (s *testInferTypeSuite) createTestCase4StrFuncs() []typeInferTestCase { return []typeInferTestCase{ {"strcmp(c_char, c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 2, 0}, - {"space(c_int_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"space(c_int_d)", mysql.TypeLongBlob, mysql.DefaultCharset, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"CONCAT(c_binary, c_int_d)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 40, types.UnspecifiedLength}, - {"CONCAT(c_bchar, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, mysql.BinaryFlag, 40, types.UnspecifiedLength}, - {"CONCAT('T', 'i', 'DB')", mysql.TypeVarString, charset.CharsetUTF8, 0, 4, types.UnspecifiedLength}, + {"CONCAT(c_bchar, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 40, types.UnspecifiedLength}, + {"CONCAT('T', 'i', 'DB')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 4, types.UnspecifiedLength}, {"CONCAT('T', 'i', 'DB', c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 24, types.UnspecifiedLength}, - {"CONCAT_WS('-', 'T', 'i', 'DB')", mysql.TypeVarString, charset.CharsetUTF8, 0, 6, types.UnspecifiedLength}, + {"CONCAT_WS('-', 'T', 'i', 'DB')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 6, types.UnspecifiedLength}, {"CONCAT_WS(',', 'TiDB', c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 25, types.UnspecifiedLength}, - {"left(c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"right(c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"lower(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"left(c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"right(c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"lower(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"lower(c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"upper(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"upper(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"upper(c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"replace(1234, 2, 55)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"replace(1234, 2, 55)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"replace(c_binary, 1, 2)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"to_base64(c_binary)", mysql.TypeVarString, charset.CharsetUTF8, 0, 28, types.UnspecifiedLength}, - {"substr(c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"to_base64(c_binary)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 28, types.UnspecifiedLength}, + {"substr(c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"substr(c_binary, c_int_d)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"uuid()", mysql.TypeVarString, charset.CharsetUTF8, 0, 36, types.UnspecifiedLength}, + {"uuid()", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 36, types.UnspecifiedLength}, {"bit_length(c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, - {"substring_index(c_int_d, '.', 1)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"substring_index(c_int_d, '.', 1)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"substring_index(c_binary, '.', 1)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"hex(c_char)", mysql.TypeVarString, charset.CharsetUTF8, 0, 120, types.UnspecifiedLength}, - {"hex(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 22, types.UnspecifiedLength}, + {"hex(c_char)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 120, types.UnspecifiedLength}, + {"hex(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 22, types.UnspecifiedLength}, {"unhex(c_int_d)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 6, types.UnspecifiedLength}, {"unhex(c_char)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 30, types.UnspecifiedLength}, - {"ltrim(c_char)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"ltrim(c_char)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"ltrim(c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"rtrim(c_char)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"rtrim(c_char)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"rtrim(c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"trim(c_char)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"trim(c_char)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"trim(c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"ascii(c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, {"ord(c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, {`c_int_d like 'abc%'`, mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0}, - {"tidb_version()", mysql.TypeVarString, charset.CharsetUTF8, 0, len(printer.GetTiDBInfo()), types.UnspecifiedLength}, + {"tidb_version()", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, len(printer.GetTiDBInfo()), types.UnspecifiedLength}, {"tidb_is_ddl_owner()", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, - {"password(c_char)", mysql.TypeVarString, charset.CharsetUTF8, 0, mysql.PWDHashLen + 1, types.UnspecifiedLength}, - {"elt(c_int_d, c_char, c_char, c_char)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, + {"password(c_char)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, mysql.PWDHashLen + 1, types.UnspecifiedLength}, + {"elt(c_int_d, c_char, c_char, c_char)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, {"elt(c_int_d, c_char, c_char, c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"elt(c_int_d, c_char, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"elt(c_int_d, c_char, c_double_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 22, types.UnspecifiedLength}, + {"elt(c_int_d, c_char, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"elt(c_int_d, c_char, c_double_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 22, types.UnspecifiedLength}, {"elt(c_int_d, c_char, c_double_d, c_int_d, c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 22, types.UnspecifiedLength}, {"locate(c_char, c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, @@ -287,14 +287,14 @@ func (s *testInferTypeSuite) createTestCase4StrFuncs() []typeInferTestCase { {"locate(c_binary, c_char, c_int_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, {"locate(c_binary, c_binary, c_int_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, - {"lpad('TiDB', 12, 'go' )", mysql.TypeVarString, charset.CharsetUTF8, 0, 48, types.UnspecifiedLength}, + {"lpad('TiDB', 12, 'go' )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 48, types.UnspecifiedLength}, {"lpad(c_binary, 12, 'go' )", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 12, types.UnspecifiedLength}, {"lpad(c_char, c_int_d, c_binary)", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"lpad(c_char, c_int_d, c_char )", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"rpad('TiDB', 12, 'go' )", mysql.TypeVarString, charset.CharsetUTF8, 0, 48, types.UnspecifiedLength}, + {"lpad(c_char, c_int_d, c_char )", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"rpad('TiDB', 12, 'go' )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 48, types.UnspecifiedLength}, {"rpad(c_binary, 12, 'go' )", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 12, types.UnspecifiedLength}, {"rpad(c_char, c_int_d, c_binary)", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"rpad(c_char, c_int_d, c_char )", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"rpad(c_char, c_int_d, c_char )", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"from_base64(c_int_d )", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"from_base64(c_bigint_d )", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, @@ -313,22 +313,22 @@ func (s *testInferTypeSuite) createTestCase4StrFuncs() []typeInferTestCase { {"from_base64(c_set )", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"from_base64(c_enum )", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"bin(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"bin(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, + {"bin(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"bin(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, {"char_length(c_int_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, {"char_length(c_float_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, @@ -399,36 +399,36 @@ func (s *testInferTypeSuite) createTestCase4StrFuncs() []typeInferTestCase { {"instr(c_char, c_char )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, {"instr(c_char, c_time_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, - {"reverse(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"reverse(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"reverse(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 12, types.UnspecifiedLength}, - {"reverse(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 22, types.UnspecifiedLength}, - {"reverse(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 8, types.UnspecifiedLength}, - {"reverse(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"reverse(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"reverse(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 65535, types.UnspecifiedLength}, + {"reverse(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"reverse(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"reverse(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 12, types.UnspecifiedLength}, + {"reverse(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 22, types.UnspecifiedLength}, + {"reverse(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 8, types.UnspecifiedLength}, + {"reverse(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"reverse(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"reverse(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 65535, types.UnspecifiedLength}, {"reverse(c_binary )", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"reverse(c_varbinary )", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"reverse(c_blob_d )", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 65535, types.UnspecifiedLength}, - {"reverse(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, types.UnspecifiedLength, types.UnspecifiedLength}, - {"reverse(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, types.UnspecifiedLength, types.UnspecifiedLength}, - - {"oct(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"oct(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, + {"reverse(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, types.UnspecifiedLength, types.UnspecifiedLength}, + {"reverse(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, types.UnspecifiedLength, types.UnspecifiedLength}, + + {"oct(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"oct(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, {"find_in_set(c_int_d , c_text_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, {"find_in_set(c_bigint_d , c_text_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, @@ -447,30 +447,30 @@ func (s *testInferTypeSuite) createTestCase4StrFuncs() []typeInferTestCase { {"find_in_set(c_set , c_text_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, {"find_in_set(c_enum , c_text_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, - {"make_set(c_int_d , c_text_d)", mysql.TypeVarString, charset.CharsetUTF8, mysql.BinaryFlag, 65535, types.UnspecifiedLength}, + {"make_set(c_int_d , c_text_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 65535, types.UnspecifiedLength}, {"make_set(c_bigint_d , c_text_d, c_binary)", mysql.TypeMediumBlob, charset.CharsetBin, mysql.BinaryFlag, 65556, types.UnspecifiedLength}, - {"quote(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 42, types.UnspecifiedLength}, - {"quote(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 42, types.UnspecifiedLength}, - {"quote(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"quote(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 46, types.UnspecifiedLength}, + {"quote(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 42, types.UnspecifiedLength}, + {"quote(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 42, types.UnspecifiedLength}, + {"quote(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"quote(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 46, types.UnspecifiedLength}, - {"convert(c_double_d using c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"convert(c_binary using c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"convert(c_binary using c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"convert(c_text_d using c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"convert(c_double_d using c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"convert(c_binary using c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"convert(c_binary using c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"convert(c_text_d using c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"insert(c_varchar, c_int_d, c_int_d, c_varchar)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"insert(c_varchar, c_int_d, c_int_d, c_varchar)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"insert(c_varchar, c_int_d, c_int_d, c_binary)", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"insert(c_binary, c_int_d, c_int_d, c_varchar)", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"insert(c_binary, c_int_d, c_int_d, c_binary)", mysql.TypeLongBlob, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"export_set(c_double_d, c_text_d, c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"export_set(c_double_d, c_text_d, c_text_d, c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"export_set(c_double_d, c_text_d, c_text_d, c_text_d, c_int_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"export_set(c_double_d, c_text_d, c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"export_set(c_double_d, c_text_d, c_text_d, c_text_d)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"export_set(c_double_d, c_text_d, c_text_d, c_text_d, c_int_d)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"format(c_double_d, c_double_d)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, - {"format(c_double_d, c_double_d, c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"format(c_double_d, c_double_d)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, + {"format(c_double_d, c_double_d, c_binary)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, types.UnspecifiedLength}, {"field(c_double_d, c_text_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, } @@ -563,8 +563,8 @@ func (s *testInferTypeSuite) createTestCase4MathFuncs() []typeInferTestCase { {"ceiling(18446744073709551615)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, {"ceiling(18446744073709551615.1)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 22, 0}, - {"conv(c_char, c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"conv(c_int_d, c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, + {"conv(c_char, c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"conv(c_int_d, c_int_d, c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, {"abs(c_int_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, {"abs(c_bigint_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, @@ -790,24 +790,24 @@ func (s *testInferTypeSuite) createTestCase4ControlFuncs() []typeInferTestCase { return []typeInferTestCase{ {"ifnull(c_int_d, c_int_d)", mysql.TypeLong, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, {"ifnull(c_int_d, c_decimal)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 14, 3}, - {"ifnull(c_int_d, c_char)", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 20, types.UnspecifiedLength}, + {"ifnull(c_int_d, c_char)", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"ifnull(c_int_d, c_binary)", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"ifnull(c_char, c_binary)", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"ifnull(null, null)", mysql.TypeNull, charset.CharsetBin, mysql.BinaryFlag, 0, 0}, - {"ifnull(c_double_d, c_timestamp_d)", mysql.TypeVarchar, charset.CharsetUTF8, 0, 22, types.UnspecifiedLength}, - {"ifnull(c_json, c_decimal)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, math.MaxUint32, types.UnspecifiedLength}, + {"ifnull(c_double_d, c_timestamp_d)", mysql.TypeVarchar, charset.CharsetUTF8MB4, 0, 22, types.UnspecifiedLength}, + {"ifnull(c_json, c_decimal)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, math.MaxUint32, types.UnspecifiedLength}, {"if(c_int_d, c_decimal, c_int_d)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 14, 3}, - {"if(c_int_d, c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 20, types.UnspecifiedLength}, + {"if(c_int_d, c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"if(c_int_d, c_binary, c_int_d)", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"if(c_int_d, c_bchar, c_int_d)", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"if(c_int_d, c_char, c_decimal)", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"if(c_int_d, c_datetime, c_int_d)", mysql.TypeVarchar, charset.CharsetUTF8, 0, 22, types.UnspecifiedLength}, + {"if(c_int_d, c_bchar, c_int_d)", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, types.UnspecifiedLength}, + {"if(c_int_d, c_char, c_decimal)", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, types.UnspecifiedLength}, + {"if(c_int_d, c_datetime, c_int_d)", mysql.TypeVarchar, charset.CharsetUTF8MB4, 0, 22, types.UnspecifiedLength}, {"if(c_int_d, c_int_d, c_double_d)", mysql.TypeDouble, charset.CharsetBin, mysql.BinaryFlag, 22, types.UnspecifiedLength}, - {"if(c_int_d, c_time_d, c_datetime)", mysql.TypeDatetime, charset.CharsetUTF8, mysql.BinaryFlag, 22, 2}, // TODO: should not be BinaryFlag - {"if(c_int_d, c_time, c_json)", mysql.TypeLongBlob, charset.CharsetUTF8, 0, math.MaxUint32, types.UnspecifiedLength}, + {"if(c_int_d, c_time_d, c_datetime)", mysql.TypeDatetime, charset.CharsetUTF8MB4, mysql.BinaryFlag, 22, 2}, // TODO: should not be BinaryFlag + {"if(c_int_d, c_time, c_json)", mysql.TypeLongBlob, charset.CharsetUTF8MB4, 0, math.MaxUint32, types.UnspecifiedLength}, {"if(null, null, null)", mysql.TypeNull, charset.CharsetBin, mysql.BinaryFlag, 0, 0}, - {"case when c_int_d then c_char else c_varchar end", mysql.TypeVarchar, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"case when c_int_d > 1 then c_double_d else c_bchar end", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 22, types.UnspecifiedLength}, + {"case when c_int_d then c_char else c_varchar end", mysql.TypeVarchar, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"case when c_int_d > 1 then c_double_d else c_bchar end", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 22, types.UnspecifiedLength}, {"case when c_int_d > 2 then c_double_d when c_int_d < 1 then c_decimal else c_double_d end", mysql.TypeDouble, charset.CharsetBin, mysql.BinaryFlag, 22, 3}, {"case when c_double_d > 2 then c_decimal else 1 end", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 6, 3}, {"case when null then null else null end", mysql.TypeNull, charset.CharsetBin, mysql.BinaryFlag, 0, types.UnspecifiedLength}, @@ -830,7 +830,7 @@ func (s *testInferTypeSuite) createTestCase4Aggregations() []typeInferTestCase { {"avg(1.0)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxDecimalWidth, 5}, {"avg(1.2e2)", mysql.TypeDouble, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxRealWidth, types.UnspecifiedLength}, {"avg(c_char)", mysql.TypeDouble, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxRealWidth, types.UnspecifiedLength}, - {"group_concat(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, mysql.MaxBlobWidth, 0}, + {"group_concat(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, mysql.MaxBlobWidth, 0}, } } @@ -839,110 +839,110 @@ func (s *testInferTypeSuite) createTestCase4InfoFunc() []typeInferTestCase { {"last_insert_id( )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag, mysql.MaxIntWidth, 0}, {"last_insert_id(c_int_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag, mysql.MaxIntWidth, 0}, {"found_rows()", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag, mysql.MaxIntWidth, 0}, - {"database()", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"current_user()", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, - {"user()", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, + {"database()", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"current_user()", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, + {"user()", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, {"connection_id()", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag, mysql.MaxIntWidth, 0}, - {"version()", mysql.TypeVarString, charset.CharsetUTF8, 0, 64, types.UnspecifiedLength}, + {"version()", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 64, types.UnspecifiedLength}, } } func (s *testInferTypeSuite) createTestCase4EncryptionFuncs() []typeInferTestCase { return []typeInferTestCase{ - {"md5(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5('1234' )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - {"md5(1234 )", mysql.TypeVarString, charset.CharsetUTF8, 0, 32, types.UnspecifiedLength}, - - {"sha(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha('1234' )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha(1234 )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - - {"sha1(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1('1234' )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - {"sha1(1234 )", mysql.TypeVarString, charset.CharsetUTF8, 0, 40, types.UnspecifiedLength}, - - {"sha2(c_int_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_bigint_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_float_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_double_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_decimal , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_datetime , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_time_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_timestamp_d, 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_char , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_varchar , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_text_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_binary , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_varbinary , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_blob_d , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_set , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_enum , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2('1234' , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(1234 , 0)", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - - {"sha2(c_int_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_bigint_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_float_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_double_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_decimal , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_datetime , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_time_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_timestamp_d, '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_char , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_varchar , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_text_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_binary , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_varbinary , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_blob_d , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_set , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(c_enum , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2('1234' , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, - {"sha2(1234 , '256')", mysql.TypeVarString, charset.CharsetUTF8, 0, 128, types.UnspecifiedLength}, + {"md5(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5('1234' )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + {"md5(1234 )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 32, types.UnspecifiedLength}, + + {"sha(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha('1234' )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha(1234 )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + + {"sha1(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1('1234' )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + {"sha1(1234 )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 40, types.UnspecifiedLength}, + + {"sha2(c_int_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_bigint_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_float_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_double_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_decimal , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_datetime , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_time_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_timestamp_d, 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_char , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_varchar , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_text_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_binary , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_varbinary , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_blob_d , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_set , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_enum , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2('1234' , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(1234 , 0)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + + {"sha2(c_int_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_bigint_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_float_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_double_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_decimal , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_datetime , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_time_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_timestamp_d, '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_char , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_varchar , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_text_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_binary , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_varbinary , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_blob_d , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_set , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(c_enum , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2('1234' , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, + {"sha2(1234 , '256')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 128, types.UnspecifiedLength}, {"AES_ENCRYPT(c_int_d, 'key')", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 32, types.UnspecifiedLength}, {"AES_ENCRYPT(c_char, 'key')", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 32, types.UnspecifiedLength}, @@ -985,7 +985,7 @@ func (s *testInferTypeSuite) createTestCase4CompareFuncs() []typeInferTestCase { {"coalesce(c_int_d, 1)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, {"coalesce(NULL, c_int_d)", mysql.TypeLong, charset.CharsetBin, mysql.BinaryFlag, 11, 0}, {"coalesce(c_int_d, c_decimal)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, - {"coalesce(c_int_d, c_datetime)", mysql.TypeVarString, charset.CharsetUTF8, 0, 22, types.UnspecifiedLength}, + {"coalesce(c_int_d, c_datetime)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 22, types.UnspecifiedLength}, {"isnull(c_int_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0}, {"isnull(c_bigint_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0}, @@ -1012,9 +1012,9 @@ func (s *testInferTypeSuite) createTestCase4CompareFuncs() []typeInferTestCase { {"nullif(c_datetime , 123)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 22, 2}, // TODO: tp should be TypeVarString, no binary flag {"nullif(c_time_d , 123)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, // TODO: tp should be TypeVarString, no binary flag {"nullif(c_timestamp_d, 123)", mysql.TypeTimestamp, charset.CharsetBin, mysql.BinaryFlag, 19, 0}, // TODO: tp should be TypeVarString, no binary flag - {"nullif(c_char , 123)", mysql.TypeString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"nullif(c_varchar , 123)", mysql.TypeVarchar, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, // TODO: tp should be TypeVarString - {"nullif(c_text_d , 123)", mysql.TypeBlob, charset.CharsetUTF8, 0, 65535, types.UnspecifiedLength}, // TODO: tp should be TypeMediumBlob + {"nullif(c_char , 123)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"nullif(c_varchar , 123)", mysql.TypeVarchar, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, // TODO: tp should be TypeVarString + {"nullif(c_text_d , 123)", mysql.TypeBlob, charset.CharsetUTF8MB4, 0, 65535, types.UnspecifiedLength}, // TODO: tp should be TypeMediumBlob {"nullif(c_binary , 123)", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, // TODO: tp should be TypeVarString {"nullif(c_varbinary , 123)", mysql.TypeVarchar, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, // TODO: tp should be TypeVarString {"nullif(c_blob_d , 123)", mysql.TypeBlob, charset.CharsetBin, mysql.BinaryFlag, 65535, types.UnspecifiedLength}, // TODO: tp should be TypeVarString @@ -1044,14 +1044,14 @@ func (s *testInferTypeSuite) createTestCase4Miscellaneous() []typeInferTestCase {"inet_aton(c_timestamp_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag, 21, 0}, {"inet_aton(c_binary)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag, 21, 0}, - {"inet_ntoa(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_float_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_double_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_decimal)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_datetime)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_time_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, - {"inet_ntoa(c_binary)", mysql.TypeVarString, charset.CharsetUTF8, 0, 93, 0}, + {"inet_ntoa(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_float_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_double_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_decimal)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_datetime)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_time_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, + {"inet_ntoa(c_binary)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 93, 0}, {"inet6_aton(c_int_d)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 16, 0}, {"inet6_aton(c_float_d)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 16, 0}, @@ -1062,14 +1062,14 @@ func (s *testInferTypeSuite) createTestCase4Miscellaneous() []typeInferTestCase {"inet6_aton(c_timestamp_d)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 16, 0}, {"inet6_aton(c_binary)", mysql.TypeVarString, charset.CharsetBin, mysql.BinaryFlag, 16, 0}, - {"inet6_ntoa(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_float_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_double_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_decimal)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_datetime)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_time_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, - {"inet6_ntoa(c_binary)", mysql.TypeVarString, charset.CharsetUTF8, 0, 117, 0}, + {"inet6_ntoa(c_int_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_float_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_double_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_decimal)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_datetime)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_time_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, + {"inet6_ntoa(c_binary)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 117, 0}, {"is_ipv4(c_int_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0}, {"is_ipv4(c_float_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 1, 0}, @@ -1112,18 +1112,18 @@ func (s *testInferTypeSuite) createTestCase4Miscellaneous() []typeInferTestCase {"any_value(c_float_d)", mysql.TypeFloat, charset.CharsetBin, mysql.BinaryFlag, 12, types.UnspecifiedLength}, {"any_value(c_double_d)", mysql.TypeDouble, charset.CharsetBin, mysql.BinaryFlag, 22, types.UnspecifiedLength}, {"any_value(c_decimal)", mysql.TypeNewDecimal, charset.CharsetBin, mysql.BinaryFlag, 6, 3}, // TODO: Flen should be 8. - {"any_value(c_datetime)", mysql.TypeDatetime, charset.CharsetUTF8, 0, 22, 2}, + {"any_value(c_datetime)", mysql.TypeDatetime, charset.CharsetUTF8MB4, 0, 22, 2}, {"any_value(c_time_d)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 10, 0}, - {"any_value(c_timestamp_d)", mysql.TypeTimestamp, charset.CharsetUTF8, 0, 19, 0}, - {"any_value(c_char)", mysql.TypeString, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"any_value(c_bchar)", mysql.TypeString, charset.CharsetUTF8, mysql.BinaryFlag, 20, types.UnspecifiedLength}, - {"any_value(c_varchar)", mysql.TypeVarchar, charset.CharsetUTF8, 0, 20, types.UnspecifiedLength}, - {"any_value(c_text_d)", mysql.TypeBlob, charset.CharsetUTF8, 0, 65535, types.UnspecifiedLength}, + {"any_value(c_timestamp_d)", mysql.TypeTimestamp, charset.CharsetUTF8MB4, 0, 19, 0}, + {"any_value(c_char)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"any_value(c_bchar)", mysql.TypeString, charset.CharsetUTF8MB4, mysql.BinaryFlag, 20, types.UnspecifiedLength}, + {"any_value(c_varchar)", mysql.TypeVarchar, charset.CharsetUTF8MB4, 0, 20, types.UnspecifiedLength}, + {"any_value(c_text_d)", mysql.TypeBlob, charset.CharsetUTF8MB4, 0, 65535, types.UnspecifiedLength}, {"any_value(c_binary)", mysql.TypeString, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"any_value(c_varbinary)", mysql.TypeVarchar, charset.CharsetBin, mysql.BinaryFlag, 20, types.UnspecifiedLength}, {"any_value(c_blob_d)", mysql.TypeBlob, charset.CharsetBin, mysql.BinaryFlag, 65535, types.UnspecifiedLength}, - {"any_value(c_set)", mysql.TypeSet, charset.CharsetUTF8, 0, types.UnspecifiedLength, types.UnspecifiedLength}, - {"any_value(c_enum)", mysql.TypeEnum, charset.CharsetUTF8, 0, types.UnspecifiedLength, types.UnspecifiedLength}, + {"any_value(c_set)", mysql.TypeSet, charset.CharsetUTF8MB4, 0, types.UnspecifiedLength, types.UnspecifiedLength}, + {"any_value(c_enum)", mysql.TypeEnum, charset.CharsetUTF8MB4, 0, types.UnspecifiedLength, types.UnspecifiedLength}, } } @@ -1181,28 +1181,28 @@ func (s *testInferTypeSuite) createTestCase4OtherFuncs() []typeInferTestCase { {"bit_count(c_set )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 2, 0}, {"bit_count(c_enum )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 2, 0}, - {`@varname`, mysql.TypeVarString, charset.CharsetUTF8, 0, mysql.MaxFieldVarCharLength, types.UnspecifiedFsp}, + {`@varname`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, mysql.MaxFieldVarCharLength, types.UnspecifiedFsp}, } } func (s *testInferTypeSuite) createTestCase4TimeFuncs() []typeInferTestCase { return []typeInferTestCase{ - {`time_format('150:02:28', '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - {`time_format(123456, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - {`time_format('bad string', '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - {`time_format(null, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - - {`date_format(null, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - {`date_format('2017-06-15', '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - {`date_format(151113102019.12, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8, 0, 44, types.UnspecifiedLength}, - - {"timestampadd(HOUR, c_int_d, c_timestamp_d)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, - {"timestampadd(minute, c_double_d, c_timestamp_d)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, - {"timestampadd(SeconD, c_int_d, c_char)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, - {"timestampadd(SeconD, c_varchar, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, - {"timestampadd(SeconD, c_int_d, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, - {"timestampadd(SeconD, c_double_d, c_bchar)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, - {"timestampadd(SeconD, c_int_d, c_blob_d)", mysql.TypeString, charset.CharsetUTF8, 0, 19, types.UnspecifiedLength}, + {`time_format('150:02:28', '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + {`time_format(123456, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + {`time_format('bad string', '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + {`time_format(null, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + + {`date_format(null, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + {`date_format('2017-06-15', '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + {`date_format(151113102019.12, '%r%r%r%r')`, mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 44, types.UnspecifiedLength}, + + {"timestampadd(HOUR, c_int_d, c_timestamp_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, + {"timestampadd(minute, c_double_d, c_timestamp_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, + {"timestampadd(SeconD, c_int_d, c_char)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, + {"timestampadd(SeconD, c_varchar, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, + {"timestampadd(SeconD, c_int_d, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, + {"timestampadd(SeconD, c_double_d, c_bchar)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, + {"timestampadd(SeconD, c_int_d, c_blob_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 19, types.UnspecifiedLength}, {"to_seconds(c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, {"to_days(c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, @@ -1237,33 +1237,33 @@ func (s *testInferTypeSuite) createTestCase4TimeFuncs() []typeInferTestCase { {"timestampdiff(second, c_int_d, c_bchar)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, {"timestampdiff(YEAR, c_blob_d, c_bigint_d)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 20, 0}, - {"addtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, + {"addtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, {"addtime(c_datetime_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"addtime(c_datetime, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 2}, {"addtime(c_timestamp, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 4}, {"addtime(c_timestamp_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"addtime(c_time, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, {"addtime(c_time_d, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, - {"addtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - - {"subtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, + {"addtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + + {"subtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, {"subtime(c_datetime_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"subtime(c_datetime, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 2}, {"subtime(c_timestamp, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 4}, {"subtime(c_timestamp_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"subtime(c_time, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, {"subtime(c_time_d, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, - {"subtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, + {"subtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, {"timestamp(c_int_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 19, 0}, {"timestamp(c_float_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 6}, @@ -1291,33 +1291,33 @@ func (s *testInferTypeSuite) createTestCase4TimeFuncs() []typeInferTestCase { {"timestamp(c_timestamp, c_char)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 6}, {"timestamp(c_int_d, c_datetime)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 22, 2}, - {"addtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, + {"addtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, {"addtime(c_datetime_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"addtime(c_datetime, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 2}, {"addtime(c_timestamp, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 4}, {"addtime(c_timestamp_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"addtime(c_time, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, {"addtime(c_time_d, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, - {"addtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"addtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - - {"subtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, + {"addtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"addtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + + {"subtime(c_int_d, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, {"subtime(c_datetime_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"subtime(c_datetime, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 2}, {"subtime(c_timestamp, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 4}, {"subtime(c_timestamp_d, c_time_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 26, 0}, {"subtime(c_time, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, {"subtime(c_time_d, c_time)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 3}, - {"subtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, - {"subtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8, 0, 26, types.UnspecifiedLength}, + {"subtime(c_char, c_time_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_char, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_char, c_int_d)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_date, c_datetime)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_date, c_timestamp)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, + {"subtime(c_date, c_time)", mysql.TypeString, charset.CharsetUTF8MB4, 0, 26, types.UnspecifiedLength}, {"hour(c_int_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, {"hour(c_bigint_d )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 3, 0}, @@ -1624,39 +1624,39 @@ func (s *testInferTypeSuite) createTestCase4TimeFuncs() []typeInferTestCase { {"month(c_set )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 2, 0}, {"month(c_enum )", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, 2, 0}, - {"monthName(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"monthName(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - - {"dayName(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_char )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_binary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_set )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, - {"dayName(c_enum )", mysql.TypeVarString, charset.CharsetUTF8, 0, 10, types.UnspecifiedLength}, + {"monthName(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"monthName(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + + {"dayName(c_int_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_bigint_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_float_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_double_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_decimal )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_datetime )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_time_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_timestamp_d)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_char )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_varchar )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_text_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_binary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_varbinary )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_blob_d )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_set )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, + {"dayName(c_enum )", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 10, types.UnspecifiedLength}, {"now() ", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 19, 0}, {"now(0) ", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, 19, 0}, @@ -1851,14 +1851,14 @@ func (s *testInferTypeSuite) createTestCase4TimeFuncs() []typeInferTestCase { {"maketime(c_int_d, c_int_d, c_varchar)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 17, 6}, {"maketime(c_int_d, c_int_d, 1.2345)", mysql.TypeDuration, charset.CharsetBin, mysql.BinaryFlag, 15, 4}, - {"get_format(DATE, 'USA')", mysql.TypeVarString, charset.CharsetUTF8, 0, 17, types.UnspecifiedLength}, + {"get_format(DATE, 'USA')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 17, types.UnspecifiedLength}, {"convert_tz(c_time_d, c_text_d, c_text_d)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxDatetimeWidthWithFsp, types.MaxFsp}, {"from_unixtime(20170101.999)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxDatetimeWidthWithFsp, 3}, {"from_unixtime(20170101.1234567)", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxDatetimeWidthWithFsp, types.MaxFsp}, {"from_unixtime('20170101.999')", mysql.TypeDatetime, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxDatetimeWidthWithFsp, types.MaxFsp}, - {"from_unixtime(20170101.123, '%H')", mysql.TypeVarString, charset.CharsetUTF8, 0, 2, types.UnspecifiedLength}, + {"from_unixtime(20170101.123, '%H')", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 2, types.UnspecifiedLength}, {"extract(day from c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, {"extract(hour from c_char)", mysql.TypeLonglong, charset.CharsetBin, mysql.BinaryFlag, mysql.MaxIntWidth, 0}, @@ -1917,17 +1917,17 @@ func (s *testInferTypeSuite) createTestCase4Literals() []typeInferTestCase { func (s *testInferTypeSuite) createTestCase4JSONFuncs() []typeInferTestCase { return []typeInferTestCase{ - {"json_type(c_json)", mysql.TypeVarString, charset.CharsetUTF8, 0, 51, types.UnspecifiedLength}, + {"json_type(c_json)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 51, types.UnspecifiedLength}, // TODO: Flen of json_unquote doesn't follow MySQL now. - {"json_unquote(c_json)", mysql.TypeVarString, charset.CharsetUTF8, 0, 0, types.UnspecifiedLength}, - {"json_extract(c_json, '')", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_set(c_json, '', 0)", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_insert(c_json, '', 0)", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_replace(c_json, '', 0)", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_remove(c_json, '')", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_merge(c_json, c_json)", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_object('k', 'v')", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, - {"json_array('k', 'v')", mysql.TypeJSON, charset.CharsetUTF8, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_unquote(c_json)", mysql.TypeVarString, charset.CharsetUTF8MB4, 0, 0, types.UnspecifiedLength}, + {"json_extract(c_json, '')", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_set(c_json, '', 0)", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_insert(c_json, '', 0)", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_replace(c_json, '', 0)", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_remove(c_json, '')", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_merge(c_json, c_json)", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_object('k', 'v')", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, + {"json_array('k', 'v')", mysql.TypeJSON, charset.CharsetUTF8MB4, mysql.BinaryFlag, mysql.MaxBlobWidth, 0}, } } diff --git a/go.mod b/go.mod index b9bc33169b9c9..3906a25895fc2 100644 --- a/go.mod +++ b/go.mod @@ -1,75 +1,34 @@ module github.com/pingcap/tidb require ( - cloud.google.com/go v0.31.0 // indirect github.com/BurntSushi/toml v0.3.1 - github.com/Shopify/sarama v1.19.0 // indirect - github.com/Shopify/toxiproxy v2.1.3+incompatible // indirect - github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 // indirect github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/blacktear23/go-proxyprotocol v0.0.0-20171102103907-62e368e1c470 - github.com/chzyer/readline v0.0.0-20171208011716-f6d7a1f6fbf3 // indirect - github.com/cockroachdb/cmux v0.0.0-20170110192607-30d10be49292 // indirect github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect - github.com/coreos/bbolt v1.3.1-coreos.6 // indirect github.com/coreos/etcd v3.3.10+incompatible - github.com/coreos/go-semver v0.2.0 // indirect - github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 // indirect - github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect - github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4 // indirect github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7 - github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1 // indirect github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 - github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 // indirect - github.com/cznic/y v0.0.0-20170802143616-045f81c6662a // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/eapache/go-resiliency v1.1.0 // indirect - github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect - github.com/eapache/queue v0.0.0-20180227141424-093482f3f8ce // indirect - github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect github.com/etcd-io/gofail v0.0.0-20180808172546-51ce9a71510a - github.com/ghodss/yaml v1.0.0 // indirect github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4 github.com/gogo/protobuf v1.1.1 // indirect - github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff // indirect - github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3 // indirect github.com/golang/protobuf v1.2.0 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c - github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.6.2 - github.com/gorilla/websocket v1.2.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/jonboulle/clockwork v0.1.0 // indirect github.com/juju/errors v0.0.0-20181012004132-a4583d0a56ea // indirect - github.com/juju/loggo v0.0.0-20180524022052-584905176618 // indirect - github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073 // indirect github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 - github.com/kr/pretty v0.1.0 // indirect - github.com/kr/pty v1.1.3 // indirect - github.com/mattn/go-shellwords v1.0.3 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/montanaflynn/stats v0.0.0-20180911141734-db72e6cae808 // indirect - github.com/myesui/uuid v1.0.0 // indirect - github.com/ngaut/log v0.0.0-20180314031856-b8e36e7ba5ac // indirect github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef - github.com/onsi/gomega v1.4.2 // indirect github.com/opentracing/basictracer-go v1.0.0 github.com/opentracing/opentracing-go v1.0.2 - github.com/petar/GoLLRB v0.0.0-20130427215148-53be0d36a84c // indirect - github.com/pierrec/lz4 v2.0.5+incompatible // indirect - github.com/pierrec/xxHash v0.1.1 // indirect github.com/pingcap/check v0.0.0-20171206051426-1c287c953996 - github.com/pingcap/errcode v0.0.0-20180921232412-a1a7271709d9 // indirect github.com/pingcap/errors v0.11.0 github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e - github.com/pingcap/kvproto v0.0.0-20181028030329-855d2192cdc7 - github.com/pingcap/parser v0.0.0-20181102070703-4acd198f5092 + github.com/pingcap/kvproto v0.0.0-20181105061835-1b5d69cd1d26 + github.com/pingcap/parser v0.0.0-20181108112017-a63108d7da4c github.com/pingcap/pd v2.1.0-rc.4+incompatible github.com/pingcap/tidb-tools v0.0.0-20181101090416-cfac1096162e github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 @@ -78,44 +37,14 @@ require ( github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d // indirect - github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect github.com/sirupsen/logrus v1.2.0 - github.com/soheilhy/cmux v0.1.4 // indirect github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 - github.com/spf13/cobra v0.0.2 // indirect - github.com/spf13/pflag v1.0.1 // indirect - github.com/syndtr/goleveldb v0.0.0-20180815032940-ae2bd5eed72d // indirect - github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect github.com/twinj/uuid v1.0.0 - github.com/uber-go/atomic v1.3.2 // indirect github.com/uber/jaeger-client-go v2.15.0+incompatible github.com/uber/jaeger-lib v1.5.0 // indirect - github.com/ugorji/go v1.1.1 // indirect - github.com/unrolled/render v0.0.0-20180914162206-b9786414de4d // indirect - github.com/urfave/negroni v1.0.0 // indirect - github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect - go.uber.org/atomic v1.3.2 // indirect - go.uber.org/multierr v1.1.0 // indirect - go.uber.org/zap v1.8.0 // indirect - golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 // indirect - golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 // indirect golang.org/x/net v0.0.0-20181029044818-c44066c5c816 - golang.org/x/oauth2 v0.0.0-20181031022657-8527f56f7107 // indirect - golang.org/x/sys v0.0.0-20181031143558-9b800f95dbbc // indirect golang.org/x/text v0.3.0 - golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect - golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563 // indirect - google.golang.org/appengine v1.2.0 // indirect - google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 // indirect google.golang.org/grpc v1.16.0 - gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect - gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/stretchr/testify.v1 v1.2.2 // indirect - honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3 // indirect ) diff --git a/go.sum b/go.sum index 7e6637029312b..02271d68a0a68 100644 --- a/go.sum +++ b/go.sum @@ -189,10 +189,14 @@ github.com/pingcap/kvproto v0.0.0-20180930052200-fae11119f066 h1:ulo0ph8sxCzY3GY github.com/pingcap/kvproto v0.0.0-20180930052200-fae11119f066/go.mod h1:0gwbe1F2iBIjuQ9AH0DbQhL+Dpr5GofU8fgYyXk+ykk= github.com/pingcap/kvproto v0.0.0-20181028030329-855d2192cdc7 h1:CYssSnPvf90ZSbFdZpsZGSI7y+drG1EfKxqTOnKnHb0= github.com/pingcap/kvproto v0.0.0-20181028030329-855d2192cdc7/go.mod h1:0gwbe1F2iBIjuQ9AH0DbQhL+Dpr5GofU8fgYyXk+ykk= +github.com/pingcap/kvproto v0.0.0-20181105061835-1b5d69cd1d26 h1:JK4VLNYbSn36QSbCnqALi2ySXdH0DfcMssT/zmLf4Ls= +github.com/pingcap/kvproto v0.0.0-20181105061835-1b5d69cd1d26/go.mod h1:0gwbe1F2iBIjuQ9AH0DbQhL+Dpr5GofU8fgYyXk+ykk= github.com/pingcap/parser v0.0.0-20181024082006-53ac409ed043 h1:P9Osi8lei5j2fiRgsBi2Wch7qe4a3yWUOsS5vSan/JU= github.com/pingcap/parser v0.0.0-20181024082006-53ac409ed043/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/parser v0.0.0-20181102070703-4acd198f5092 h1:vGjjf7fhuaO9udn6QEFzvsNJDwVxFmdJvIJhCdCNe/E= github.com/pingcap/parser v0.0.0-20181102070703-4acd198f5092/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v0.0.0-20181108112017-a63108d7da4c h1:qXcJU4FwcXUV4zN8Cg2Lht4LkJDFK+Tn9dGDJn6nAsw= +github.com/pingcap/parser v0.0.0-20181108112017-a63108d7da4c/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/pd v0.0.0-20180619050643-0ec6ffcf94e8/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/pd v0.0.0-20181015053559-eb892dda1e33 h1:UQKEV9u9PR1RjFiJqqGijsHAEzTDZe89xdgB4Lj9S8Y= github.com/pingcap/pd v0.0.0-20181015053559-eb892dda1e33/go.mod h1:+bFEXnol47I8RhLpb0RbP5ZvpG6Ca7dw0LL/ARsmRk0= @@ -200,6 +204,7 @@ github.com/pingcap/pd v2.0.5+incompatible h1:nUTetzzM/tLbd03DoMB76AAvI57oYftNib9 github.com/pingcap/pd v2.0.5+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE= github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= +github.com/pingcap/tidb v0.0.0-20181105182855-379ee5b1915a/go.mod h1:tq1TVnaDUrh46KbB+oJA34Ob3eMbinTopWVzhX5Rj94= github.com/pingcap/tidb v2.0.8+incompatible/go.mod h1:I8C6jrPINP2rrVunTRd7C9fRRhQrtR43S1/CL5ix/yQ= github.com/pingcap/tidb-tools v0.0.0-20181025073300-5db58e3b7e66 h1:auCqWgjgklL7o7c3Pj5PaSOAd9hBZd3OwEyvGSEStnA= github.com/pingcap/tidb-tools v0.0.0-20181025073300-5db58e3b7e66/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= @@ -292,6 +297,8 @@ github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4MEFmbnK4h3BD7AUmskWv2+EeZJCCs= github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/zimulala/parser v0.0.0-20181108115726-650caa67be64 h1:QYGszvB1bmVsjvH6Id0AAnYXK9KUzSlbD8sbfaSf+Rw= +github.com/zimulala/parser v0.0.0-20181108115726-650caa67be64/go.mod h1:G69sWhUqZ6QkeOjLFa/d9eJsBxH7Y/JhkqOyvz1gZ3M= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -301,6 +308,7 @@ go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20150218234220-1351f936d976/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180503215945-1f94bef427e3 h1:+U/hI4i24Enhs+2BEMCN7wg16uT6entqa2mTg/PUKB8= golang.org/x/crypto v0.0.0-20180503215945-1f94bef427e3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -322,6 +330,7 @@ golang.org/x/sys v0.0.0-20180501092740-78d5f264b493/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180510032850-7dfd1290c791 h1:fFK3O68ywqxh9kSJokoTlNlK0NSLZ66TYEOB+yqcOVs= golang.org/x/sys v0.0.0-20180510032850-7dfd1290c791/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0 h1:biUuj9O+0+XckRUCDzjoOGm6yFV5c0IHbm1ODP3e4Zw= @@ -344,6 +353,7 @@ google.golang.org/genproto v0.0.0-20170711235230-b0a3dcfcd1a9/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20170815003206-6b7d9516179c/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4 h1:0rk3/gV3HbvCeUzVMhdxV3TEVKMVPDnayjN7sYRmcxY= google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/infoschema/tables.go b/infoschema/tables.go index 24161a2966a75..a346468a05637 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -82,8 +82,8 @@ func buildColumnInfo(tableName string, col columnInfo) *model.ColumnInfo { mCollation := charset.CharsetBin mFlag := mysql.UnsignedFlag if col.tp == mysql.TypeVarchar || col.tp == mysql.TypeBlob { - mCharset = mysql.DefaultCharset - mCollation = mysql.DefaultCollationName + mCharset = charset.CharsetUTF8MB4 + mCollation = charset.CollationUTF8MB4 mFlag = col.flag } fieldType := types.FieldType{ @@ -881,7 +881,7 @@ func dataForTables(ctx sessionctx.Context, schemas []*model.DBInfo) ([][]types.D for _, table := range schema.Tables { collation := table.Collate if collation == "" { - collation = charset.CollationUTF8 + collation = mysql.DefaultCollationName } createTime := types.Time{ Time: types.FromGoTime(table.GetUpdateTime()), diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 7c9ded48def9e..dfe826ea595c4 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -168,12 +168,10 @@ func (s *testSuite) TestSchemataCharacterSet(c *C) { c.Assert(err, IsNil) defer do.Close() - // The default collation is still reported as utf8_bin - // But this is consistent with information_schema.tables -> table_collation tk := testkit.NewTestKit(c, store) tk.MustExec("CREATE DATABASE `foo` DEFAULT CHARACTER SET = 'utf8mb4'") tk.MustQuery("select default_character_set_name, default_collation_name FROM information_schema.SCHEMATA WHERE schema_name = 'foo'").Check( - testkit.Rows("utf8mb4 utf8_bin")) + testkit.Rows("utf8mb4 utf8mb4_bin")) } diff --git a/kv/buffer_store.go b/kv/buffer_store.go index 4854fe0a6eab7..950fd04b4c79f 100644 --- a/kv/buffer_store.go +++ b/kv/buffer_store.go @@ -74,26 +74,26 @@ func (s *BufferStore) Get(k Key) ([]byte, error) { return val, nil } -// Seek implements the Retriever interface. -func (s *BufferStore) Seek(k Key) (Iterator, error) { - bufferIt, err := s.MemBuffer.Seek(k) +// Iter implements the Retriever interface. +func (s *BufferStore) Iter(k Key, upperBound Key) (Iterator, error) { + bufferIt, err := s.MemBuffer.Iter(k, upperBound) if err != nil { return nil, errors.Trace(err) } - retrieverIt, err := s.r.Seek(k) + retrieverIt, err := s.r.Iter(k, upperBound) if err != nil { return nil, errors.Trace(err) } return NewUnionIter(bufferIt, retrieverIt, false) } -// SeekReverse implements the Retriever interface. -func (s *BufferStore) SeekReverse(k Key) (Iterator, error) { - bufferIt, err := s.MemBuffer.SeekReverse(k) +// IterReverse implements the Retriever interface. +func (s *BufferStore) IterReverse(k Key) (Iterator, error) { + bufferIt, err := s.MemBuffer.IterReverse(k) if err != nil { return nil, errors.Trace(err) } - retrieverIt, err := s.r.SeekReverse(k) + retrieverIt, err := s.r.IterReverse(k) if err != nil { return nil, errors.Trace(err) } diff --git a/kv/buffer_store_test.go b/kv/buffer_store_test.go index af84a9294cd24..1af716011c2a4 100644 --- a/kv/buffer_store_test.go +++ b/kv/buffer_store_test.go @@ -53,7 +53,7 @@ func (s testBufferStoreSuite) TestSaveTo(c *C) { err := bs.SaveTo(mutator) c.Check(err, IsNil) - iter, err := mutator.Seek(nil) + iter, err := mutator.Iter(nil, nil) c.Check(err, IsNil) for iter.Valid() { cmp := bytes.Compare(iter.Key(), iter.Value()) diff --git a/kv/kv.go b/kv/kv.go index 8c8a0830b208a..84e0ea8fd07f5 100644 --- a/kv/kv.go +++ b/kv/kv.go @@ -79,15 +79,17 @@ type Retriever interface { // Get gets the value for key k from kv store. // If corresponding kv pair does not exist, it returns nil and ErrNotExist. Get(k Key) ([]byte, error) - // Seek creates an Iterator positioned on the first entry that k <= entry's key. + // Iter creates an Iterator positioned on the first entry that k <= entry's key. // If such entry is not found, it returns an invalid Iterator with no error. + // It yields only keys that < upperBound. If upperBound is nil, it means the upperBound is unbounded. // The Iterator must be Closed after use. - Seek(k Key) (Iterator, error) + Iter(k Key, upperBound Key) (Iterator, error) - // SeekReverse creates a reversed Iterator positioned on the first entry which key is less than k. + // IterReverse creates a reversed Iterator positioned on the first entry which key is less than k. // The returned iterator will iterate from greater key to smaller key. // If k is nil, the returned iterator will be positioned at the last key. - SeekReverse(k Key) (Iterator, error) + // TODO: Add lower bound limit + IterReverse(k Key) (Iterator, error) } // Mutator is the interface wraps the basic Set and Delete methods. diff --git a/kv/mem_buffer_test.go b/kv/mem_buffer_test.go index 92807b528da2d..e7ec5a1f4f9e6 100644 --- a/kv/mem_buffer_test.go +++ b/kv/mem_buffer_test.go @@ -76,7 +76,7 @@ func valToStr(c *C, iter Iterator) string { func checkNewIterator(c *C, buffer MemBuffer) { for i := startIndex; i < testCount; i++ { val := encodeInt(i * indexStep) - iter, err := buffer.Seek(val) + iter, err := buffer.Iter(val, nil) c.Assert(err, IsNil) c.Assert([]byte(iter.Key()), BytesEquals, val) c.Assert(decodeInt([]byte(valToStr(c, iter))), Equals, i*indexStep) @@ -86,7 +86,7 @@ func checkNewIterator(c *C, buffer MemBuffer) { // Test iterator Next() for i := startIndex; i < testCount-1; i++ { val := encodeInt(i * indexStep) - iter, err := buffer.Seek(val) + iter, err := buffer.Iter(val, nil) c.Assert(err, IsNil) c.Assert([]byte(iter.Key()), BytesEquals, val) c.Assert(valToStr(c, iter), Equals, string(val)) @@ -102,7 +102,7 @@ func checkNewIterator(c *C, buffer MemBuffer) { } // Non exist and beyond maximum seek test - iter, err := buffer.Seek(encodeInt(testCount * indexStep)) + iter, err := buffer.Iter(encodeInt(testCount*indexStep), nil) c.Assert(err, IsNil) c.Assert(iter.Valid(), IsFalse) @@ -110,7 +110,7 @@ func checkNewIterator(c *C, buffer MemBuffer) { // it returns the smallest key that larger than the one we are seeking inBetween := encodeInt((testCount-1)*indexStep - 1) last := encodeInt((testCount - 1) * indexStep) - iter, err = buffer.Seek(inBetween) + iter, err = buffer.Iter(inBetween, nil) c.Assert(err, IsNil) c.Assert(iter.Valid(), IsTrue) c.Assert([]byte(iter.Key()), Not(BytesEquals), inBetween) @@ -140,7 +140,7 @@ func (s *testKVSuite) TestNewIterator(c *C) { defer testleak.AfterTest(c)() for _, buffer := range s.bs { // should be invalid - iter, err := buffer.Seek(nil) + iter, err := buffer.Iter(nil, nil) c.Assert(err, IsNil) c.Assert(iter.Valid(), IsFalse) @@ -155,7 +155,7 @@ func (s *testKVSuite) TestIterNextUntil(c *C) { buffer := NewMemDbBuffer(DefaultTxnMembufCap) insertData(c, buffer) - iter, err := buffer.Seek(nil) + iter, err := buffer.Iter(nil, nil) c.Assert(err, IsNil) err = NextUntil(iter, func(k Key) bool { @@ -168,7 +168,7 @@ func (s *testKVSuite) TestIterNextUntil(c *C) { func (s *testKVSuite) TestBasicNewIterator(c *C) { defer testleak.AfterTest(c)() for _, buffer := range s.bs { - it, err := buffer.Seek([]byte("2")) + it, err := buffer.Iter([]byte("2"), nil) c.Assert(err, IsNil) c.Assert(it.Valid(), IsFalse) } @@ -193,7 +193,7 @@ func (s *testKVSuite) TestNewIteratorMin(c *C) { } cnt := 0 - it, err := buffer.Seek(nil) + it, err := buffer.Iter(nil, nil) c.Assert(err, IsNil) for it.Valid() { cnt++ @@ -201,7 +201,7 @@ func (s *testKVSuite) TestNewIteratorMin(c *C) { } c.Assert(cnt, Equals, 6) - it, err = buffer.Seek([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000")) + it, err = buffer.Iter([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "DATA_test_main_db_tbl_tbl_test_record__00000000000000000001") } @@ -294,7 +294,7 @@ func benchIterator(b *testing.B, buffer MemBuffer) { } b.ResetTimer() for i := 0; i < b.N; i++ { - iter, err := buffer.Seek(nil) + iter, err := buffer.Iter(nil, nil) if err != nil { b.Error(err) } diff --git a/kv/memdb_buffer.go b/kv/memdb_buffer.go index 3066288f4146f..8579800e236c2 100644 --- a/kv/memdb_buffer.go +++ b/kv/memdb_buffer.go @@ -50,14 +50,10 @@ func NewMemDbBuffer(cap int) MemBuffer { } } -// Seek creates an Iterator. -func (m *memDbBuffer) Seek(k Key) (Iterator, error) { - var i Iterator - if k == nil { - i = &memDbIter{iter: m.db.NewIterator(&util.Range{}), reverse: false} - } else { - i = &memDbIter{iter: m.db.NewIterator(&util.Range{Start: []byte(k)}), reverse: false} - } +// Iter creates an Iterator. +func (m *memDbBuffer) Iter(k Key, upperBound Key) (Iterator, error) { + i := &memDbIter{iter: m.db.NewIterator(&util.Range{Start: []byte(k), Limit: []byte(upperBound)}), reverse: false} + err := i.Next() if err != nil { return nil, errors.Trace(err) @@ -69,7 +65,7 @@ func (m *memDbBuffer) SetCap(cap int) { } -func (m *memDbBuffer) SeekReverse(k Key) (Iterator, error) { +func (m *memDbBuffer) IterReverse(k Key) (Iterator, error) { var i *memDbIter if k == nil { i = &memDbIter{iter: m.db.NewIterator(&util.Range{}), reverse: true} @@ -161,7 +157,7 @@ func (i *memDbIter) Close() { // WalkMemBuffer iterates all buffered kv pairs in memBuf func WalkMemBuffer(memBuf MemBuffer, f func(k Key, v []byte) error) error { - iter, err := memBuf.Seek(nil) + iter, err := memBuf.Iter(nil, nil) if err != nil { return errors.Trace(err) } diff --git a/kv/mock.go b/kv/mock.go index 4dc7fafa490e0..a84618d264957 100644 --- a/kv/mock.go +++ b/kv/mock.go @@ -68,11 +68,11 @@ func (t *mockTxn) Get(k Key) ([]byte, error) { return nil, nil } -func (t *mockTxn) Seek(k Key) (Iterator, error) { +func (t *mockTxn) Iter(k Key, upperBound Key) (Iterator, error) { return nil, nil } -func (t *mockTxn) SeekReverse(k Key) (Iterator, error) { +func (t *mockTxn) IterReverse(k Key) (Iterator, error) { return nil, nil } @@ -211,10 +211,10 @@ func (s *mockSnapshot) BatchGet(keys []Key) (map[string][]byte, error) { return m, nil } -func (s *mockSnapshot) Seek(k Key) (Iterator, error) { - return s.store.Seek(k) +func (s *mockSnapshot) Iter(k Key, upperBound Key) (Iterator, error) { + return s.store.Iter(k, upperBound) } -func (s *mockSnapshot) SeekReverse(k Key) (Iterator, error) { - return s.store.SeekReverse(k) +func (s *mockSnapshot) IterReverse(k Key) (Iterator, error) { + return s.store.IterReverse(k) } diff --git a/kv/mock_test.go b/kv/mock_test.go index 93dc59490b4a4..2c1db84cf8a97 100644 --- a/kv/mock_test.go +++ b/kv/mock_test.go @@ -46,8 +46,8 @@ func (s testMockSuite) TestInterface(c *C) { if transaction.IsReadOnly() { transaction.Get(Key("lock")) transaction.Set(Key("lock"), []byte{}) - transaction.Seek(Key("lock")) - transaction.SeekReverse(Key("lock")) + transaction.Iter(Key("lock"), nil) + transaction.IterReverse(Key("lock")) } transaction.Commit(context.Background()) diff --git a/kv/union_store.go b/kv/union_store.go index 016f9f9d9d5d2..2af127f6a40c8 100644 --- a/kv/union_store.go +++ b/kv/union_store.go @@ -127,18 +127,18 @@ func (lmb *lazyMemBuffer) Delete(k Key) error { return lmb.mb.Delete(k) } -func (lmb *lazyMemBuffer) Seek(k Key) (Iterator, error) { +func (lmb *lazyMemBuffer) Iter(k Key, upperBound Key) (Iterator, error) { if lmb.mb == nil { return invalidIterator{}, nil } - return lmb.mb.Seek(k) + return lmb.mb.Iter(k, upperBound) } -func (lmb *lazyMemBuffer) SeekReverse(k Key) (Iterator, error) { +func (lmb *lazyMemBuffer) IterReverse(k Key) (Iterator, error) { if lmb.mb == nil { return invalidIterator{}, nil } - return lmb.mb.SeekReverse(k) + return lmb.mb.IterReverse(k) } func (lmb *lazyMemBuffer) Size() int { diff --git a/kv/union_store_test.go b/kv/union_store_test.go index c4e6c400f915a..3297b14f969bc 100644 --- a/kv/union_store_test.go +++ b/kv/union_store_test.go @@ -63,46 +63,46 @@ func (s *testUnionStoreSuite) TestSeek(c *C) { s.store.Set([]byte("2"), []byte("2")) s.store.Set([]byte("3"), []byte("3")) - iter, err := s.us.Seek(nil) + iter, err := s.us.Iter(nil, nil) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("1"), []byte("2"), []byte("3")}, [][]byte{[]byte("1"), []byte("2"), []byte("3")}) - iter, err = s.us.Seek([]byte("2")) + iter, err = s.us.Iter([]byte("2"), nil) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("2"), []byte("3")}, [][]byte{[]byte("2"), []byte("3")}) s.us.Set([]byte("4"), []byte("4")) - iter, err = s.us.Seek([]byte("2")) + iter, err = s.us.Iter([]byte("2"), nil) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("2"), []byte("3"), []byte("4")}, [][]byte{[]byte("2"), []byte("3"), []byte("4")}) s.us.Delete([]byte("3")) - iter, err = s.us.Seek([]byte("2")) + iter, err = s.us.Iter([]byte("2"), nil) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("2"), []byte("4")}, [][]byte{[]byte("2"), []byte("4")}) } -func (s *testUnionStoreSuite) TestSeekReverse(c *C) { +func (s *testUnionStoreSuite) TestIterReverse(c *C) { defer testleak.AfterTest(c)() s.store.Set([]byte("1"), []byte("1")) s.store.Set([]byte("2"), []byte("2")) s.store.Set([]byte("3"), []byte("3")) - iter, err := s.us.SeekReverse(nil) + iter, err := s.us.IterReverse(nil) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("3"), []byte("2"), []byte("1")}, [][]byte{[]byte("3"), []byte("2"), []byte("1")}) - iter, err = s.us.SeekReverse([]byte("3")) + iter, err = s.us.IterReverse([]byte("3")) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("2"), []byte("1")}, [][]byte{[]byte("2"), []byte("1")}) s.us.Set([]byte("0"), []byte("0")) - iter, err = s.us.SeekReverse([]byte("3")) + iter, err = s.us.IterReverse([]byte("3")) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("2"), []byte("1"), []byte("0")}, [][]byte{[]byte("2"), []byte("1"), []byte("0")}) s.us.Delete([]byte("1")) - iter, err = s.us.SeekReverse([]byte("3")) + iter, err = s.us.IterReverse([]byte("3")) c.Assert(err, IsNil) checkIterator(c, iter, [][]byte{[]byte("2"), []byte("0")}, [][]byte{[]byte("2"), []byte("0")}) } diff --git a/meta/autoid/autoid_test.go b/meta/autoid/autoid_test.go index fcec3999a0731..5b56967b546f3 100644 --- a/meta/autoid/autoid_test.go +++ b/meta/autoid/autoid_test.go @@ -69,7 +69,7 @@ func (*testSuite) TestT(c *C) { id, err = alloc.Alloc(1) c.Assert(err, IsNil) c.Assert(id, Equals, int64(2)) - id, err = alloc.Alloc(0) + _, err = alloc.Alloc(0) c.Assert(err, NotNil) globalAutoId, err = alloc.NextGlobalAutoID(1) c.Assert(err, IsNil) diff --git a/metrics/session.go b/metrics/session.go index cb024694a8549..4bfc98f486f37 100644 --- a/metrics/session.go +++ b/metrics/session.go @@ -62,14 +62,14 @@ var ( Subsystem: "session", Name: "retry_error_total", Help: "Counter of session retry error.", - }, []string{LblType}) + }, []string{LblSQLType, LblType}) TransactionCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: "tidb", Subsystem: "session", Name: "transaction_total", Help: "Counter of transactions.", - }, []string{LblType}) + }, []string{LblSQLType, LblType}) SessionRestrictedSQLCounter = prometheus.NewCounter( prometheus.CounterOpts{ @@ -86,7 +86,7 @@ var ( Name: "transaction_statement_num", Help: "Buckated histogram of statements count in each transaction.", Buckets: prometheus.ExponentialBuckets(1, 2, 12), - }, []string{LblType}) + }, []string{LblSQLType, LblType}) TransactionDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ @@ -95,7 +95,7 @@ var ( Name: "transaction_duration_seconds", Help: "Bucketed histogram of a transaction execution duration, including retry.", Buckets: prometheus.ExponentialBuckets(0.001, 2, 16), // range 1ms ~ 64s - }, []string{LblType}) + }, []string{LblSQLType, LblType}) ) // Label constants. diff --git a/owner/fail_test.go b/owner/fail_test.go index 8f7a4b34494f5..32e0c032d4b60 100644 --- a/owner/fail_test.go +++ b/owner/fail_test.go @@ -53,8 +53,10 @@ func (s *testSuite) SetUpSuite(c *C) { } func (s *testSuite) TearDownSuite(c *C) { - err := s.ln.Close() - c.Assert(err, IsNil) + if s.ln != nil { + err := s.ln.Close() + c.Assert(err, IsNil) + } } var ( diff --git a/planner/core/common_plans.go b/planner/core/common_plans.go index bf850652505d9..bdb3e49eee580 100644 --- a/planner/core/common_plans.go +++ b/planner/core/common_plans.go @@ -61,6 +61,12 @@ type ShowDDLJobQueries struct { JobIDs []int64 } +// ShowNextRowID is for showing the next global row ID. +type ShowNextRowID struct { + baseSchemaProducer + TableName *ast.TableName +} + // CheckTable is used for checking table data, built from the 'admin check table' statement. type CheckTable struct { baseSchemaProducer diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index 9c2bf2b054315..4aba634a193f9 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -196,18 +196,15 @@ func (er *expressionRewriter) constructBinaryOpFunction(l expression.Expression, return nil, errors.Trace(err) } } + if op == ast.NE { + return expression.ComposeDNFCondition(er.ctx, funcs...), nil + } return expression.ComposeCNFCondition(er.ctx, funcs...), nil default: larg0, rarg0 := expression.GetFuncArg(l, 0), expression.GetFuncArg(r, 0) var expr1, expr2, expr3 expression.Expression - if op == ast.LE || op == ast.GE { - expr1 = expression.NewFunctionInternal(er.ctx, op, types.NewFieldType(mysql.TypeTiny), larg0, rarg0) - expr1 = expression.NewFunctionInternal(er.ctx, ast.EQ, types.NewFieldType(mysql.TypeTiny), expr1, expression.Zero) - expr2 = expression.Zero - } else if op == ast.LT || op == ast.GT { - expr1 = expression.NewFunctionInternal(er.ctx, ast.NE, types.NewFieldType(mysql.TypeTiny), larg0, rarg0) - expr2 = expression.NewFunctionInternal(er.ctx, op, types.NewFieldType(mysql.TypeTiny), larg0, rarg0) - } + expr1 = expression.NewFunctionInternal(er.ctx, ast.NE, types.NewFieldType(mysql.TypeTiny), larg0, rarg0) + expr2 = expression.NewFunctionInternal(er.ctx, op, types.NewFieldType(mysql.TypeTiny), larg0, rarg0) var err error l, err = expression.PopRowFirstArg(er.ctx, l) if err != nil { @@ -217,6 +214,18 @@ func (er *expressionRewriter) constructBinaryOpFunction(l expression.Expression, if err != nil { return nil, errors.Trace(err) } + if evalexpr, ok := expr1.(*expression.Constant); ok { + _, isNull, err1 := evalexpr.EvalInt(er.ctx, chunk.Row{}) + if err1 != nil || isNull { + return expr1, err1 + } + } + if evalexpr, ok := expr2.(*expression.Constant); ok { + _, isNull, err1 := evalexpr.EvalInt(er.ctx, chunk.Row{}) + if err1 != nil || isNull { + return expr2, err1 + } + } expr3, err = er.constructBinaryOpFunction(l, r, op) if err != nil { return nil, errors.Trace(err) @@ -472,10 +481,10 @@ func (er *expressionRewriter) buildQuantifierPlan(plan4Agg *LogicalAggregation, proj.SetSchema(expression.NewSchema(joinSchema.Clone().Columns[:outerSchemaLen]...)) proj.Exprs = append(proj.Exprs, cond) proj.schema.Append(&expression.Column{ - ColName: model.NewCIStr("aux_col"), - UniqueID: er.ctx.GetSessionVars().AllocPlanColumnID(), - IsAggOrSubq: true, - RetType: cond.GetType(), + ColName: model.NewCIStr("aux_col"), + UniqueID: er.ctx.GetSessionVars().AllocPlanColumnID(), + IsReferenced: true, + RetType: cond.GetType(), }) proj.SetChildren(er.p) er.p = proj @@ -647,7 +656,7 @@ func (er *expressionRewriter) handleInSubquery(v *ast.PatternInExpr) (ast.Node, // Build distinct for the inner query. agg := er.b.buildDistinct(np, np.Schema().Len()) for _, col := range agg.schema.Columns { - col.IsAggOrSubq = true + col.IsReferenced = true } eq, left, right, other := extractOnCondition(expression.SplitCNFItems(checkCondition), er.p, agg, false, false) // Build inner join above the aggregation. @@ -1164,12 +1173,14 @@ func (er *expressionRewriter) rewriteFuncCall(v *ast.FuncCallExpr) bool { } stackLen := len(er.ctxStack) arg1 := er.ctxStack[stackLen-2] - newArg1, isColumn := arg1.(*expression.Column) + col, isColumn := arg1.(*expression.Column) // if expr1 is a column and column has not null flag, then we can eliminate ifnull on // this column. - if isColumn && mysql.HasNotNullFlag(newArg1.RetType.Flag) { + if isColumn && mysql.HasNotNullFlag(col.RetType.Flag) { + newCol := col.Clone().(*expression.Column) + newCol.IsReferenced = true er.ctxStack = er.ctxStack[:stackLen-len(v.Args)] - er.ctxStack = append(er.ctxStack, newArg1) + er.ctxStack = append(er.ctxStack, newCol) return true } diff --git a/planner/core/expression_rewriter_test.go b/planner/core/expression_rewriter_test.go new file mode 100644 index 0000000000000..9d10a34150a29 --- /dev/null +++ b/planner/core/expression_rewriter_test.go @@ -0,0 +1,43 @@ +// Copyright 2018 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 core_test + +import ( + . "github.com/pingcap/check" + "github.com/pingcap/tidb/util/testkit" + "github.com/pingcap/tidb/util/testleak" +) + +var _ = Suite(&testExpressionRewriterSuite{}) + +type testExpressionRewriterSuite struct { +} + +func (s *testExpressionRewriterSuite) TestIfNullEliminateColName(c *C) { + defer testleak.AfterTest(c)() + store, dom, err := newStoreWithBootstrap() + c.Assert(err, IsNil) + tk := testkit.NewTestKit(c, store) + defer func() { + dom.Close() + store.Close() + }() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int not null, b int not null)") + rs, err := tk.Exec("select ifnull(a,b) from t") + c.Assert(err, IsNil) + fields := rs.Fields() + c.Assert(fields[0].Column.Name.L, Equals, "ifnull(a,b)") +} diff --git a/planner/core/expression_test.go b/planner/core/expression_test.go index 21bd952f0ed9d..232afd3a62d70 100644 --- a/planner/core/expression_test.go +++ b/planner/core/expression_test.go @@ -218,6 +218,49 @@ func (s *testExpressionSuite) TestIsNull(c *C) { s.runTests(c, tests) } +func (s *testExpressionSuite) TestCompareRow(c *C) { + defer testleak.AfterTest(c)() + tests := []testCase{ + { + exprStr: "row(1,2,3)=row(1,2,3)", + resultStr: "1", + }, + { + exprStr: "row(1,2,3)=row(1+3,2,3)", + resultStr: "0", + }, + { + exprStr: "row(1,2,3)<>row(1,2,3)", + resultStr: "0", + }, + { + exprStr: "row(1,2,3)<>row(1+3,2,3)", + resultStr: "1", + }, + { + exprStr: "row(1+3,2,3)<>row(1+3,2,3)", + resultStr: "0", + }, + { + exprStr: "row(1,2,3)", + }, + { + exprStr: "row(1,2,3)=row(0,NULL,3)", + resultStr: "1", + }, + { + exprStr: "row(1,2,3)<=row(2,NULL,3)", + resultStr: "1", + }, + } + s.runTests(c, tests) +} + func (s *testExpressionSuite) TestIsTruth(c *C) { defer testleak.AfterTest(c)() tests := []testCase{ diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index 784f4d2b91562..f35ba0a2e3e5b 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -108,10 +108,10 @@ func (b *PlanBuilder) buildAggregation(p LogicalPlan, aggFuncList []*ast.Aggrega aggIndexMap[i] = position plan4Agg.AggFuncs = append(plan4Agg.AggFuncs, newFunc) schema4Agg.Append(&expression.Column{ - ColName: model.NewCIStr(fmt.Sprintf("%d_col_%d", plan4Agg.id, position)), - UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), - IsAggOrSubq: true, - RetType: newFunc.RetTp, + ColName: model.NewCIStr(fmt.Sprintf("%d_col_%d", plan4Agg.id, position)), + UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), + IsReferenced: true, + RetType: newFunc.RetTp, }) } } @@ -309,9 +309,13 @@ func (b *PlanBuilder) buildJoin(joinNode *ast.Join) (LogicalPlan, error) { // Set join type. switch joinNode.Tp { case ast.LeftJoin: + // left outer join need to be checked elimination + b.optFlag = b.optFlag | flagEliminateOuterJoin joinPlan.JoinType = LeftOuterJoin resetNotNullFlag(joinPlan.schema, leftPlan.Schema().Len(), joinPlan.schema.Len()) case ast.RightJoin: + // right outer join need to be checked elimination + b.optFlag = b.optFlag | flagEliminateOuterJoin joinPlan.JoinType = RightOuterJoin resetNotNullFlag(joinPlan.schema, 0, leftPlan.Schema().Len()) default: @@ -570,7 +574,7 @@ func (b *PlanBuilder) buildProjectionFieldNameFromExpressions(field *ast.SelectF // buildProjectionField builds the field object according to SelectField in projection. func (b *PlanBuilder) buildProjectionField(id, position int, field *ast.SelectField, expr expression.Expression) *expression.Column { var origTblName, tblName, origColName, colName, dbName model.CIStr - if c, ok := expr.(*expression.Column); ok && !c.IsAggOrSubq { + if c, ok := expr.(*expression.Column); ok && !c.IsReferenced { // Field is a column reference. colName, origColName, tblName, origTblName, dbName = b.buildProjectionFieldNameFromColumns(field, c) } else if field.AsName.L != "" { @@ -710,7 +714,8 @@ func (b *PlanBuilder) buildUnion(union *ast.UnionStmt) (LogicalPlan, error) { if unionDistinctPlan != nil { unionDistinctPlan = b.buildDistinct(unionDistinctPlan, unionDistinctPlan.Schema().Len()) if len(allSelectPlans) > 0 { - allSelectPlans = append(allSelectPlans, unionDistinctPlan) + // Can't change the statements order in order to get the correct column info. + allSelectPlans = append([]LogicalPlan{unionDistinctPlan}, allSelectPlans...) } } @@ -1959,7 +1964,7 @@ func (b *PlanBuilder) buildApplyWithJoinType(outerPlan, innerPlan LogicalPlan, t ap.SetChildren(outerPlan, innerPlan) ap.SetSchema(expression.MergeSchema(outerPlan.Schema(), innerPlan.Schema())) for i := outerPlan.Schema().Len(); i < ap.Schema().Len(); i++ { - ap.schema.Columns[i].IsAggOrSubq = true + ap.schema.Columns[i].IsReferenced = true } return ap } @@ -1997,10 +2002,10 @@ func (b *PlanBuilder) buildSemiJoin(outerPlan, innerPlan LogicalPlan, onConditio if asScalar { newSchema := outerPlan.Schema().Clone() newSchema.Append(&expression.Column{ - ColName: model.NewCIStr(fmt.Sprintf("%d_aux_0", joinPlan.id)), - RetType: types.NewFieldType(mysql.TypeTiny), - IsAggOrSubq: true, - UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), + ColName: model.NewCIStr(fmt.Sprintf("%d_aux_0", joinPlan.id)), + RetType: types.NewFieldType(mysql.TypeTiny), + IsReferenced: true, + UniqueID: b.ctx.GetSessionVars().AllocPlanColumnID(), }) joinPlan.SetSchema(newSchema) if not { diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index 860ad68dc0e6f..e65d9a8811348 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -1811,7 +1811,7 @@ func (s *testPlanSuite) TestUnion(c *C) { }, { sql: "select a from t union select a from t union all select a from t", - best: "UnionAll{DataScan(t)->Projection->UnionAll{DataScan(t)->Projection->DataScan(t)->Projection}->Aggr(firstrow(t.a))->Projection}", + best: "UnionAll{UnionAll{DataScan(t)->Projection->DataScan(t)->Projection}->Aggr(firstrow(t.a))->Projection->DataScan(t)->Projection}", err: false, }, { @@ -1824,6 +1824,11 @@ func (s *testPlanSuite) TestUnion(c *C) { best: "", err: true, }, + { + sql: "select * from (select 1 as a union select 1 union all select 2) t order by a", + best: "UnionAll{UnionAll{Dual->Projection->Projection->Dual->Projection->Projection}->Aggr(firstrow(a))->Projection->Dual->Projection->Projection}->Projection->Sort", + err: false, + }, } for i, tt := range tests { comment := Commentf("case:%v sql:%s", i, tt.sql) @@ -1838,7 +1843,7 @@ func (s *testPlanSuite) TestUnion(c *C) { plan, err := builder.Build(stmt) if tt.err { c.Assert(err, NotNil) - return + continue } c.Assert(err, IsNil) p := plan.(LogicalPlan) @@ -2021,3 +2026,67 @@ func (s *testPlanSuite) TestNameResolver(c *C) { } } } + +func (s *testPlanSuite) TestOuterJoinEliminator(c *C) { + defer testleak.AfterTest(c)() + tests := []struct { + sql string + best string + }{ + // Test left outer join + distinct + { + sql: "select distinct t1.a, t1.b from t t1 left outer join t t2 on t1.b = t2.b", + best: "DataScan(t1)->Aggr(firstrow(t1.a),firstrow(t1.b))", + }, + // Test right outer join + distinct + { + sql: "select distinct t2.a, t2.b from t t1 right outer join t t2 on t1.b = t2.b", + best: "DataScan(t2)->Aggr(firstrow(t2.a),firstrow(t2.b))", + }, + // Test duplicate agnostic agg functions on join + { + sql: "select max(t1.a), min(t1.b) from t t1 left join t t2 on t1.b = t2.b", + best: "DataScan(t1)->Aggr(max(t1.a),min(t1.b))->Projection", + }, + { + sql: "select sum(distinct t1.a) from t t1 left join t t2 on t1.a = t2.a and t1.b = t2.b", + best: "DataScan(t1)->Aggr(sum(t1.a))->Projection", + }, + { + sql: "select count(distinct t1.a, t1.b) from t t1 left join t t2 on t1.b = t2.b", + best: "DataScan(t1)->Aggr(count(t1.a, t1.b))->Projection", + }, + // Test left outer join + { + sql: "select t1.b from t t1 left outer join t t2 on t1.a = t2.a", + best: "DataScan(t1)->Projection", + }, + // Test right outer join + { + sql: "select t2.b from t t1 right outer join t t2 on t1.a = t2.a", + best: "DataScan(t2)->Projection", + }, + // For complex join query + { + sql: "select max(t3.b) from (t t1 left join t t2 on t1.a = t2.a) right join t t3 on t1.b = t3.b", + best: "DataScan(t3)->TopN([t3.b true],0,1)->Aggr(max(t3.b))->Projection", + }, + } + + for i, tt := range tests { + comment := Commentf("case:%v sql:%s", i, tt.sql) + stmt, err := s.ParseOneStmt(tt.sql, "", "") + c.Assert(err, IsNil, comment) + Preprocess(s.ctx, stmt, s.is, false) + builder := &PlanBuilder{ + ctx: mockContext(), + is: s.is, + colMapper: make(map[*ast.ColumnNameExpr]int), + } + p, err := builder.Build(stmt) + c.Assert(err, IsNil) + p, err = logicalOptimize(builder.optFlag, p.(LogicalPlan)) + c.Assert(err, IsNil) + c.Assert(ToString(p), Equals, tt.best, comment) + } +} diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index 305aa57eddb35..3065ebb1891d7 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -39,6 +39,7 @@ const ( flagEliminateProjection flagMaxMinEliminate flagPredicatePushDown + flagEliminateOuterJoin flagPartitionProcessor flagPushDownAgg flagPushDownTopN @@ -52,6 +53,7 @@ var optRuleList = []logicalOptRule{ &projectionEliminater{}, &maxMinEliminator{}, &ppdSolver{}, + &outerJoinEliminator{}, &partitionProcessor{}, &aggregationPushDownSolver{}, &pushDownTopNOptimizer{}, diff --git a/planner/core/plan_to_pb_test.go b/planner/core/plan_to_pb_test.go index fd1ff48291a70..c42b921aa9fdc 100644 --- a/planner/core/plan_to_pb_test.go +++ b/planner/core/plan_to_pb_test.go @@ -37,7 +37,7 @@ func (s *testDistsqlSuite) TestColumnToProto(c *C) { FieldType: *tp, } pc := model.ColumnToProto(col) - expect := &tipb.ColumnInfo{ColumnId: 0, Tp: 3, Collation: 83, ColumnLen: -1, Decimal: -1, Flag: 10, Elems: []string(nil), DefaultVal: []uint8(nil), PkHandle: false, XXX_unrecognized: []uint8(nil)} + expect := &tipb.ColumnInfo{ColumnId: 0, Tp: 3, Collation: mysql.DefaultCollationID, ColumnLen: -1, Decimal: -1, Flag: 10, Elems: []string(nil), DefaultVal: []uint8(nil), PkHandle: false, XXX_unrecognized: []uint8(nil)} c.Assert(pc, DeepEquals, expect) cols := []*model.ColumnInfo{col, col} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 19ea97c150af4..d1934c86b5d40 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -20,6 +20,7 @@ import ( "github.com/cznic/mathutil" "github.com/pingcap/errors" "github.com/pingcap/parser/ast" + "github.com/pingcap/parser/charset" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" "github.com/pingcap/parser/opcode" @@ -502,6 +503,10 @@ func (b *PlanBuilder) buildAdmin(as *ast.AdminStmt) (Plan, error) { p := &ChecksumTable{Tables: as.Tables} p.SetSchema(buildChecksumTableSchema()) ret = p + case ast.AdminShowNextRowID: + p := &ShowNextRowID{TableName: as.Tables[0]} + p.SetSchema(buildShowNextRowID()) + ret = p case ast.AdminShowDDL: p := &ShowDDL{} p.SetSchema(buildShowDDLFields()) @@ -761,6 +766,15 @@ func (b *PlanBuilder) buildAnalyze(as *ast.AnalyzeTableStmt) (Plan, error) { return b.buildAnalyzeTable(as) } +func buildShowNextRowID() *expression.Schema { + schema := expression.NewSchema(make([]*expression.Column, 0, 4)...) + schema.Append(buildColumn("", "DB_NAME", mysql.TypeVarchar, mysql.MaxDatabaseNameLength)) + schema.Append(buildColumn("", "TABLE_NAME", mysql.TypeVarchar, mysql.MaxTableNameLength)) + schema.Append(buildColumn("", "COLUMN_NAME", mysql.TypeVarchar, mysql.MaxColumnNameLength)) + schema.Append(buildColumn("", "NEXT_GLOBAL_ROW_ID", mysql.TypeLonglong, 4)) + return schema +} + func buildShowDDLFields() *expression.Schema { schema := expression.NewSchema(make([]*expression.Column, 0, 4)...) schema.Append(buildColumn("", "SCHEMA_VER", mysql.TypeLonglong, 4)) @@ -839,8 +853,8 @@ func buildColumn(tableName, name string, tp byte, size int) *expression.Column { cs, cl := types.DefaultCharsetForType(tp) flag := mysql.UnsignedFlag if tp == mysql.TypeVarchar || tp == mysql.TypeBlob { - cs = mysql.DefaultCharset - cl = mysql.DefaultCollationName + cs = charset.CharsetUTF8MB4 + cl = charset.CollationUTF8MB4 flag = 0 } diff --git a/planner/core/rule_join_elimination.go b/planner/core/rule_join_elimination.go new file mode 100644 index 0000000000000..f6dd30834a701 --- /dev/null +++ b/planner/core/rule_join_elimination.go @@ -0,0 +1,192 @@ +// Copyright 2018 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 core + +import ( + "github.com/pingcap/parser/ast" + "github.com/pingcap/tidb/expression" +) + +type outerJoinEliminator struct { +} + +// tryToEliminateOuterJoin will eliminate outer join plan base on the following rules +// 1. outer join elimination: For example left outer join, if the parent only use the +// columns from left table and the join key of right table(the inner table) is a unique +// key of the right table. the left outer join can be eliminated. +// 2. outer join elimination with duplicate agnostic aggregate functions: For example left outer join. +// If the parent only use the columns from left table with 'distinct' label. The left outer join can +// be eliminated. +func (o *outerJoinEliminator) tryToEliminateOuterJoin(p *LogicalJoin, aggCols []*expression.Column, parentSchema *expression.Schema) LogicalPlan { + var innerChildIdx int + switch p.JoinType { + case LeftOuterJoin: + innerChildIdx = 1 + case RightOuterJoin: + innerChildIdx = 0 + default: + return p + } + + outerPlan := p.children[1^innerChildIdx] + innerPlan := p.children[innerChildIdx] + // outer join elimination with duplicate agnostic aggregate functions + if o.isAggColsAllFromOuterTable(outerPlan, aggCols) { + return outerPlan + } + // outer join elimination without duplicate agnostic aggregate functions + if !o.isParentColsAllFromOuterTable(outerPlan, parentSchema) { + return p + } + innerJoinKeys := o.extractInnerJoinKeys(p, innerChildIdx) + if o.isInnerJoinKeysContainUniqueKey(innerPlan, innerJoinKeys) { + return outerPlan + } + if o.isInnerJoinKeysContainIndex(innerPlan, innerJoinKeys) { + return outerPlan + } + return p +} + +// extract join keys as a schema for inner child of a outer join +func (o *outerJoinEliminator) extractInnerJoinKeys(join *LogicalJoin, innerChildIdx int) *expression.Schema { + var joinKeys []*expression.Column + for _, eqCond := range join.EqualConditions { + joinKeys = append(joinKeys, eqCond.GetArgs()[innerChildIdx].(*expression.Column)) + } + return expression.NewSchema(joinKeys...) +} + +func (o *outerJoinEliminator) isAggColsAllFromOuterTable(outerPlan LogicalPlan, aggCols []*expression.Column) bool { + if len(aggCols) == 0 { + return false + } + for _, col := range aggCols { + columnName := &ast.ColumnName{Schema: col.DBName, Table: col.TblName, Name: col.ColName} + if c, _ := outerPlan.Schema().FindColumn(columnName); c == nil { + return false + } + } + return true +} + +// check whether schema cols of join's parent plan are all from outer join table +func (o *outerJoinEliminator) isParentColsAllFromOuterTable(outerPlan LogicalPlan, parentSchema *expression.Schema) bool { + if parentSchema == nil { + return false + } + for _, col := range parentSchema.Columns { + columnName := &ast.ColumnName{Schema: col.DBName, Table: col.TblName, Name: col.ColName} + if c, _ := outerPlan.Schema().FindColumn(columnName); c == nil { + return false + } + } + return true +} + +// check whether one of unique keys sets is contained by inner join keys +func (o *outerJoinEliminator) isInnerJoinKeysContainUniqueKey(innerPlan LogicalPlan, joinKeys *expression.Schema) bool { + for _, keyInfo := range innerPlan.Schema().Keys { + joinKeysContainKeyInfo := true + for _, col := range keyInfo { + columnName := &ast.ColumnName{Schema: col.DBName, Table: col.TblName, Name: col.ColName} + if c, _ := joinKeys.FindColumn(columnName); c == nil { + joinKeysContainKeyInfo = false + break + } + } + if joinKeysContainKeyInfo { + return true + } + } + return false +} + +// check whether one of index sets is contained by inner join index +func (o *outerJoinEliminator) isInnerJoinKeysContainIndex(innerPlan LogicalPlan, joinKeys *expression.Schema) bool { + ds, ok := innerPlan.(*DataSource) + if !ok { + return false + } + for _, path := range ds.possibleAccessPaths { + if path.isTablePath { + continue + } + idx := path.index + if !idx.Unique { + continue + } + joinKeysContainIndex := true + for _, idxCol := range idx.Columns { + columnName := &ast.ColumnName{Schema: ds.DBName, Table: ds.tableInfo.Name, Name: idxCol.Name} + if c, _ := joinKeys.FindColumn(columnName); c == nil { + joinKeysContainIndex = false + break + } + } + if joinKeysContainIndex { + return true + } + } + return false +} + +// Check whether a LogicalPlan is a LogicalAggregation and its all aggregate functions is duplicate agnostic. +// Also, check all the args are expression.Column. +func (o *outerJoinEliminator) isDuplicateAgnosticAgg(p LogicalPlan) (_ bool, cols []*expression.Column) { + agg, ok := p.(*LogicalAggregation) + if !ok { + return false, nil + } + cols = agg.groupByCols + for _, aggDesc := range agg.AggFuncs { + if !aggDesc.HasDistinct && + aggDesc.Name != ast.AggFuncFirstRow && + aggDesc.Name != ast.AggFuncMax && + aggDesc.Name != ast.AggFuncMin { + return false, nil + } + for _, expr := range aggDesc.Args { + if col, ok := expr.(*expression.Column); ok { + cols = append(cols, col) + } else { + return false, nil + } + } + } + return true, cols +} + +func (o *outerJoinEliminator) doOptimize(p LogicalPlan, aggCols []*expression.Column, parentSchema *expression.Schema) LogicalPlan { + // check the duplicate agnostic aggregate functions + if ok, newCols := o.isDuplicateAgnosticAgg(p); ok { + aggCols = newCols + } + + newChildren := make([]LogicalPlan, 0, len(p.Children())) + for _, child := range p.Children() { + newChild := o.doOptimize(child, aggCols, p.Schema()) + newChildren = append(newChildren, newChild) + } + p.SetChildren(newChildren...) + join, isJoin := p.(*LogicalJoin) + if !isJoin { + return p + } + return o.tryToEliminateOuterJoin(join, aggCols, parentSchema) +} + +func (o *outerJoinEliminator) optimize(p LogicalPlan) (LogicalPlan, error) { + return o.doOptimize(p, nil, nil), nil +} diff --git a/privilege/privileges/errors.go b/privilege/privileges/errors.go new file mode 100644 index 0000000000000..4585009e326ab --- /dev/null +++ b/privilege/privileges/errors.go @@ -0,0 +1,39 @@ +// Copyright 2018 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 privileges + +import ( + "github.com/pingcap/parser/mysql" + "github.com/pingcap/parser/terror" +) + +// privilege error codes. +const ( + codeInvalidPrivilegeType terror.ErrCode = 1 + codeInvalidUserNameFormat = 2 + codeErrNonexistingGrant = mysql.ErrNonexistingGrant +) + +var ( + errInvalidPrivilegeType = terror.ClassPrivilege.New(codeInvalidPrivilegeType, "unknown privilege type") + errInvalidUserNameFormat = terror.ClassPrivilege.New(codeInvalidUserNameFormat, "wrong username format") + errNonexistingGrant = terror.ClassPrivilege.New(codeErrNonexistingGrant, mysql.MySQLErrName[mysql.ErrNonexistingGrant]) +) + +func init() { + privilegeMySQLErrCodes := map[terror.ErrCode]uint16{ + codeErrNonexistingGrant: mysql.ErrNonexistingGrant, + } + terror.ErrClassToMySQLCodes[terror.ClassPrivilege] = privilegeMySQLErrCodes +} diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index d131518635c27..a4ee42fae8d9f 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -18,7 +18,6 @@ import ( "github.com/pingcap/parser/auth" "github.com/pingcap/parser/mysql" - "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" @@ -28,17 +27,6 @@ import ( // SkipWithGrant causes the server to start without using the privilege system at all. var SkipWithGrant = false -// privilege error codes. -const ( - codeInvalidPrivilegeType terror.ErrCode = 1 - codeInvalidUserNameFormat = 2 -) - -var ( - errInvalidPrivilegeType = terror.ClassPrivilege.New(codeInvalidPrivilegeType, "unknown privilege type") - errInvalidUserNameFormat = terror.ClassPrivilege.New(codeInvalidUserNameFormat, "wrong username format") -) - var _ privilege.Manager = (*UserPrivileges)(nil) // UserPrivileges implements privilege.Manager interface. @@ -139,10 +127,18 @@ func (p *UserPrivileges) UserPrivilegesTable() [][]types.Datum { } // ShowGrants implements privilege.Manager ShowGrants interface. -func (p *UserPrivileges) ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity) ([]string, error) { +func (p *UserPrivileges) ShowGrants(ctx sessionctx.Context, user *auth.UserIdentity) (grants []string, err error) { mysqlPrivilege := p.Handle.Get() + u := user.Username + h := user.Hostname if len(user.AuthUsername) > 0 && len(user.AuthHostname) > 0 { - return mysqlPrivilege.showGrants(user.AuthUsername, user.AuthHostname), nil + u = user.AuthUsername + h = user.AuthHostname } - return mysqlPrivilege.showGrants(user.Username, user.Hostname), nil + grants = mysqlPrivilege.showGrants(u, h) + if len(grants) == 0 { + err = errNonexistingGrant.GenWithStackByArgs(u, h) + } + + return } diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go index d00565505ad9a..48cebbe1bae9f 100644 --- a/privilege/privileges/privileges_test.go +++ b/privilege/privileges/privileges_test.go @@ -21,6 +21,7 @@ import ( . "github.com/pingcap/check" "github.com/pingcap/parser/auth" "github.com/pingcap/parser/mysql" + "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/privilege" @@ -239,9 +240,12 @@ func (s *testPrivilegeSuite) TestShowGrants(c *C) { ctx.GetSessionVars().User = &auth.UserIdentity{Username: "root", Hostname: "localhost"} mustExec(c, se, `DROP USER 'show'@'localhost'`) mustExec(c, se, `FLUSH PRIVILEGES;`) + + // This should now return an error gs, err = pc.ShowGrants(se, &auth.UserIdentity{Username: "show", Hostname: "localhost"}) - c.Assert(err, IsNil) - c.Assert(gs, HasLen, 0) + // cant show grants for non-existent + errNonexistingGrant := terror.ClassPrivilege.New(mysql.ErrNonexistingGrant, mysql.MySQLErrName[mysql.ErrNonexistingGrant]) + c.Assert(terror.ErrorEqual(err, errNonexistingGrant), IsTrue) } func (s *testPrivilegeSuite) TestDropTablePriv(c *C) { diff --git a/server/tidb_test.go b/server/tidb_test.go index a1713363d5e2c..7b93d982a93c8 100644 --- a/server/tidb_test.go +++ b/server/tidb_test.go @@ -513,7 +513,7 @@ func (ts *TidbTestSuite) TestFieldList(c *C) { case 10, 11, 12, 15, 16: // c_char char(20), c_varchar varchar(20), c_text_d text, // c_set set('a', 'b', 'c'), c_enum enum('a', 'b', 'c') - c.Assert(col.Charset, Equals, uint16(tmysql.CharsetIDs["utf8"]), Commentf("index %d", i)) + c.Assert(col.Charset, Equals, uint16(tmysql.CharsetIDs[tmysql.DefaultCharset]), Commentf("index %d", i)) continue } diff --git a/session/session.go b/session/session.go index c52d2fb71fda4..6bea4b1995408 100644 --- a/session/session.go +++ b/session/session.go @@ -349,10 +349,11 @@ func (s *session) doCommitWithRetry(ctx context.Context) error { err = s.retry(ctx, uint(maxRetryCount)) } } + label := s.getSQLLabel() counter := s.sessionVars.TxnCtx.StatementCount duration := time.Since(s.GetSessionVars().TxnCtx.CreateTime).Seconds() - metrics.StatementPerTransaction.WithLabelValues(metrics.RetLabel(err)).Observe(float64(counter)) - metrics.TransactionDuration.WithLabelValues(metrics.RetLabel(err)).Observe(float64(duration)) + metrics.StatementPerTransaction.WithLabelValues(label, metrics.RetLabel(err)).Observe(float64(counter)) + metrics.TransactionDuration.WithLabelValues(label, metrics.RetLabel(err)).Observe(float64(duration)) s.cleanRetryInfo() if isoLevelOneShot := &s.sessionVars.TxnIsolationLevelOneShot; isoLevelOneShot.State != 0 { @@ -390,7 +391,7 @@ func (s *session) CommitTxn(ctx context.Context) error { if err != nil { label = metrics.LblError } - metrics.TransactionCounter.WithLabelValues(label).Inc() + metrics.TransactionCounter.WithLabelValues(s.getSQLLabel(), label).Inc() return errors.Trace(err) } @@ -398,7 +399,7 @@ func (s *session) RollbackTxn(ctx context.Context) error { var err error if s.txn.Valid() { terror.Log(s.txn.Rollback()) - metrics.TransactionCounter.WithLabelValues(metrics.LblRollback).Inc() + metrics.TransactionCounter.WithLabelValues(s.getSQLLabel(), metrics.LblRollback).Inc() } s.cleanRetryInfo() s.txn.changeToInvalid() @@ -443,6 +444,13 @@ const sqlLogMaxLen = 1024 // SchemaChangedWithoutRetry is used for testing. var SchemaChangedWithoutRetry bool +func (s *session) getSQLLabel() string { + if s.sessionVars.InRestrictedSQL { + return metrics.LblInternal + } + return metrics.LblGeneral +} + func (s *session) isRetryableError(err error) bool { if SchemaChangedWithoutRetry { return kv.IsRetryableError(err) @@ -468,6 +476,7 @@ func (s *session) retry(ctx context.Context, maxCnt uint) error { nh := GetHistory(s) var err error var schemaVersion int64 + orgStartTS := s.GetSessionVars().TxnCtx.StartTS for { s.PrepareTxnCtx(ctx) s.sessionVars.RetryInfo.ResetOffset() @@ -497,6 +506,8 @@ func (s *session) retry(ctx context.Context, maxCnt uint) error { } s.StmtCommit() } + log.Warnf("con:%d retrying_txn_start_ts:%d original_txn_start_ts:(%d)", + connID, s.GetSessionVars().TxnCtx.StartTS, orgStartTS) if hook := ctx.Value("preCommitHook"); hook != nil { // For testing purpose. hook.(func())() @@ -509,13 +520,13 @@ func (s *session) retry(ctx context.Context, maxCnt uint) error { } if !s.isRetryableError(err) { log.Warnf("con:%d session:%v, err:%v in retry", connID, s, err) - metrics.SessionRetryErrorCounter.WithLabelValues(metrics.LblUnretryable) + metrics.SessionRetryErrorCounter.WithLabelValues(s.getSQLLabel(), metrics.LblUnretryable) return errors.Trace(err) } retryCnt++ if retryCnt >= maxCnt { log.Warnf("con:%d Retry reached max count %d", connID, retryCnt) - metrics.SessionRetryErrorCounter.WithLabelValues(metrics.LblReachMax) + metrics.SessionRetryErrorCounter.WithLabelValues(s.getSQLLabel(), metrics.LblReachMax) return errors.Trace(err) } log.Warnf("con:%d retryable error: %v, txn: %v", connID, err, s.txn) @@ -746,11 +757,7 @@ func (s *session) executeStatement(ctx context.Context, connID uint64, stmtNode } return nil, errors.Trace(err) } - label := metrics.LblGeneral - if s.sessionVars.InRestrictedSQL { - label = metrics.LblInternal - } - metrics.SessionExecuteRunDuration.WithLabelValues(label).Observe(time.Since(startTime).Seconds()) + metrics.SessionExecuteRunDuration.WithLabelValues(s.getSQLLabel()).Observe(time.Since(startTime).Seconds()) if recordSet != nil { recordSets = append(recordSets, recordSet) @@ -784,10 +791,7 @@ func (s *session) execute(ctx context.Context, sql string) (recordSets []sqlexec log.Warnf("con:%d parse error:\n%v\n%s", connID, err, sql) return nil, errors.Trace(err) } - label := metrics.LblGeneral - if s.sessionVars.InRestrictedSQL { - label = metrics.LblInternal - } + label := s.getSQLLabel() metrics.SessionExecuteParseDuration.WithLabelValues(label).Observe(time.Since(startTS).Seconds()) compiler := executor.Compiler{Ctx: s} @@ -1439,7 +1443,7 @@ func logStmt(node ast.StmtNode, vars *variable.SessionVars) { func logQuery(query string, vars *variable.SessionVars) { if atomic.LoadUint32(&variable.ProcessGeneralLog) != 0 && !vars.InRestrictedSQL { query = executor.QueryReplacer.Replace(query) - log.Infof("[GENERAL_LOG] con:%d user:%s schema_ver:%d start_ts:%d sql:%s%s", + log.Infof("[GENERAL_LOG] con:%d user:%s schema_ver:%d txn_start_ts:%d sql:%s%s", vars.ConnectionID, vars.User, vars.TxnCtx.SchemaVersion, vars.TxnCtx.StartTS, query, vars.GetExecuteArgumentsInfo()) } } diff --git a/session/txn.go b/session/txn.go index 0dc4445003eb2..53444bcf0bc56 100644 --- a/session/txn.go +++ b/session/txn.go @@ -159,26 +159,26 @@ func (st *TxnState) Delete(k kv.Key) error { return st.buf.Delete(k) } -// Seek overrides the Transaction interface. -func (st *TxnState) Seek(k kv.Key) (kv.Iterator, error) { - bufferIt, err := st.buf.Seek(k) +// Iter overrides the Transaction interface. +func (st *TxnState) Iter(k kv.Key, upperBound kv.Key) (kv.Iterator, error) { + bufferIt, err := st.buf.Iter(k, upperBound) if err != nil { return nil, errors.Trace(err) } - retrieverIt, err := st.Transaction.Seek(k) + retrieverIt, err := st.Transaction.Iter(k, upperBound) if err != nil { return nil, errors.Trace(err) } return kv.NewUnionIter(bufferIt, retrieverIt, false) } -// SeekReverse overrides the Transaction interface. -func (st *TxnState) SeekReverse(k kv.Key) (kv.Iterator, error) { - bufferIt, err := st.buf.SeekReverse(k) +// IterReverse overrides the Transaction interface. +func (st *TxnState) IterReverse(k kv.Key) (kv.Iterator, error) { + bufferIt, err := st.buf.IterReverse(k) if err != nil { return nil, errors.Trace(err) } - retrieverIt, err := st.Transaction.SeekReverse(k) + retrieverIt, err := st.Transaction.IterReverse(k) if err != nil { return nil, errors.Trace(err) } diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 2582a375641de..2ea3721a7f808 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -254,6 +254,10 @@ type SessionVars struct { // AllowAggPushDown can be set to false to forbid aggregation push down. AllowAggPushDown bool + // AllowWriteRowID can be set to false to forbid write data to _tidb_rowid. + // This variable is currently not recommended to be turned on. + AllowWriteRowID bool + // AllowInSubqToJoinAndAgg can be set to false to forbid rewriting the semi join to inner join with agg. AllowInSubqToJoinAndAgg bool @@ -555,6 +559,8 @@ func (s *SessionVars) SetSystemVar(name string, val string) error { s.SkipUTF8Check = TiDBOptOn(val) case TiDBOptAggPushDown: s.AllowAggPushDown = TiDBOptOn(val) + case TiDBOptWriteRowID: + s.AllowWriteRowID = TiDBOptOn(val) case TiDBOptInSubqToJoinAndAgg: s.AllowInSubqToJoinAndAgg = TiDBOptOn(val) case TiDBIndexLookupConcurrency: @@ -611,6 +617,8 @@ func (s *SessionVars) SetSystemVar(name string, val string) error { atomic.StoreUint32(&ProcessGeneralLog, uint32(tidbOptPositiveInt32(val, DefTiDBGeneralLog))) case TiDBSlowLogThreshold: atomic.StoreUint64(&config.GetGlobalConfig().Log.SlowThreshold, uint64(tidbOptInt64(val, logutil.DefaultSlowThreshold))) + case TiDBQueryLogMaxLen: + atomic.StoreUint64(&config.GetGlobalConfig().Log.QueryLogMaxLen, uint64(tidbOptInt64(val, logutil.DefaultQueryLogMaxLen))) case TiDBRetryLimit: s.RetryLimit = tidbOptInt64(val, DefTiDBRetryLimit) case TiDBDisableTxnAutoRetry: diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 872f9760e0feb..a4947a79fc01c 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -17,7 +17,6 @@ import ( "strconv" "strings" - "github.com/pingcap/parser/charset" "github.com/pingcap/parser/mysql" "github.com/pingcap/parser/terror" "github.com/pingcap/tidb/config" @@ -165,7 +164,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal, "innodb_max_undo_log_size", ""}, {ScopeGlobal | ScopeSession, "range_alloc_block_size", "4096"}, {ScopeGlobal, ConnectTimeout, "10"}, - {ScopeGlobal | ScopeSession, "collation_server", charset.CollationUTF8}, + {ScopeGlobal | ScopeSession, "collation_server", mysql.DefaultCollationName}, {ScopeNone, "have_rtree_keys", "YES"}, {ScopeGlobal, "innodb_old_blocks_pct", "37"}, {ScopeGlobal, "innodb_file_format", "Antelope"}, @@ -297,7 +296,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal | ScopeSession, "query_cache_wlock_invalidate", "OFF"}, {ScopeGlobal | ScopeSession, "sql_buffer_result", "OFF"}, {ScopeGlobal | ScopeSession, "character_set_filesystem", "binary"}, - {ScopeGlobal | ScopeSession, "collation_database", charset.CollationUTF8}, + {ScopeGlobal | ScopeSession, "collation_database", mysql.DefaultCollationName}, {ScopeGlobal | ScopeSession, "auto_increment_increment", "1"}, {ScopeGlobal | ScopeSession, "max_heap_table_size", "16777216"}, {ScopeGlobal | ScopeSession, "div_precision_increment", "4"}, @@ -318,7 +317,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal, "innodb_purge_batch_size", "300"}, {ScopeNone, "have_profiling", "NO"}, {ScopeGlobal, "slave_checkpoint_group", "512"}, - {ScopeGlobal | ScopeSession, "character_set_client", charset.CharsetUTF8}, + {ScopeGlobal | ScopeSession, "character_set_client", mysql.DefaultCharset}, {ScopeNone, "slave_load_tmpdir", "/var/tmp/"}, {ScopeGlobal, "innodb_buffer_pool_dump_now", "OFF"}, {ScopeGlobal, "relay_log_purge", "ON"}, @@ -393,7 +392,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal | ScopeSession, "binlog_direct_non_transactional_updates", "OFF"}, {ScopeGlobal, "innodb_change_buffering", "all"}, {ScopeGlobal | ScopeSession, "sql_big_selects", "ON"}, - {ScopeGlobal | ScopeSession, CharacterSetResults, charset.CharsetUTF8}, + {ScopeGlobal | ScopeSession, CharacterSetResults, mysql.DefaultCharset}, {ScopeGlobal, "innodb_max_purge_lag_delay", "0"}, {ScopeGlobal | ScopeSession, "session_track_schema", ""}, {ScopeGlobal, "innodb_io_capacity_max", "2000"}, @@ -406,7 +405,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal, "innodb_buffer_pool_load_abort", "OFF"}, {ScopeGlobal | ScopeSession, "tx_isolation", "REPEATABLE-READ"}, {ScopeGlobal | ScopeSession, "transaction_isolation", "REPEATABLE-READ"}, - {ScopeGlobal | ScopeSession, "collation_connection", charset.CollationUTF8}, + {ScopeGlobal | ScopeSession, "collation_connection", mysql.DefaultCollationName}, {ScopeGlobal, "rpl_semi_sync_master_timeout", ""}, {ScopeGlobal | ScopeSession, "transaction_prealloc_size", "4096"}, {ScopeNone, "slave_skip_errors", "OFF"}, @@ -501,7 +500,7 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal, "max_points_in_geometry", ""}, {ScopeGlobal, "innodb_stats_sample_pages", "8"}, {ScopeGlobal | ScopeSession, "profiling_history_size", "15"}, - {ScopeGlobal | ScopeSession, "character_set_database", charset.CharsetUTF8}, + {ScopeGlobal | ScopeSession, "character_set_database", mysql.DefaultCharset}, {ScopeNone, "have_symlink", "YES"}, {ScopeGlobal | ScopeSession, "storage_engine", "InnoDB"}, {ScopeGlobal | ScopeSession, "sql_log_off", "OFF"}, @@ -580,10 +579,10 @@ var defaultSysVars = []*SysVar{ {ScopeGlobal, "flush", "OFF"}, {ScopeGlobal | ScopeSession, "eq_range_index_dive_limit", "10"}, {ScopeNone, "performance_schema_events_stages_history_size", "10"}, - {ScopeGlobal | ScopeSession, "character_set_connection", charset.CharsetUTF8}, + {ScopeGlobal | ScopeSession, "character_set_connection", mysql.DefaultCharset}, {ScopeGlobal, "myisam_use_mmap", "OFF"}, {ScopeGlobal | ScopeSession, "ndb_join_pushdown", ""}, - {ScopeGlobal | ScopeSession, "character_set_server", charset.CharsetUTF8}, + {ScopeGlobal | ScopeSession, "character_set_server", mysql.DefaultCharset}, {ScopeGlobal, "validate_password_special_char_count", "1"}, {ScopeNone, "performance_schema_max_thread_instances", "402"}, {ScopeGlobal, "slave_rows_search_algorithms", "TABLE_SCAN,INDEX_SCAN"}, @@ -622,6 +621,7 @@ var defaultSysVars = []*SysVar{ /* TiDB specific variables */ {ScopeSession, TiDBSnapshot, ""}, {ScopeSession, TiDBOptAggPushDown, boolToIntStr(DefOptAggPushDown)}, + {ScopeSession, TiDBOptWriteRowID, boolToIntStr(DefOptWriteRowID)}, {ScopeGlobal | ScopeSession, TiDBBuildStatsConcurrency, strconv.Itoa(DefBuildStatsConcurrency)}, {ScopeGlobal, TiDBAutoAnalyzeRatio, strconv.FormatFloat(DefAutoAnalyzeRatio, 'f', -1, 64)}, {ScopeGlobal, TiDBAutoAnalyzeStartTime, DefAutoAnalyzeStartTime}, @@ -664,6 +664,7 @@ var defaultSysVars = []*SysVar{ /* The following variable is defined as session scope but is actually server scope. */ {ScopeSession, TiDBGeneralLog, strconv.Itoa(DefTiDBGeneralLog)}, {ScopeSession, TiDBSlowLogThreshold, strconv.Itoa(logutil.DefaultSlowThreshold)}, + {ScopeSession, TiDBQueryLogMaxLen, strconv.Itoa(logutil.DefaultQueryLogMaxLen)}, {ScopeSession, TiDBConfig, ""}, {ScopeGlobal | ScopeSession, TiDBDDLReorgWorkerCount, strconv.Itoa(DefTiDBDDLReorgWorkerCount)}, {ScopeSession, TiDBDDLReorgPriority, "PRIORITY_LOW"}, diff --git a/sessionctx/variable/tidb_vars.go b/sessionctx/variable/tidb_vars.go index bd281ab174be5..248ab3e5cd8b6 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/sessionctx/variable/tidb_vars.go @@ -41,6 +41,9 @@ const ( // tidb_opt_agg_push_down is used to enable/disable the optimizer rule of aggregation push down. TiDBOptAggPushDown = "tidb_opt_agg_push_down" + // tidb_opt_write_row_id is used to enable/disable the operations of insert、replace and update to _tidb_rowid. + TiDBOptWriteRowID = "tidb_opt_write_row_id" + // Auto analyze will run if (table modify count)/(table row count) is greater than this value. TiDBAutoAnalyzeRatio = "tidb_auto_analyze_ratio" @@ -98,6 +101,9 @@ const ( // tidb_slow_log_threshold is used to set the slow log threshold in the server. TiDBSlowLogThreshold = "tidb_slow_log_threshold" + // tidb_query_log_max_len is used to set the max length of the query in the log. + TiDBQueryLogMaxLen = "tidb_query_log_max_len" + // tidb_retry_limit is the maximum number of retries when committing a transaction. TiDBRetryLimit = "tidb_retry_limit" @@ -226,6 +232,7 @@ const ( DefChecksumTableConcurrency = 4 DefSkipUTF8Check = false DefOptAggPushDown = false + DefOptWriteRowID = false DefOptInSubqToJoinAndAgg = true DefBatchInsert = false DefBatchDelete = false diff --git a/sessionctx/variable/varsutil.go b/sessionctx/variable/varsutil.go index 78392b9ad692b..d0b3128221ebc 100644 --- a/sessionctx/variable/varsutil.go +++ b/sessionctx/variable/varsutil.go @@ -87,6 +87,8 @@ func GetSessionOnlySysVars(s *SessionVars, key string) (string, bool, error) { return mysql.Priority2Str[mysql.PriorityEnum(atomic.LoadInt32(&ForcePriority))], true, nil case TiDBSlowLogThreshold: return strconv.FormatUint(atomic.LoadUint64(&config.GetGlobalConfig().Log.SlowThreshold), 10), true, nil + case TiDBQueryLogMaxLen: + return strconv.FormatUint(atomic.LoadUint64(&config.GetGlobalConfig().Log.QueryLogMaxLen), 10), true, nil } sVal, ok := s.systems[key] if ok { @@ -327,7 +329,9 @@ func ValidateSetSystemVar(vars *SessionVars, name string, value string) (string, TIDBMemQuotaIndexLookupReader, TIDBMemQuotaIndexLookupJoin, TIDBMemQuotaNestedLoopApply, - TiDBRetryLimit, TiDBSlowLogThreshold: + TiDBRetryLimit, + TiDBSlowLogThreshold, + TiDBQueryLogMaxLen: _, err := strconv.ParseInt(value, 10, 64) if err != nil { return value, ErrWrongValueForVar.GenWithStackByArgs(name) diff --git a/sessionctx/variable/varsutil_test.go b/sessionctx/variable/varsutil_test.go index c93ac9911973c..dd0fc336e1c85 100644 --- a/sessionctx/variable/varsutil_test.go +++ b/sessionctx/variable/varsutil_test.go @@ -78,6 +78,7 @@ func (s *testVarsutilSuite) TestNewSessionVars(c *C) { c.Assert(vars.MemQuotaIndexLookupJoin, Equals, int64(DefTiDBMemQuotaIndexLookupJoin)) c.Assert(vars.MemQuotaNestedLoopApply, Equals, int64(DefTiDBMemQuotaNestedLoopApply)) c.Assert(vars.EnableRadixJoin, Equals, DefTiDBUseRadixJoin) + c.Assert(vars.AllowWriteRowID, Equals, DefOptWriteRowID) assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.Concurrency)) assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.MemQuota)) diff --git a/statistics/gc.go b/statistics/gc.go index 84405d63e6bbc..0ca7c8c4dc604 100644 --- a/statistics/gc.go +++ b/statistics/gc.go @@ -20,7 +20,6 @@ import ( "github.com/cznic/mathutil" "github.com/pingcap/errors" "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/store/tikv/oracle" "github.com/pingcap/tidb/util/sqlexec" "golang.org/x/net/context" ) @@ -31,7 +30,7 @@ func (h *Handle) GCStats(is infoschema.InfoSchema, ddlLease time.Duration) error // To make sure that all the deleted tables' schema and stats info have been acknowledged to all tidb, // we only garbage collect version before 10 lease. lease := mathutil.MaxInt64(int64(h.Lease), int64(ddlLease)) - offset := oracle.ComposeTS(10*lease, 0) + offset := DurationToTS(10 * time.Duration(lease)) if h.LastUpdateVersion() < offset { return nil } diff --git a/statistics/handle.go b/statistics/handle.go index e904d6f5bbe6d..8484f448ea2ed 100644 --- a/statistics/handle.go +++ b/statistics/handle.go @@ -109,6 +109,11 @@ func (h *Handle) GetQueryFeedback() []*QueryFeedback { return h.feedback } +// DurationToTS converts duration to timestamp. +func DurationToTS(d time.Duration) uint64 { + return oracle.ComposeTS(d.Nanoseconds()/int64(time.Millisecond), 0) +} + // Update reads stats meta from store and updates the stats map. func (h *Handle) Update(is infoschema.InfoSchema) error { lastVersion := h.LastUpdateVersion() @@ -117,7 +122,7 @@ func (h *Handle) Update(is infoschema.InfoSchema) error { // and A0 < B0 < B1 < A1. We will first read the stats of B, and update the lastVersion to B0, but we cannot read // the table stats of A0 if we read stats that greater than lastVersion which is B0. // We can read the stats if the diff between commit time and version is less than three lease. - offset := oracle.ComposeTS(3*int64(h.Lease), 0) + offset := DurationToTS(3 * h.Lease) if lastVersion >= offset { lastVersion = lastVersion - offset } else { diff --git a/statistics/handle_test.go b/statistics/handle_test.go index 08f5435fe6302..0d3aa16f89a86 100644 --- a/statistics/handle_test.go +++ b/statistics/handle_test.go @@ -238,6 +238,14 @@ func (s *testStatsCacheSuite) TestAvgColLen(c *C) { c.Assert(statsTbl.Columns[tableInfo.Columns[3].ID].AvgColSize(statsTbl.Count), Equals, 16.0) } +func (s *testStatsCacheSuite) TestDurationToTS(c *C) { + tests := []time.Duration{time.Millisecond, time.Second, time.Minute, time.Hour} + for _, t := range tests { + ts := statistics.DurationToTS(t) + c.Assert(oracle.ExtractPhysical(ts)*int64(time.Millisecond), Equals, int64(t)) + } +} + func (s *testStatsCacheSuite) TestVersion(c *C) { defer cleanEnv(c, s.store, s.do) testKit := testkit.NewTestKit(c, s.store) @@ -249,11 +257,12 @@ func (s *testStatsCacheSuite) TestVersion(c *C) { tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) c.Assert(err, IsNil) tableInfo1 := tbl1.Meta() - h := statistics.NewHandle(testKit.Se, 1) - testKit.MustExec("update mysql.stats_meta set version = 2 where table_id = ?", tableInfo1.ID) + h := statistics.NewHandle(testKit.Se, time.Millisecond) + unit := oracle.ComposeTS(1, 0) + testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", 2*unit, tableInfo1.ID) h.Update(is) - c.Assert(h.LastUpdateVersion(), Equals, uint64(2)) + c.Assert(h.LastUpdateVersion(), Equals, 2*unit) statsTbl1 := h.GetTableStats(tableInfo1) c.Assert(statsTbl1.Pseudo, IsFalse) @@ -264,15 +273,15 @@ func (s *testStatsCacheSuite) TestVersion(c *C) { c.Assert(err, IsNil) tableInfo2 := tbl2.Meta() // A smaller version write, and we can still read it. - testKit.MustExec("update mysql.stats_meta set version = 1 where table_id = ?", tableInfo2.ID) + testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", unit, tableInfo2.ID) h.Update(is) - c.Assert(h.LastUpdateVersion(), Equals, uint64(2)) + c.Assert(h.LastUpdateVersion(), Equals, 2*unit) statsTbl2 := h.GetTableStats(tableInfo2) c.Assert(statsTbl2.Pseudo, IsFalse) testKit.MustExec("insert t1 values(1,2)") testKit.MustExec("analyze table t1") - offset := oracle.ComposeTS(3*int64(h.Lease), 0) + offset := 3 * unit testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", offset+4, tableInfo1.ID) h.Update(is) c.Assert(h.LastUpdateVersion(), Equals, offset+uint64(4)) diff --git a/store/mockstore/mocktikv/analyze.go b/store/mockstore/mocktikv/analyze.go index 2727efe1ce48a..5895fa3e56c68 100644 --- a/store/mockstore/mocktikv/analyze.go +++ b/store/mockstore/mocktikv/analyze.go @@ -18,7 +18,6 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/parser/ast" - "github.com/pingcap/parser/charset" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/kv" @@ -144,7 +143,7 @@ func (h *rpcHandler) handleAnalyzeColumnsReq(req *coprocessor.Request, analyzeRe for i := range e.fields { rf := new(ast.ResultField) rf.Column = new(model.ColumnInfo) - rf.Column.FieldType = types.FieldType{Tp: mysql.TypeBlob, Flen: mysql.MaxBlobWidth, Charset: charset.CharsetUTF8, Collate: charset.CollationUTF8} + rf.Column.FieldType = types.FieldType{Tp: mysql.TypeBlob, Flen: mysql.MaxBlobWidth, Charset: mysql.DefaultCharset, Collate: mysql.DefaultCollationName} e.fields[i] = rf } diff --git a/store/mockstore/mocktikv/mock_tikv_test.go b/store/mockstore/mocktikv/mock_tikv_test.go index 807a4267134ba..a4af86f037a28 100644 --- a/store/mockstore/mocktikv/mock_tikv_test.go +++ b/store/mockstore/mocktikv/mock_tikv_test.go @@ -119,7 +119,11 @@ func (s *testMockTiKVSuite) mustDeleteOK(c *C, key string, startTS, commitTS uin } func (s *testMockTiKVSuite) mustScanOK(c *C, start string, limit int, ts uint64, expect ...string) { - pairs := s.store.Scan([]byte(start), nil, limit, ts, kvrpcpb.IsolationLevel_SI) + s.mustRangeScanOK(c, start, "", limit, ts, expect...) +} + +func (s *testMockTiKVSuite) mustRangeScanOK(c *C, start, end string, limit int, ts uint64, expect ...string) { + pairs := s.store.Scan([]byte(start), []byte(end), limit, ts, kvrpcpb.IsolationLevel_SI) c.Assert(len(pairs)*2, Equals, len(expect)) for i := 0; i < len(pairs); i++ { c.Assert(pairs[i].Err, IsNil) @@ -129,7 +133,11 @@ func (s *testMockTiKVSuite) mustScanOK(c *C, start string, limit int, ts uint64, } func (s *testMockTiKVSuite) mustReverseScanOK(c *C, end string, limit int, ts uint64, expect ...string) { - pairs := s.store.ReverseScan(nil, []byte(end), limit, ts, kvrpcpb.IsolationLevel_SI) + s.mustRangeReverseScanOK(c, "", end, limit, ts, expect...) +} + +func (s *testMockTiKVSuite) mustRangeReverseScanOK(c *C, start, end string, limit int, ts uint64, expect ...string) { + pairs := s.store.ReverseScan([]byte(start), []byte(end), limit, ts, kvrpcpb.IsolationLevel_SI) c.Assert(len(pairs)*2, Equals, len(expect)) for i := 0; i < len(pairs); i++ { c.Assert(pairs[i].Err, IsNil) @@ -249,6 +257,9 @@ func (s *testMockTiKVSuite) TestReverseScan(c *C) { s.mustReverseScanOK(c, "C\x00", 3, 10, "C", "C10", "A", "A10") s.mustReverseScanOK(c, "C\x00", 4, 10, "C", "C10", "A", "A10") s.mustReverseScanOK(c, "B", 1, 10, "A", "A10") + s.mustRangeReverseScanOK(c, "", "E", 5, 10, "C", "C10", "A", "A10") + s.mustRangeReverseScanOK(c, "", "C\x00", 5, 10, "C", "C10", "A", "A10") + s.mustRangeReverseScanOK(c, "A\x00", "C", 5, 10) } checkV10() @@ -260,6 +271,9 @@ func (s *testMockTiKVSuite) TestReverseScan(c *C) { s.mustReverseScanOK(c, "Z", 5, 20, "E", "E10", "D", "D20", "C", "C10", "B", "B20", "A", "A10") s.mustReverseScanOK(c, "C\x00", 5, 20, "C", "C10", "B", "B20", "A", "A10") s.mustReverseScanOK(c, "A\x00", 1, 20, "A", "A10") + s.mustRangeReverseScanOK(c, "B", "D", 5, 20, "C", "C10", "B", "B20") + s.mustRangeReverseScanOK(c, "B", "D\x00", 5, 20, "D", "D20", "C", "C10", "B", "B20") + s.mustRangeReverseScanOK(c, "B\x00", "D\x00", 5, 20, "D", "D20", "C", "C10") } checkV10() checkV20() @@ -308,6 +322,9 @@ func (s *testMockTiKVSuite) TestScan(c *C) { s.mustScanOK(c, "A\x00", 3, 10, "C", "C10", "E", "E10") s.mustScanOK(c, "C", 4, 10, "C", "C10", "E", "E10") s.mustScanOK(c, "F", 1, 10) + s.mustRangeScanOK(c, "", "E", 5, 10, "A", "A10", "C", "C10") + s.mustRangeScanOK(c, "", "C\x00", 5, 10, "A", "A10", "C", "C10") + s.mustRangeScanOK(c, "A\x00", "C", 5, 10) } checkV10() @@ -319,6 +336,9 @@ func (s *testMockTiKVSuite) TestScan(c *C) { s.mustScanOK(c, "", 5, 20, "A", "A10", "B", "B20", "C", "C10", "D", "D20", "E", "E10") s.mustScanOK(c, "C", 5, 20, "C", "C10", "D", "D20", "E", "E10") s.mustScanOK(c, "D\x00", 1, 20, "E", "E10") + s.mustRangeScanOK(c, "B", "D", 5, 20, "B", "B20", "C", "C10") + s.mustRangeScanOK(c, "B", "D\x00", 5, 20, "B", "B20", "C", "C10", "D", "D20") + s.mustRangeScanOK(c, "B\x00", "D\x00", 5, 20, "C", "C10", "D", "D20") } checkV10() checkV20() diff --git a/store/mockstore/mocktikv/rpc.go b/store/mockstore/mocktikv/rpc.go index 96458eb569aa3..7cc6009569537 100644 --- a/store/mockstore/mocktikv/rpc.go +++ b/store/mockstore/mocktikv/rpc.go @@ -229,7 +229,11 @@ func (h *rpcHandler) handleKvScan(req *kvrpcpb.ScanRequest) *kvrpcpb.ScanRespons if !h.checkKeyInRegion(req.GetStartKey()) { panic("KvScan: startKey not in region") } - pairs := h.mvccStore.Scan(req.GetStartKey(), h.endKey, int(req.GetLimit()), req.GetVersion(), h.isolationLevel) + endKey := h.endKey + if len(req.EndKey) > 0 && (len(endKey) == 0 || bytes.Compare(req.EndKey, endKey) < 0) { + endKey = req.EndKey + } + pairs := h.mvccStore.Scan(req.GetStartKey(), endKey, int(req.GetLimit()), req.GetVersion(), h.isolationLevel) return &kvrpcpb.ScanResponse{ Pairs: convertToPbPairs(pairs), } diff --git a/store/store_test.go b/store/store_test.go index bf481caeab7da..95ef4c131b4e9 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -97,7 +97,7 @@ func valToStr(c *C, iter kv.Iterator) string { func checkSeek(c *C, txn kv.Transaction) { for i := startIndex; i < testCount; i++ { val := encodeInt(i * indexStep) - iter, err := txn.Seek(val) + iter, err := txn.Iter(val, nil) c.Assert(err, IsNil) c.Assert([]byte(iter.Key()), BytesEquals, val) c.Assert(decodeInt([]byte(valToStr(c, iter))), Equals, i*indexStep) @@ -107,7 +107,7 @@ func checkSeek(c *C, txn kv.Transaction) { // Test iterator Next() for i := startIndex; i < testCount-1; i++ { val := encodeInt(i * indexStep) - iter, err := txn.Seek(val) + iter, err := txn.Iter(val, nil) c.Assert(err, IsNil) c.Assert([]byte(iter.Key()), BytesEquals, val) c.Assert(valToStr(c, iter), Equals, string(val)) @@ -123,7 +123,7 @@ func checkSeek(c *C, txn kv.Transaction) { } // Non exist and beyond maximum seek test - iter, err := txn.Seek(encodeInt(testCount * indexStep)) + iter, err := txn.Iter(encodeInt(testCount*indexStep), nil) c.Assert(err, IsNil) c.Assert(iter.Valid(), IsFalse) @@ -131,7 +131,7 @@ func checkSeek(c *C, txn kv.Transaction) { // it returns the smallest key that larger than the one we are seeking inBetween := encodeInt((testCount-1)*indexStep - 1) last := encodeInt((testCount - 1) * indexStep) - iter, err = txn.Seek(inBetween) + iter, err = txn.Iter(inBetween, nil) c.Assert(err, IsNil) c.Assert(iter.Valid(), IsTrue) c.Assert([]byte(iter.Key()), Not(BytesEquals), inBetween) @@ -278,7 +278,7 @@ func (s *testKVSuite) TestDelete2(c *C) { txn, err = s.s.Begin() c.Assert(err, IsNil) - it, err := txn.Seek([]byte("DATA_test_tbl_department_record__0000000001_0003")) + it, err := txn.Iter([]byte("DATA_test_tbl_department_record__0000000001_0003"), nil) c.Assert(err, IsNil) for it.Valid() { err = txn.Delete([]byte(it.Key())) @@ -290,7 +290,7 @@ func (s *testKVSuite) TestDelete2(c *C) { txn, err = s.s.Begin() c.Assert(err, IsNil) - it, _ = txn.Seek([]byte("DATA_test_tbl_department_record__000000000")) + it, _ = txn.Iter([]byte("DATA_test_tbl_department_record__000000000"), nil) c.Assert(it.Valid(), IsFalse) txn.Commit(context.Background()) } @@ -312,7 +312,7 @@ func (s *testKVSuite) TestBasicSeek(c *C) { c.Assert(err, IsNil) defer txn.Commit(context.Background()) - it, err := txn.Seek([]byte("2")) + it, err := txn.Iter([]byte("2"), nil) c.Assert(err, IsNil) c.Assert(it.Valid(), Equals, false) txn.Delete([]byte("1")) @@ -333,30 +333,30 @@ func (s *testKVSuite) TestBasicTable(c *C) { err = txn.Set([]byte("1"), []byte("1")) c.Assert(err, IsNil) - it, err := txn.Seek([]byte("0")) + it, err := txn.Iter([]byte("0"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "1") err = txn.Set([]byte("0"), []byte("0")) c.Assert(err, IsNil) - it, err = txn.Seek([]byte("0")) + it, err = txn.Iter([]byte("0"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "0") err = txn.Delete([]byte("0")) c.Assert(err, IsNil) txn.Delete([]byte("1")) - it, err = txn.Seek([]byte("0")) + it, err = txn.Iter([]byte("0"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "2") err = txn.Delete([]byte("3")) c.Assert(err, IsNil) - it, err = txn.Seek([]byte("2")) + it, err = txn.Iter([]byte("2"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "2") - it, err = txn.Seek([]byte("3")) + it, err = txn.Iter([]byte("3"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "4") err = txn.Delete([]byte("2")) @@ -411,14 +411,14 @@ func (s *testKVSuite) TestSeekMin(c *C) { txn.Set([]byte(row.key), []byte(row.value)) } - it, err := txn.Seek(nil) + it, err := txn.Iter(nil, nil) c.Assert(err, IsNil) for it.Valid() { fmt.Printf("%s, %s\n", it.Key(), it.Value()) it.Next() } - it, err = txn.Seek([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000")) + it, err = txn.Iter([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000"), nil) c.Assert(err, IsNil) c.Assert(string(it.Key()), Equals, "DATA_test_main_db_tbl_tbl_test_record__00000000000000000001") diff --git a/store/tikv/2pc.go b/store/tikv/2pc.go index 9d785f3a9fe4c..382663e0d3671 100644 --- a/store/tikv/2pc.go +++ b/store/tikv/2pc.go @@ -569,7 +569,8 @@ func (c *twoPhaseCommitter) execute(ctx context.Context) error { if !committed && !undetermined { c.cleanWg.Add(1) go func() { - err := c.cleanupKeys(NewBackoffer(context.Background(), cleanupMaxBackoff).WithVars(c.txn.vars), c.keys) + cleanupKeysCtx := context.WithValue(context.Background(), txnStartKey, ctx.Value(txnStartKey)) + err := c.cleanupKeys(NewBackoffer(cleanupKeysCtx, cleanupMaxBackoff).WithVars(c.txn.vars), c.keys) if err != nil { metrics.TiKVSecondaryLockCleanupFailureCounter.WithLabelValues("rollback").Inc() log.Infof("con:%d 2PC cleanup err: %v, tid: %d", c.connID, err, c.startTS) diff --git a/store/tikv/backoff.go b/store/tikv/backoff.go index 25c78cd9a60be..a36469d27d5a5 100644 --- a/store/tikv/backoff.go +++ b/store/tikv/backoff.go @@ -185,6 +185,9 @@ type Backoffer struct { vars *kv.Variables } +// txnStartKey is a key for transaction start_ts info in context.Context. +const txnStartKey = "_txn_start_key" + // NewBackoffer creates a Backoffer with maximum sleep time(in ms). func NewBackoffer(ctx context.Context, maxSleep int) *Backoffer { return &Backoffer{ @@ -228,7 +231,11 @@ func (b *Backoffer) Backoff(typ backoffType, err error) error { b.totalSleep += f(b.ctx) b.types = append(b.types, typ) - log.Debugf("%v, retry later(totalsleep %dms, maxsleep %dms)", err, b.totalSleep, b.maxSleep) + var startTs interface{} = "" + if ts := b.ctx.Value(txnStartKey); ts != nil { + startTs = ts + } + log.Debugf("%v, retry later(totalsleep %dms, maxsleep %dms), type: %s, txn_start_ts: %v", err, b.totalSleep, b.maxSleep, typ.String(), startTs) b.errors = append(b.errors, errors.Errorf("%s at %s", err.Error(), time.Now().Format(time.RFC3339Nano))) if b.maxSleep > 0 && b.totalSleep >= b.maxSleep { diff --git a/store/tikv/coprocessor.go b/store/tikv/coprocessor.go index dc87f2454e62e..8221e53a3f94f 100644 --- a/store/tikv/coprocessor.go +++ b/store/tikv/coprocessor.go @@ -80,6 +80,7 @@ func (c *CopClient) supportExpr(exprType tipb.ExprType) bool { // Send builds the request and gets the coprocessor iterator response. func (c *CopClient) Send(ctx context.Context, req *kv.Request, vars *kv.Variables) kv.Response { + ctx = context.WithValue(ctx, txnStartKey, req.StartTs) bo := NewBackoffer(ctx, copBuildTaskMaxBackoff).WithVars(vars) tasks, err := buildCopTasks(bo, c.store.regionCache, &copRanges{mid: req.KeyRanges}, req.Desc, req.Streaming) if err != nil { diff --git a/store/tikv/delete_range_test.go b/store/tikv/delete_range_test.go index 7864eb0954878..ee0b3c6c73126 100644 --- a/store/tikv/delete_range_test.go +++ b/store/tikv/delete_range_test.go @@ -50,7 +50,7 @@ func (s *testDeleteRangeSuite) TearDownTest(c *C) { func (s *testDeleteRangeSuite) checkData(c *C, expectedData map[string]string) { txn, err := s.store.Begin() c.Assert(err, IsNil) - it, err := txn.Seek([]byte("a")) + it, err := txn.Iter([]byte("a"), nil) c.Assert(err, IsNil) // Scan all data and save into a map diff --git a/store/tikv/latch/latch.go b/store/tikv/latch/latch.go index f6f57a460b106..a62b6da8a09cb 100644 --- a/store/tikv/latch/latch.go +++ b/store/tikv/latch/latch.go @@ -18,8 +18,10 @@ import ( "math/bits" "sort" "sync" + "time" "github.com/cznic/mathutil" + log "github.com/sirupsen/logrus" "github.com/spaolacci/murmur3" ) @@ -181,6 +183,12 @@ func (latches *Latches) releaseSlot(lock *Lock) (nextLock *Lock) { } find.maxCommitTS = mathutil.MaxUint64(find.maxCommitTS, lock.commitTS) find.value = nil + // Make a copy of the key, so latch does not reference the transaction's memory. + // If we do not do it, transaction memory can't be recycle by GC and there will + // be a leak. + copyKey := make([]byte, len(find.key)) + copy(copyKey, find.key) + find.key = copyKey if len(latch.waiting) == 0 { return nil } @@ -201,6 +209,8 @@ func (latches *Latches) releaseSlot(lock *Lock) (nextLock *Lock) { latch.waiting = latch.waiting[:len(latch.waiting)-1] if find.maxCommitTS > nextLock.startTS { + find.value = nextLock + nextLock.acquiredCount++ nextLock.isStale = true } } @@ -252,27 +262,32 @@ func (latches *Latches) acquireSlot(lock *Lock) acquireResult { } // recycle is not thread safe, the latch should acquire its lock before executing this function. -func (l *latch) recycle(currentTS uint64) { +func (l *latch) recycle(currentTS uint64) int { + total := 0 fakeHead := node{next: l.queue} prev := &fakeHead for curr := prev.next; curr != nil; curr = curr.next { if tsoSub(currentTS, curr.maxCommitTS) >= expireDuration && curr.value == nil { l.count-- prev.next = curr.next + total++ } else { prev = curr } } l.queue = fakeHead.next + return total } func (latches *Latches) recycle(currentTS uint64) { + total := 0 for i := 0; i < len(latches.slots); i++ { latch := &latches.slots[i] latch.Lock() - latch.recycle(currentTS) + total += latch.recycle(currentTS) latch.Unlock() } + log.Debugf("recycle run at %v, recycle count = %d...\n", time.Now(), total) } func findNode(list *node, key []byte) *node { diff --git a/store/tikv/latch/latch_test.go b/store/tikv/latch/latch_test.go index 951a9e3de1802..4b10c118883a6 100644 --- a/store/tikv/latch/latch_test.go +++ b/store/tikv/latch/latch_test.go @@ -122,6 +122,9 @@ func (s *testLatchSuite) TestRecycle(c *C) { lock.SetCommitTS(startTS + 1) var wakeupList []*Lock latches.release(lock, wakeupList) + // Release lock will grant latch to lock1 automatically, + // so release lock1 is called here. + latches.release(lock1, wakeupList) lock2 := latches.genLock(startTS+3, [][]byte{ []byte("b"), []byte("c"), diff --git a/store/tikv/latch/scheduler.go b/store/tikv/latch/scheduler.go index 5f4e84cd7760d..cbf2cb8157dfa 100644 --- a/store/tikv/latch/scheduler.go +++ b/store/tikv/latch/scheduler.go @@ -44,9 +44,8 @@ func NewScheduler(size uint) *LatchesScheduler { return scheduler } -// A transaction can last for at most 10 minutes, see also gcworker. -const expireDuration = 10 * time.Minute -const checkInterval = 5 * time.Minute +const expireDuration = 2 * time.Minute +const checkInterval = 1 * time.Minute const checkCounter = 50000 const latchListCount = 5 @@ -62,7 +61,7 @@ func (scheduler *LatchesScheduler) run() { if lock.commitTS > lock.startTS { currentTS := lock.commitTS elapsed := tsoSub(currentTS, scheduler.lastRecycleTime) - if elapsed > checkInterval && counter > checkCounter { + if elapsed > checkInterval || counter > checkCounter { go scheduler.latches.recycle(lock.commitTS) scheduler.lastRecycleTime = currentTS counter = 0 diff --git a/store/tikv/latch/scheduler_test.go b/store/tikv/latch/scheduler_test.go index fc2d5b1514d0f..a2e88dde6b407 100644 --- a/store/tikv/latch/scheduler_test.go +++ b/store/tikv/latch/scheduler_test.go @@ -14,7 +14,10 @@ package latch import ( + "bytes" + "math/rand" "sync" + "time" . "github.com/pingcap/check" ) @@ -28,28 +31,64 @@ func (s *testSchedulerSuite) SetUpTest(c *C) { } func (s *testSchedulerSuite) TestWithConcurrency(c *C) { - txns := [][][]byte{ - {[]byte("a"), []byte("b"), []byte("c")}, - {[]byte("a"), []byte("d"), []byte("e"), []byte("f")}, - {[]byte("e"), []byte("f"), []byte("g"), []byte("h")}, - } - sched := NewScheduler(1024) + sched := NewScheduler(7) defer sched.Close() + rand.Seed(time.Now().Unix()) + ch := make(chan [][]byte, 100) + const workerCount = 10 var wg sync.WaitGroup - wg.Add(len(txns)) - for _, txn := range txns { - go func(txn [][]byte, wg *sync.WaitGroup) { - lock := sched.Lock(getTso(), txn) - defer sched.UnLock(lock) - if lock.IsStale() { - // Should restart the transaction or return error - } else { - lock.SetCommitTS(getTso()) - // Do 2pc + wg.Add(workerCount) + for i := 0; i < workerCount; i++ { + go func(ch <-chan [][]byte, wg *sync.WaitGroup) { + for txn := range ch { + lock := sched.Lock(getTso(), txn) + if lock.IsStale() { + // Should restart the transaction or return error + } else { + lock.SetCommitTS(getTso()) + // Do 2pc + } + sched.UnLock(lock) } wg.Done() - }(txn, &wg) + }(ch, &wg) + } + + for i := 0; i < 999; i++ { + ch <- generate() } + close(ch) + wg.Wait() } + +// generate generates something like: +// {[]byte("a"), []byte("b"), []byte("c")} +// {[]byte("a"), []byte("d"), []byte("e"), []byte("f")} +// {[]byte("e"), []byte("f"), []byte("g"), []byte("h")} +// The data should not repeat in the sequence. +func generate() [][]byte { + table := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'} + ret := make([][]byte, 0, 5) + chance := []int{100, 60, 40, 20} + for i := 0; i < len(chance); i++ { + needMore := rand.Intn(100) < chance[i] + if needMore { + randBytes := []byte{table[rand.Intn(len(table))]} + if !contains(randBytes, ret) { + ret = append(ret, randBytes) + } + } + } + return ret +} + +func contains(x []byte, set [][]byte) bool { + for _, y := range set { + if bytes.Compare(x, y) == 0 { + return true + } + } + return false +} diff --git a/store/tikv/lock_test.go b/store/tikv/lock_test.go index 20bedba474f10..31e0c4597b121 100644 --- a/store/tikv/lock_test.go +++ b/store/tikv/lock_test.go @@ -116,7 +116,7 @@ func (s *testLockSuite) TestScanLockResolveWithSeek(c *C) { txn, err := s.store.Begin() c.Assert(err, IsNil) - iter, err := txn.Seek([]byte("a")) + iter, err := txn.Iter([]byte("a"), nil) c.Assert(err, IsNil) for ch := byte('a'); ch <= byte('z'); ch++ { c.Assert(iter.Valid(), IsTrue) @@ -133,7 +133,7 @@ func (s *testLockSuite) TestScanLockResolveWithSeekKeyOnly(c *C) { txn, err := s.store.Begin() c.Assert(err, IsNil) txn.SetOption(kv.KeyOnly, true) - iter, err := txn.Seek([]byte("a")) + iter, err := txn.Iter([]byte("a"), nil) c.Assert(err, IsNil) for ch := byte('a'); ch <= byte('z'); ch++ { c.Assert(iter.Valid(), IsTrue) diff --git a/store/tikv/safepoint_test.go b/store/tikv/safepoint_test.go index 0af5fcaa2ef92..69b85d02bd7a7 100644 --- a/store/tikv/safepoint_test.go +++ b/store/tikv/safepoint_test.go @@ -100,7 +100,7 @@ func (s *testSafePointSuite) TestSafePoint(c *C) { s.waitUntilErrorPlugIn(txn3.startTS) - _, seekerr := txn3.Seek(encodeKey(s.prefix, "")) + _, seekerr := txn3.Iter(encodeKey(s.prefix, ""), nil) c.Assert(seekerr, NotNil) isFallBehind = terror.ErrorEqual(errors.Cause(geterr2), ErrGCTooEarly) isMayFallBehind = terror.ErrorEqual(errors.Cause(geterr2), ErrPDServerTimeout.GenWithStackByArgs("start timestamp may fall behind safe point")) diff --git a/store/tikv/scan.go b/store/tikv/scan.go index 93637a0c90589..6bf1813bbcf41 100644 --- a/store/tikv/scan.go +++ b/store/tikv/scan.go @@ -30,10 +30,11 @@ type Scanner struct { cache []*pb.KvPair idx int nextStartKey []byte + endKey []byte eof bool } -func newScanner(snapshot *tikvSnapshot, startKey []byte, batchSize int) (*Scanner, error) { +func newScanner(snapshot *tikvSnapshot, startKey []byte, endKey []byte, batchSize int) (*Scanner, error) { // It must be > 1. Otherwise scanner won't skipFirst. if batchSize <= 1 { batchSize = scanBatchSize @@ -43,6 +44,7 @@ func newScanner(snapshot *tikvSnapshot, startKey []byte, batchSize int) (*Scanne batchSize: batchSize, valid: true, nextStartKey: startKey, + endKey: endKey, } err := scanner.Next() if kv.IsErrNotFound(err) { @@ -74,7 +76,7 @@ func (s *Scanner) Value() []byte { // Next return next element. func (s *Scanner) Next() error { - bo := NewBackoffer(context.Background(), scannerNextMaxBackoff) + bo := NewBackoffer(context.WithValue(context.Background(), txnStartKey, s.snapshot.version.Ver), scannerNextMaxBackoff) if !s.valid { return errors.New("scanner iterator is invalid") } @@ -96,6 +98,11 @@ func (s *Scanner) Next() error { } current := s.cache[s.idx] + if len(s.endKey) > 0 && kv.Key(current.Key).Cmp(kv.Key(s.endKey)) >= 0 { + s.eof = true + s.Close() + return nil + } // Try to resolve the lock if current.GetError() != nil { // 'current' would be modified if the lock being resolved @@ -147,6 +154,7 @@ func (s *Scanner) getData(bo *Backoffer) error { Type: tikvrpc.CmdScan, Scan: &pb.ScanRequest{ StartKey: s.nextStartKey, + EndKey: s.endKey, Limit: uint32(s.batchSize), Version: s.startTS(), KeyOnly: s.snapshot.keyOnly, @@ -199,7 +207,7 @@ func (s *Scanner) getData(bo *Backoffer) error { // No more data in current Region. Next getData() starts // from current Region's endKey. s.nextStartKey = loc.EndKey - if len(loc.EndKey) == 0 { + if len(loc.EndKey) == 0 || (len(s.endKey) > 0 && kv.Key(s.nextStartKey).Cmp(kv.Key(s.endKey)) >= 0) { // Current Region is the last one. s.eof = true } diff --git a/store/tikv/scan_mock_test.go b/store/tikv/scan_mock_test.go index af453a985ff32..94b720314f2bd 100644 --- a/store/tikv/scan_mock_test.go +++ b/store/tikv/scan_mock_test.go @@ -41,11 +41,19 @@ func (s *testScanMockSuite) TestScanMultipleRegions(c *C) { txn, err = store.Begin() c.Assert(err, IsNil) snapshot := newTiKVSnapshot(store, kv.Version{Ver: txn.StartTS()}) - scanner, err := newScanner(snapshot, []byte("a"), 10) + scanner, err := newScanner(snapshot, []byte("a"), nil, 10) c.Assert(err, IsNil) for ch := byte('a'); ch <= byte('z'); ch++ { c.Assert([]byte{ch}, BytesEquals, []byte(scanner.Key())) c.Assert(scanner.Next(), IsNil) } c.Assert(scanner.Valid(), IsFalse) + + scanner, err = newScanner(snapshot, []byte("a"), []byte("i"), 10) + c.Assert(err, IsNil) + for ch := byte('a'); ch <= byte('h'); ch++ { + c.Assert([]byte{ch}, BytesEquals, []byte(scanner.Key())) + c.Assert(scanner.Next(), IsNil) + } + c.Assert(scanner.Valid(), IsFalse) } diff --git a/store/tikv/scan_test.go b/store/tikv/scan_test.go index e4897bc2de2b7..ff9184d0c0323 100644 --- a/store/tikv/scan_test.go +++ b/store/tikv/scan_test.go @@ -40,7 +40,7 @@ func (s *testScanSuite) SetUpSuite(c *C) { func (s *testScanSuite) TearDownSuite(c *C) { txn := s.beginTxn(c) - scanner, err := txn.Seek(encodeKey(s.prefix, "")) + scanner, err := txn.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) c.Assert(scanner, NotNil) for scanner.Valid() { @@ -62,7 +62,25 @@ func (s *testScanSuite) beginTxn(c *C) *tikvTxn { return txn.(*tikvTxn) } -func (s *testScanSuite) TestSeek(c *C) { +func (s *testScanSuite) TestScan(c *C) { + check := func(c *C, scan kv.Iterator, rowNum int, keyOnly bool) { + for i := 0; i < rowNum; i++ { + k := scan.Key() + c.Assert([]byte(k), BytesEquals, encodeKey(s.prefix, s08d("key", i))) + if !keyOnly { + v := scan.Value() + c.Assert(v, BytesEquals, valueBytes(i)) + } + // Because newScan return first item without calling scan.Next() just like go-hbase, + // for-loop count will decrease 1. + if i < rowNum-1 { + scan.Next() + } + } + scan.Next() + c.Assert(scan.Valid(), IsFalse) + } + for _, rowNum := range s.rowNums { txn := s.beginTxn(c) for i := 0; i < rowNum; i++ { @@ -76,57 +94,35 @@ func (s *testScanSuite) TestSeek(c *C) { val, err := txn2.Get(encodeKey(s.prefix, s08d("key", 0))) c.Assert(err, IsNil) c.Assert(val, BytesEquals, valueBytes(0)) - scan, err := txn2.Seek(encodeKey(s.prefix, "")) + // Test scan without upperBound + scan, err := txn2.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) - - for i := 0; i < rowNum; i++ { - k := scan.Key() - c.Assert([]byte(k), BytesEquals, encodeKey(s.prefix, s08d("key", i))) - v := scan.Value() - c.Assert(v, BytesEquals, valueBytes(i)) - // Because newScan return first item without calling scan.Next() just like go-hbase, - // for-loop count will decrease 1. - if i < rowNum-1 { - scan.Next() - } - } - scan.Next() - c.Assert(scan.Valid(), IsFalse) + check(c, scan, rowNum, false) + // Test scan with upperBound + upperBound := rowNum / 2 + scan, err = txn2.Iter(encodeKey(s.prefix, ""), encodeKey(s.prefix, s08d("key", upperBound))) + c.Assert(err, IsNil) + check(c, scan, upperBound, false) txn3 := s.beginTxn(c) txn3.SetOption(kv.KeyOnly, true) - scan, err = txn3.Seek(encodeKey(s.prefix, "")) + // Test scan without upper bound + scan, err = txn3.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) - - for i := 0; i < rowNum; i++ { - k := scan.Key() - c.Assert([]byte(k), BytesEquals, encodeKey(s.prefix, s08d("key", i))) - // Because newScan return first item without calling scan.Next() just like go-hbase, - // for-loop count will decrease 1. - if i < rowNum-1 { - scan.Next() - } - } - scan.Next() - c.Assert(scan.Valid(), IsFalse) + check(c, scan, rowNum, true) + // test scan with upper bound + scan, err = txn3.Iter(encodeKey(s.prefix, ""), encodeKey(s.prefix, s08d("key", upperBound))) + c.Assert(err, IsNil) + check(c, scan, upperBound, true) // Restore KeyOnly to false txn3.SetOption(kv.KeyOnly, false) - scan, err = txn3.Seek(encodeKey(s.prefix, "")) + scan, err = txn3.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) - - for i := 0; i < rowNum; i++ { - k := scan.Key() - c.Assert([]byte(k), BytesEquals, encodeKey(s.prefix, s08d("key", i))) - v := scan.Value() - c.Assert(v, BytesEquals, valueBytes(i)) - // Because newScan return first item without calling scan.Next() just like go-hbase, - // for-loop count will decrease 1. - if i < rowNum-1 { - scan.Next() - } - } - scan.Next() - c.Assert(scan.Valid(), IsFalse) + check(c, scan, rowNum, true) + // test scan with upper bound + scan, err = txn3.Iter(encodeKey(s.prefix, ""), encodeKey(s.prefix, s08d("key", upperBound))) + c.Assert(err, IsNil) + check(c, scan, upperBound, true) } } diff --git a/store/tikv/snapshot.go b/store/tikv/snapshot.go index 4a3c8c0862e23..c8d5ff9f42b97 100644 --- a/store/tikv/snapshot.go +++ b/store/tikv/snapshot.go @@ -73,7 +73,8 @@ func (s *tikvSnapshot) BatchGet(keys []kv.Key) (map[string][]byte, error) { // We want [][]byte instead of []kv.Key, use some magic to save memory. bytesKeys := *(*[][]byte)(unsafe.Pointer(&keys)) - bo := NewBackoffer(context.Background(), batchGetMaxBackoff).WithVars(s.vars) + ctx := context.WithValue(context.Background(), txnStartKey, s.version.Ver) + bo := NewBackoffer(ctx, batchGetMaxBackoff).WithVars(s.vars) // Create a map to collect key-values from region servers. var mu sync.Mutex @@ -208,7 +209,8 @@ func (s *tikvSnapshot) batchGetSingleRegion(bo *Backoffer, batch batchKeys, coll // Get gets the value for key k from snapshot. func (s *tikvSnapshot) Get(k kv.Key) ([]byte, error) { - val, err := s.get(NewBackoffer(context.Background(), getMaxBackoff), k) + ctx := context.WithValue(context.Background(), txnStartKey, s.version.Ver) + val, err := s.get(NewBackoffer(ctx, getMaxBackoff), k) if err != nil { return nil, errors.Trace(err) } @@ -278,14 +280,14 @@ func (s *tikvSnapshot) get(bo *Backoffer, k kv.Key) ([]byte, error) { } } -// Seek return a list of key-value pair after `k`. -func (s *tikvSnapshot) Seek(k kv.Key) (kv.Iterator, error) { - scanner, err := newScanner(s, k, scanBatchSize) +// Iter return a list of key-value pair after `k`. +func (s *tikvSnapshot) Iter(k kv.Key, upperBound kv.Key) (kv.Iterator, error) { + scanner, err := newScanner(s, k, upperBound, scanBatchSize) return scanner, errors.Trace(err) } -// SeekReverse creates a reversed Iterator positioned on the first entry which key is less than k. -func (s *tikvSnapshot) SeekReverse(k kv.Key) (kv.Iterator, error) { +// IterReverse creates a reversed Iterator positioned on the first entry which key is less than k. +func (s *tikvSnapshot) IterReverse(k kv.Key) (kv.Iterator, error) { return nil, kv.ErrNotImplemented } diff --git a/store/tikv/snapshot_test.go b/store/tikv/snapshot_test.go index 4ea695f224e4b..61d5da2112863 100644 --- a/store/tikv/snapshot_test.go +++ b/store/tikv/snapshot_test.go @@ -42,7 +42,7 @@ func (s *testSnapshotSuite) SetUpSuite(c *C) { func (s *testSnapshotSuite) TearDownSuite(c *C) { txn := s.beginTxn(c) - scanner, err := txn.Seek(encodeKey(s.prefix, "")) + scanner, err := txn.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) c.Assert(scanner, NotNil) for scanner.Valid() { @@ -70,7 +70,7 @@ func (s *testSnapshotSuite) checkAll(keys []kv.Key, c *C) { m, err := snapshot.BatchGet(keys) c.Assert(err, IsNil) - scan, err := txn.Seek(encodeKey(s.prefix, "")) + scan, err := txn.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) cnt := 0 for scan.Valid() { diff --git a/store/tikv/store_test.go b/store/tikv/store_test.go index 8e54767417063..6fef8610854a6 100644 --- a/store/tikv/store_test.go +++ b/store/tikv/store_test.go @@ -229,7 +229,7 @@ func (s *testStoreSuite) TestRequestPriority(c *C) { // Cover Seek request. client.priority = pb.CommandPri_High txn.SetOption(kv.Priority, kv.PriorityHigh) - iter, err := txn.Seek([]byte("key")) + iter, err := txn.Iter([]byte("key"), nil) c.Assert(err, IsNil) for iter.Valid() { c.Assert(iter.Next(), IsNil) diff --git a/store/tikv/ticlient_test.go b/store/tikv/ticlient_test.go index e243dc396e7d1..d336e03e2b178 100644 --- a/store/tikv/ticlient_test.go +++ b/store/tikv/ticlient_test.go @@ -61,7 +61,7 @@ func clearStorage(store kv.Storage) error { if err != nil { return errors.Trace(err) } - iter, err := txn.Seek(nil) + iter, err := txn.Iter(nil, nil) if err != nil { return errors.Trace(err) } @@ -93,7 +93,7 @@ func (s *testTiclientSuite) SetUpSuite(c *C) { func (s *testTiclientSuite) TearDownSuite(c *C) { // Clean all data, or it may pollute other data. txn := s.beginTxn(c) - scanner, err := txn.Seek(encodeKey(s.prefix, "")) + scanner, err := txn.Iter(encodeKey(s.prefix, ""), nil) c.Assert(err, IsNil) c.Assert(scanner, NotNil) for scanner.Valid() { diff --git a/store/tikv/txn.go b/store/tikv/txn.go index 9b72431a0e07c..c94b2e66ee138 100644 --- a/store/tikv/txn.go +++ b/store/tikv/txn.go @@ -115,23 +115,23 @@ func (txn *tikvTxn) String() string { return fmt.Sprintf("%d", txn.StartTS()) } -func (txn *tikvTxn) Seek(k kv.Key) (kv.Iterator, error) { +func (txn *tikvTxn) Iter(k kv.Key, upperBound kv.Key) (kv.Iterator, error) { metrics.TiKVTxnCmdCounter.WithLabelValues("seek").Inc() start := time.Now() defer func() { metrics.TiKVTxnCmdHistogram.WithLabelValues("seek").Observe(time.Since(start).Seconds()) }() - return txn.us.Seek(k) + return txn.us.Iter(k, upperBound) } -// SeekReverse creates a reversed Iterator positioned on the first entry which key is less than k. -func (txn *tikvTxn) SeekReverse(k kv.Key) (kv.Iterator, error) { +// IterReverse creates a reversed Iterator positioned on the first entry which key is less than k. +func (txn *tikvTxn) IterReverse(k kv.Key) (kv.Iterator, error) { metrics.TiKVTxnCmdCounter.WithLabelValues("seek_reverse").Inc() start := time.Now() defer func() { metrics.TiKVTxnCmdHistogram.WithLabelValues("seek_reverse").Observe(time.Since(start).Seconds()) }() - return txn.us.SeekReverse(k) + return txn.us.IterReverse(k) } func (txn *tikvTxn) Delete(k kv.Key) error { diff --git a/structure/hash.go b/structure/hash.go index ecc70b64517a2..609c4df398b68 100644 --- a/structure/hash.go +++ b/structure/hash.go @@ -238,7 +238,7 @@ func (t *TxStructure) HClear(key []byte) error { func (t *TxStructure) iterateHash(key []byte, fn func(k []byte, v []byte) error) error { dataPrefix := t.hashDataKeyPrefix(key) - it, err := t.reader.Seek(dataPrefix) + it, err := t.reader.Iter(dataPrefix, dataPrefix.PrefixNext()) if err != nil { return errors.Trace(err) } diff --git a/table/tables/gen_expr.go b/table/tables/gen_expr.go index 8296d1491c9c4..85ea540eac85b 100644 --- a/table/tables/gen_expr.go +++ b/table/tables/gen_expr.go @@ -19,14 +19,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/parser" "github.com/pingcap/parser/ast" + "github.com/pingcap/parser/charset" "github.com/pingcap/parser/model" ) -// getDefaultCharsetAndCollate is copyed from ddl/ddl_api.go. -func getDefaultCharsetAndCollate() (string, string) { - return "utf8", "utf8_bin" -} - // nameResolver is the visitor to resolve table name and column name. // it combines TableInfo and ColumnInfo to a generation expression. type nameResolver struct { @@ -64,7 +60,7 @@ func (nr *nameResolver) Leave(inNode ast.Node) (node ast.Node, ok bool) { // it into ast.ExprNode. This function is for that. func parseExpression(expr string) (node ast.ExprNode, err error) { expr = fmt.Sprintf("select %s", expr) - charset, collation := getDefaultCharsetAndCollate() + charset, collation := charset.GetDefaultCharsetAndCollate() stmts, err := parser.New().Parse(expr, charset, collation) if err == nil { node = stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr diff --git a/table/tables/index.go b/table/tables/index.go index 3f9d77402a2ef..d86c45c6c1d72 100644 --- a/table/tables/index.go +++ b/table/tables/index.go @@ -240,7 +240,7 @@ func (c *index) Delete(sc *stmtctx.StatementContext, m kv.Mutator, indexedValues // Drop removes the KV index from store. func (c *index) Drop(rm kv.RetrieverMutator) error { - it, err := rm.Seek(c.prefix) + it, err := rm.Iter(c.prefix, c.prefix.PrefixNext()) if err != nil { return errors.Trace(err) } @@ -270,7 +270,8 @@ func (c *index) Seek(sc *stmtctx.StatementContext, r kv.Retriever, indexedValues return nil, false, errors.Trace(err) } - it, err := r.Seek(key) + upperBound := c.prefix.PrefixNext() + it, err := r.Iter(key, upperBound) if err != nil { return nil, false, errors.Trace(err) } @@ -284,7 +285,8 @@ func (c *index) Seek(sc *stmtctx.StatementContext, r kv.Retriever, indexedValues // SeekFirst returns an iterator which points to the first entry of the KV index. func (c *index) SeekFirst(r kv.Retriever) (iter table.IndexIterator, err error) { - it, err := r.Seek(c.prefix) + upperBound := c.prefix.PrefixNext() + it, err := r.Iter(c.prefix, upperBound) if err != nil { return nil, errors.Trace(err) } diff --git a/table/tables/tables.go b/table/tables/tables.go index 37dee1163c016..d1126be735582 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -782,7 +782,8 @@ func (t *tableCommon) buildIndexForRow(ctx sessionctx.Context, rm kv.RetrieverMu // IterRecords implements table.Table IterRecords interface. func (t *tableCommon) IterRecords(ctx sessionctx.Context, startKey kv.Key, cols []*table.Column, fn table.RecordIterFunc) error { - it, err := ctx.Txn().Seek(startKey) + prefix := t.RecordPrefix() + it, err := ctx.Txn().Iter(startKey, prefix.PrefixNext()) if err != nil { return errors.Trace(err) } @@ -798,7 +799,6 @@ func (t *tableCommon) IterRecords(ctx sessionctx.Context, startKey kv.Key, cols for _, col := range cols { colMap[col.ID] = &col.FieldType } - prefix := t.RecordPrefix() defaultVals := make([]types.Datum, len(cols)) for it.Valid() && it.Key().HasPrefix(prefix) { // first kv pair is row lock information. @@ -912,7 +912,7 @@ func (t *tableCommon) RebaseAutoID(ctx sessionctx.Context, newBase int64, isSetS // Seek implements table.Table Seek interface. func (t *tableCommon) Seek(ctx sessionctx.Context, h int64) (int64, bool, error) { seekKey := tablecodec.EncodeRowKeyWithHandle(t.physicalTableID, h) - iter, err := ctx.Txn().Seek(seekKey) + iter, err := ctx.Txn().Iter(seekKey, t.RecordPrefix().PrefixNext()) if !iter.Valid() || !iter.Key().HasPrefix(t.RecordPrefix()) { // No more records in the table, skip to the end. return 0, false, nil diff --git a/types/const_test.go b/types/const_test.go new file mode 100644 index 0000000000000..a68771ea0c512 --- /dev/null +++ b/types/const_test.go @@ -0,0 +1,321 @@ +// 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 types_test + +import ( + "flag" + "testing" + + . "github.com/pingcap/check" + "github.com/pingcap/parser" + "github.com/pingcap/parser/mysql" + "github.com/pingcap/tidb/domain" + "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/session" + "github.com/pingcap/tidb/store/mockstore" + "github.com/pingcap/tidb/store/mockstore/mocktikv" + "github.com/pingcap/tidb/util/testkit" + "github.com/pingcap/tidb/util/testleak" + "golang.org/x/net/context" +) + +func TestT(t *testing.T) { + CustomVerboseFlag = true + TestingT(t) +} + +var _ = Suite(&testMySQLConstSuite{}) + +type testMySQLConstSuite struct { + cluster *mocktikv.Cluster + mvccStore mocktikv.MVCCStore + store kv.Storage + dom *domain.Domain + *parser.Parser +} + +var mockTikv = flag.Bool("mockTikv", true, "use mock tikv store in executor test") + +func (s *testMySQLConstSuite) SetUpSuite(c *C) { + s.Parser = parser.New() + flag.Lookup("mockTikv") + useMockTikv := *mockTikv + if useMockTikv { + s.cluster = mocktikv.NewCluster() + mocktikv.BootstrapWithSingleStore(s.cluster) + s.mvccStore = mocktikv.MustNewMVCCStore() + store, err := mockstore.NewMockTikvStore( + mockstore.WithCluster(s.cluster), + mockstore.WithMVCCStore(s.mvccStore), + ) + c.Assert(err, IsNil) + s.store = store + session.SetSchemaLease(0) + session.SetStatsLease(0) + } + var err error + s.dom, err = session.BootstrapSession(s.store) + c.Assert(err, IsNil) +} + +func (s *testMySQLConstSuite) TearDownSuite(c *C) { + s.dom.Close() + s.store.Close() + testleak.AfterTest(c)() +} + +func (s *testMySQLConstSuite) TestGetSQLMode(c *C) { + positiveCases := []struct { + arg string + }{ + {"NO_ZERO_DATE"}, + {",,NO_ZERO_DATE"}, + {"NO_ZERO_DATE,NO_ZERO_IN_DATE"}, + {""}, + {", "}, + {","}, + } + + for _, t := range positiveCases { + _, err := mysql.GetSQLMode(mysql.FormatSQLModeStr(t.arg)) + c.Assert(err, IsNil) + } + + negativeCases := []struct { + arg string + }{ + {"NO_ZERO_DATE, NO_ZERO_IN_DATE"}, + {"NO_ZERO_DATE,adfadsdfasdfads"}, + {", ,NO_ZERO_DATE"}, + {" ,"}, + } + + for _, t := range negativeCases { + _, err := mysql.GetSQLMode(mysql.FormatSQLModeStr(t.arg)) + c.Assert(err, NotNil) + } +} + +func (s *testMySQLConstSuite) TestSQLMode(c *C) { + tests := []struct { + arg string + hasNoZeroDateMode bool + hasNoZeroInDateMode bool + hasErrorForDivisionByZeroMode bool + }{ + {"NO_ZERO_DATE", true, false, false}, + {"NO_ZERO_IN_DATE", false, true, false}, + {"ERROR_FOR_DIVISION_BY_ZERO", false, false, true}, + {"NO_ZERO_IN_DATE,NO_ZERO_DATE", true, true, false}, + {"NO_ZERO_DATE,NO_ZERO_IN_DATE", true, true, false}, + {"NO_ZERO_DATE,NO_ZERO_IN_DATE", true, true, false}, + {"NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO", true, true, true}, + {"NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO", false, true, true}, + {"", false, false, false}, + } + + for _, t := range tests { + sqlMode, _ := mysql.GetSQLMode(t.arg) + c.Assert(sqlMode.HasNoZeroDateMode(), Equals, t.hasNoZeroDateMode) + c.Assert(sqlMode.HasNoZeroInDateMode(), Equals, t.hasNoZeroInDateMode) + c.Assert(sqlMode.HasErrorForDivisionByZeroMode(), Equals, t.hasErrorForDivisionByZeroMode) + } +} + +func (s *testMySQLConstSuite) TestRealAsFloatMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a real);") + result := tk.MustQuery("desc t") + c.Check(result.Rows(), HasLen, 1) + row := result.Rows()[0] + c.Assert(row[1], Equals, "double") + + tk.MustExec("drop table if exists t;") + tk.MustExec("set sql_mode='REAL_AS_FLOAT'") + tk.MustExec("create table t (a real)") + result = tk.MustQuery("desc t") + c.Check(result.Rows(), HasLen, 1) + row = result.Rows()[0] + c.Assert(row[1], Equals, "float") +} + +func (s *testMySQLConstSuite) TestPipesAsConcatMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("SET sql_mode='PIPES_AS_CONCAT';") + r := tk.MustQuery(`SELECT 'hello' || 'world';`) + r.Check(testkit.Rows("helloworld")) +} + +func (s *testMySQLConstSuite) TestNoUnsignedSubtractionMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + ctx := context.Background() + tk.MustExec("set sql_mode='NO_UNSIGNED_SUBTRACTION'") + r := tk.MustQuery("SELECT CAST(0 as UNSIGNED) - 1;") + r.Check(testkit.Rows("-1")) + rs, _ := tk.Exec("SELECT CAST(18446744073709551615 as UNSIGNED) - 1;") + _, err := session.GetRows4Test(ctx, tk.Se, rs) + c.Assert(err, NotNil) + c.Assert(rs.Close(), IsNil) + rs, _ = tk.Exec("SELECT 1 - CAST(18446744073709551615 as UNSIGNED);") + _, err = session.GetRows4Test(ctx, tk.Se, rs) + c.Assert(err, NotNil) + c.Assert(rs.Close(), IsNil) + rs, _ = tk.Exec("SELECT CAST(-1 as UNSIGNED) - 1") + _, err = session.GetRows4Test(ctx, tk.Se, rs) + c.Assert(err, NotNil) + c.Assert(rs.Close(), IsNil) + rs, _ = tk.Exec("SELECT CAST(9223372036854775808 as UNSIGNED) - 1") + _, err = session.GetRows4Test(ctx, tk.Se, rs) + c.Assert(err, NotNil) + c.Assert(rs.Close(), IsNil) +} + +func (s *testMySQLConstSuite) TestHighNotPrecedenceMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int);") + tk.MustExec("insert into t1 values (0),(1),(NULL);") + r := tk.MustQuery(`SELECT * FROM t1 WHERE NOT a BETWEEN 2 AND 3;`) + r.Check(testkit.Rows("0", "1")) + r = tk.MustQuery(`SELECT NOT 1 BETWEEN -5 AND 5;`) + r.Check(testkit.Rows("0")) + tk.MustExec("set sql_mode='high_not_precedence';") + r = tk.MustQuery(`SELECT * FROM t1 WHERE NOT a BETWEEN 2 AND 3;`) + r.Check(testkit.Rows()) + r = tk.MustQuery(`SELECT NOT 1 BETWEEN -5 AND 5;`) + r.Check(testkit.Rows("1")) +} + +func (s *testMySQLConstSuite) TestIgnoreSpaceMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("set sql_mode=''") + tk.MustExec("CREATE TABLE COUNT (a bigint);") + tk.MustExec("DROP TABLE COUNT;") + tk.MustExec("CREATE TABLE `COUNT` (a bigint);") + tk.MustExec("DROP TABLE COUNT;") + _, err := tk.Exec("CREATE TABLE COUNT(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.COUNT(a bigint);") + tk.MustExec("DROP TABLE COUNT;") + + tk.MustExec("CREATE TABLE BIT_AND (a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + tk.MustExec("CREATE TABLE `BIT_AND` (a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + _, err = tk.Exec("CREATE TABLE BIT_AND(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.BIT_AND(a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + + tk.MustExec("CREATE TABLE NOW (a bigint);") + tk.MustExec("DROP TABLE NOW;") + tk.MustExec("CREATE TABLE `NOW` (a bigint);") + tk.MustExec("DROP TABLE NOW;") + _, err = tk.Exec("CREATE TABLE NOW(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.NOW(a bigint);") + tk.MustExec("DROP TABLE NOW;") + + tk.MustExec("set sql_mode='IGNORE_SPACE'") + _, err = tk.Exec("CREATE TABLE COUNT (a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE `COUNT` (a bigint);") + tk.MustExec("DROP TABLE COUNT;") + _, err = tk.Exec("CREATE TABLE COUNT(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.COUNT(a bigint);") + tk.MustExec("DROP TABLE COUNT;") + + _, err = tk.Exec("CREATE TABLE BIT_AND (a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE `BIT_AND` (a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + _, err = tk.Exec("CREATE TABLE BIT_AND(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.BIT_AND(a bigint);") + tk.MustExec("DROP TABLE BIT_AND;") + + _, err = tk.Exec("CREATE TABLE NOW (a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE `NOW` (a bigint);") + tk.MustExec("DROP TABLE NOW;") + _, err = tk.Exec("CREATE TABLE NOW(a bigint);") + c.Assert(err, NotNil) + tk.MustExec("CREATE TABLE test.NOW(a bigint);") + tk.MustExec("DROP TABLE NOW;") + +} + +func (s *testMySQLConstSuite) TestPadCharToFullLengthMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + // test type `CHAR(n)` + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a char(10));") + tk.MustExec("insert into t1 values ('xy');") + tk.MustExec("set sql_mode='';") + r := tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) + r.Check(testkit.Rows("0 2")) + r = tk.MustQuery(`SELECT count(*) FROM t1 WHERE a='xy ';`) + r.Check(testkit.Rows("0")) + tk.MustExec("set sql_mode='PAD_CHAR_TO_FULL_LENGTH';") + r = tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) + r.Check(testkit.Rows("1 10")) + r = tk.MustQuery(`SELECT count(*) FROM t1 WHERE a='xy ';`) + r.Check(testkit.Rows("1")) + + // test type `VARCHAR(n)` + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a varchar(10));") + tk.MustExec("insert into t1 values ('xy');") + tk.MustExec("set sql_mode='';") + r = tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) + r.Check(testkit.Rows("0 2")) + r = tk.MustQuery(`SELECT count(*) FROM t1 WHERE a='xy ';`) + r.Check(testkit.Rows("0")) + tk.MustExec("set sql_mode='PAD_CHAR_TO_FULL_LENGTH';") + r = tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) + r.Check(testkit.Rows("0 2")) +} + +func (s *testMySQLConstSuite) TestNoBackslashEscapesMode(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("set sql_mode=''") + r := tk.MustQuery("SELECT '\\\\'") + r.Check(testkit.Rows("\\")) + tk.MustExec("set sql_mode='NO_BACKSLASH_ESCAPES'") + r = tk.MustQuery("SELECT '\\\\'") + r.Check(testkit.Rows("\\\\")) +} + +func (s *testMySQLConstSuite) TestServerStatus(c *C) { + tests := []struct { + arg uint16 + IsCursorExists bool + }{ + {0, false}, + {mysql.ServerStatusInTrans | mysql.ServerStatusNoBackslashEscaped, false}, + {mysql.ServerStatusCursorExists, true}, + {mysql.ServerStatusCursorExists | mysql.ServerStatusLastRowSend, true}, + } + + for _, t := range tests { + ret := mysql.HasCursorExistsFlag(t.arg) + c.Assert(ret, Equals, t.IsCursorExists) + } +} diff --git a/types/field_type.go b/types/field_type.go index 9da5c8044a0fd..7fd81b97b9473 100644 --- a/types/field_type.go +++ b/types/field_type.go @@ -166,8 +166,7 @@ func DefaultTypeForValue(value interface{}, tp *FieldType) { // TODO: tp.Flen should be len(x) * 3 (max bytes length of CharsetUTF8) tp.Flen = len(x) tp.Decimal = UnspecifiedLength - tp.Charset = mysql.DefaultCharset - tp.Collate = mysql.DefaultCollationName + tp.Charset, tp.Collate = charset.GetDefaultCharsetAndCollate() case float64: tp.Tp = mysql.TypeDouble s := strconv.FormatFloat(x, 'f', -1, 64) diff --git a/types/field_type_test.go b/types/field_type_test.go index f1d0292270223..04ec2e4b90aba 100644 --- a/types/field_type_test.go +++ b/types/field_type_test.go @@ -172,7 +172,7 @@ func (s *testFieldTypeSuite) TestDefaultTypeForValue(c *C) { {nil, mysql.TypeNull, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag}, {1, mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag}, {uint64(1), mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag}, - {"abc", mysql.TypeVarString, 3, UnspecifiedLength, charset.CharsetUTF8, charset.CollationUTF8, 0}, + {"abc", mysql.TypeVarString, 3, UnspecifiedLength, charset.CharsetUTF8MB4, charset.CollationUTF8MB4, 0}, {1.1, mysql.TypeDouble, 3, -1, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag}, {[]byte("abc"), mysql.TypeBlob, 3, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag}, {HexLiteral{}, mysql.TypeVarString, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag}, diff --git a/util/admin/admin.go b/util/admin/admin.go index e49fb2ee9f8d1..ab324eb51f3d6 100644 --- a/util/admin/admin.go +++ b/util/admin/admin.go @@ -639,7 +639,10 @@ func rowWithCols(sessCtx sessionctx.Context, txn kv.Retriever, t table.Table, h // genExprs use to calculate generated column value. func iterRecords(sessCtx sessionctx.Context, retriever kv.Retriever, t table.Table, startKey kv.Key, cols []*table.Column, fn table.RecordIterFunc, genExprs map[model.TableColumnID]expression.Expression) error { - it, err := retriever.Seek(startKey) + prefix := t.RecordPrefix() + keyUpperBound := prefix.PrefixNext() + + it, err := retriever.Iter(startKey, keyUpperBound) if err != nil { return errors.Trace(err) } @@ -651,7 +654,6 @@ func iterRecords(sessCtx sessionctx.Context, retriever kv.Retriever, t table.Tab log.Debugf("startKey:%q, key:%q, value:%q", startKey, it.Key(), it.Value()) rowDecoder := makeRowDecoder(t, cols, genExprs) - prefix := t.RecordPrefix() for it.Valid() && it.Key().HasPrefix(prefix) { // first kv pair is row lock information. // TODO: check valid lock diff --git a/util/chunk/pool.go b/util/chunk/pool.go new file mode 100644 index 0000000000000..69a95735c2f3a --- /dev/null +++ b/util/chunk/pool.go @@ -0,0 +1,85 @@ +// Copyright 2018 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 chunk + +import ( + "sync" + + "github.com/pingcap/tidb/types" +) + +// Pool is the column pool. +// NOTE: Pool is non-copyable. +type Pool struct { + initCap int + + varLenColPool *sync.Pool + fixLenColPool4 *sync.Pool + fixLenColPool8 *sync.Pool + fixLenColPool16 *sync.Pool + fixLenColPool40 *sync.Pool +} + +// NewPool creates a new Pool. +func NewPool(initCap int) *Pool { + return &Pool{ + initCap: initCap, + varLenColPool: &sync.Pool{New: func() interface{} { return newVarLenColumn(initCap, nil) }}, + fixLenColPool4: &sync.Pool{New: func() interface{} { return newFixedLenColumn(4, initCap) }}, + fixLenColPool8: &sync.Pool{New: func() interface{} { return newFixedLenColumn(8, initCap) }}, + fixLenColPool16: &sync.Pool{New: func() interface{} { return newFixedLenColumn(16, initCap) }}, + fixLenColPool40: &sync.Pool{New: func() interface{} { return newFixedLenColumn(40, initCap) }}, + } +} + +// GetChunk gets a Chunk from the Pool. +func (p *Pool) GetChunk(fields []*types.FieldType) *Chunk { + chk := new(Chunk) + chk.capacity = p.initCap + chk.columns = make([]*column, len(fields)) + for i, f := range fields { + switch elemLen := getFixedLen(f); elemLen { + case varElemLen: + chk.columns[i] = p.varLenColPool.Get().(*column) + case 4: + chk.columns[i] = p.fixLenColPool4.Get().(*column) + case 8: + chk.columns[i] = p.fixLenColPool8.Get().(*column) + case 16: + chk.columns[i] = p.fixLenColPool16.Get().(*column) + case 40: + chk.columns[i] = p.fixLenColPool40.Get().(*column) + } + } + return chk +} + +// PutChunk puts a Chunk back to the Pool. +func (p *Pool) PutChunk(fields []*types.FieldType, chk *Chunk) { + for i, f := range fields { + switch elemLen := getFixedLen(f); elemLen { + case varElemLen: + p.varLenColPool.Put(chk.columns[i]) + case 4: + p.fixLenColPool4.Put(chk.columns[i]) + case 8: + p.fixLenColPool8.Put(chk.columns[i]) + case 16: + p.fixLenColPool16.Put(chk.columns[i]) + case 40: + p.fixLenColPool40.Put(chk.columns[i]) + } + } + chk.columns = nil // release the column references. +} diff --git a/util/chunk/pool_test.go b/util/chunk/pool_test.go new file mode 100644 index 0000000000000..f4b6b010a5c9b --- /dev/null +++ b/util/chunk/pool_test.go @@ -0,0 +1,113 @@ +// Copyright 2018 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 chunk + +import ( + "testing" + + "github.com/pingcap/check" + "github.com/pingcap/parser/mysql" + "github.com/pingcap/tidb/types" +) + +var _ = check.Suite(&poolTestSuite{}) + +type poolTestSuite struct{} + +func (s *poolTestSuite) TestNewPool(c *check.C) { + pool := NewPool(1024) + c.Assert(pool.initCap, check.Equals, 1024) + c.Assert(pool.varLenColPool, check.NotNil) + c.Assert(pool.fixLenColPool4, check.NotNil) + c.Assert(pool.fixLenColPool8, check.NotNil) + c.Assert(pool.fixLenColPool16, check.NotNil) + c.Assert(pool.fixLenColPool40, check.NotNil) +} + +func (s *poolTestSuite) TestPoolGetChunk(c *check.C) { + initCap := 1024 + pool := NewPool(initCap) + + fieldTypes := []*types.FieldType{ + {Tp: mysql.TypeVarchar}, + {Tp: mysql.TypeJSON}, + {Tp: mysql.TypeFloat}, + {Tp: mysql.TypeNewDecimal}, + {Tp: mysql.TypeDouble}, + {Tp: mysql.TypeLonglong}, + {Tp: mysql.TypeTimestamp}, + {Tp: mysql.TypeDatetime}, + } + + chk := pool.GetChunk(fieldTypes) + c.Assert(chk, check.NotNil) + c.Assert(chk.NumCols(), check.Equals, len(fieldTypes)) + c.Assert(chk.columns[0].elemBuf, check.IsNil) + c.Assert(chk.columns[1].elemBuf, check.IsNil) + c.Assert(len(chk.columns[2].elemBuf), check.Equals, getFixedLen(fieldTypes[2])) + c.Assert(len(chk.columns[3].elemBuf), check.Equals, getFixedLen(fieldTypes[3])) + c.Assert(len(chk.columns[4].elemBuf), check.Equals, getFixedLen(fieldTypes[4])) + c.Assert(len(chk.columns[5].elemBuf), check.Equals, getFixedLen(fieldTypes[5])) + c.Assert(len(chk.columns[6].elemBuf), check.Equals, getFixedLen(fieldTypes[6])) + c.Assert(len(chk.columns[7].elemBuf), check.Equals, getFixedLen(fieldTypes[7])) + + c.Assert(cap(chk.columns[2].data), check.Equals, initCap*getFixedLen(fieldTypes[2])) + c.Assert(cap(chk.columns[3].data), check.Equals, initCap*getFixedLen(fieldTypes[3])) + c.Assert(cap(chk.columns[4].data), check.Equals, initCap*getFixedLen(fieldTypes[4])) + c.Assert(cap(chk.columns[5].data), check.Equals, initCap*getFixedLen(fieldTypes[5])) + c.Assert(cap(chk.columns[6].data), check.Equals, initCap*getFixedLen(fieldTypes[6])) + c.Assert(cap(chk.columns[7].data), check.Equals, initCap*getFixedLen(fieldTypes[7])) +} + +func (s *poolTestSuite) TestPoolPutChunk(c *check.C) { + initCap := 1024 + pool := NewPool(initCap) + + fieldTypes := []*types.FieldType{ + {Tp: mysql.TypeVarchar}, + {Tp: mysql.TypeJSON}, + {Tp: mysql.TypeFloat}, + {Tp: mysql.TypeNewDecimal}, + {Tp: mysql.TypeDouble}, + {Tp: mysql.TypeLonglong}, + {Tp: mysql.TypeTimestamp}, + {Tp: mysql.TypeDatetime}, + } + + chk := pool.GetChunk(fieldTypes) + pool.PutChunk(fieldTypes, chk) + c.Assert(len(chk.columns), check.Equals, 0) +} + +func BenchmarkPoolChunkOperation(b *testing.B) { + pool := NewPool(1024) + + fieldTypes := []*types.FieldType{ + {Tp: mysql.TypeVarchar}, + {Tp: mysql.TypeJSON}, + {Tp: mysql.TypeFloat}, + {Tp: mysql.TypeNewDecimal}, + {Tp: mysql.TypeDouble}, + {Tp: mysql.TypeLonglong}, + {Tp: mysql.TypeTimestamp}, + {Tp: mysql.TypeDatetime}, + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + pool.PutChunk(fieldTypes, pool.GetChunk(fieldTypes)) + } + }) +} diff --git a/util/logutil/log.go b/util/logutil/log.go index 33051664728bf..24b306e25887f 100644 --- a/util/logutil/log.go +++ b/util/logutil/log.go @@ -35,6 +35,8 @@ const ( defaultLogLevel = log.InfoLevel // DefaultSlowThreshold is the default slow log threshold in millisecond. DefaultSlowThreshold = 300 + // DefaultQueryLogMaxLen is the default max length of the query in the log. + DefaultQueryLogMaxLen = 2048 ) // FileLogConfig serializes file log related config in toml/json. diff --git a/util/prefix_helper.go b/util/prefix_helper.go index 3fc172b02d783..cb01e3b57b102 100644 --- a/util/prefix_helper.go +++ b/util/prefix_helper.go @@ -26,7 +26,7 @@ import ( // ScanMetaWithPrefix scans metadata with the prefix. func ScanMetaWithPrefix(retriever kv.Retriever, prefix kv.Key, filter func(kv.Key, []byte) bool) error { - iter, err := retriever.Seek(prefix) + iter, err := retriever.Iter(prefix, prefix.PrefixNext()) if err != nil { return errors.Trace(err) } @@ -56,7 +56,7 @@ func ScanMetaWithPrefix(retriever kv.Retriever, prefix kv.Key, filter func(kv.Ke // DelKeyWithPrefix deletes keys with prefix. func DelKeyWithPrefix(rm kv.RetrieverMutator, prefix kv.Key) error { var keys []kv.Key - iter, err := rm.Seek(prefix) + iter, err := rm.Iter(prefix, prefix.PrefixNext()) if err != nil { return errors.Trace(err) }