diff --git a/cmd/explaintest/r/new_character_set.result b/cmd/explaintest/r/new_character_set.result index d1b05fbb4ed84..db70e0d82f72b 100644 --- a/cmd/explaintest/r/new_character_set.result +++ b/cmd/explaintest/r/new_character_set.result @@ -105,3 +105,9 @@ insert into t values('{"点赞": "你好"}', '{"点赞": "你好"}', '{"点赞": select * from t; j b s1 s2 st en {"点赞": "你好"} {"鐐硅禐": "浣犲ソ"} {"鐐硅禐": "浣犲ソ"} {"点赞": "你好"} {"点赞": "你好"} {"点赞": "你好"} +set names utf8mb4; +set @@character_set_client=gbk; +set @@character_set_connection=gbk; +select hex('一a'), '一a'; +hex('涓?') 涓? +E4B83F 涓? diff --git a/cmd/explaintest/t/new_character_set.test b/cmd/explaintest/t/new_character_set.test index 4f159d89c04cd..a608bda7622f5 100644 --- a/cmd/explaintest/t/new_character_set.test +++ b/cmd/explaintest/t/new_character_set.test @@ -63,3 +63,8 @@ set character_set_connection = utf8mb4; create table t(j json, b blob, s1 varchar(255) collate binary, s2 varchar(255), st set('{"点赞": "你好"}'), en enum('{"点赞": "你好"}')); insert into t values('{"点赞": "你好"}', '{"点赞": "你好"}', '{"点赞": "你好"}', '{"点赞": "你好"}', '{"点赞": "你好"}', '{"点赞": "你好"}'); select * from t; + +set names utf8mb4; +set @@character_set_client=gbk; +set @@character_set_connection=gbk; +select hex('一a'), '一a'; diff --git a/ddl/placement_policy_test.go b/ddl/placement_policy_test.go index e7537a4ae3947..8ad01dbdf5558 100644 --- a/ddl/placement_policy_test.go +++ b/ddl/placement_policy_test.go @@ -624,6 +624,124 @@ func (s *testDBSuite6) TestCreateTableWithPlacementPolicy(c *C) { tk.MustExec("drop placement policy if exists y") } +func (s *testDBSuite6) getClonedTable(dbName string, tableName string) (*model.TableInfo, error) { + tbl, err := s.dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName)) + if err != nil { + return nil, err + } + + tblMeta := tbl.Meta() + tblMeta = tblMeta.Clone() + policyRef := *tblMeta.PlacementPolicyRef + tblMeta.PlacementPolicyRef = &policyRef + return tblMeta, nil +} + +func (s *testDBSuite6) getClonedDatabase(dbName string) (*model.DBInfo, bool) { + db, ok := s.dom.InfoSchema().SchemaByName(model.NewCIStr(dbName)) + if !ok { + return nil, ok + } + + db = db.Clone() + policyRef := *db.PlacementPolicyRef + db.PlacementPolicyRef = &policyRef + return db, true +} + +func (s *testDBSuite6) TestCreateTableWithInfoPlacement(c *C) { + clearAllBundles(c) + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop database if exists test2") + tk.MustExec("drop placement policy if exists p1") + + tk.MustExec("create placement policy p1 followers=1") + defer tk.MustExec("drop placement policy if exists p1") + tk.MustExec("create table t1(a int) placement policy p1") + defer tk.MustExec("drop table if exists t1") + tk.MustExec("create database test2") + defer tk.MustExec("drop database if exists test2") + + tbl, err := s.getClonedTable("test", "t1") + c.Assert(err, IsNil) + policy, ok := s.dom.InfoSchema().PolicyByName(model.NewCIStr("p1")) + c.Assert(ok, IsTrue) + c.Assert(tbl.PlacementPolicyRef.ID, Equals, policy.ID) + + tk.MustExec("alter table t1 placement policy='default'") + tk.MustExec("drop placement policy p1") + tk.MustExec("create placement policy p1 followers=2") + c.Assert(s.dom.DDL().CreateTableWithInfo(tk.Se, model.NewCIStr("test2"), tbl, ddl.OnExistError), IsNil) + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `a` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustQuery("show create table test2.t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `a` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p1` */")) + tk.MustQuery("show placement where target='TABLE test2.t1'").Check(testkit.Rows("TABLE test2.t1 FOLLOWERS=2 PENDING")) + + // The ref id for new table should be the new policy id + tbl2, err := s.getClonedTable("test2", "t1") + c.Assert(err, IsNil) + policy2, ok := s.dom.InfoSchema().PolicyByName(model.NewCIStr("p1")) + c.Assert(ok, IsTrue) + c.Assert(tbl2.PlacementPolicyRef.ID, Equals, policy2.ID) + c.Assert(policy2.ID != policy.ID, IsTrue) + + // Test policy not exists + tbl2.Name = model.NewCIStr("t3") + tbl2.PlacementPolicyRef.Name = model.NewCIStr("pxx") + err = s.dom.DDL().CreateTableWithInfo(tk.Se, model.NewCIStr("test2"), tbl2, ddl.OnExistError) + c.Assert(err.Error(), Equals, "[schema:8239]Unknown placement policy 'pxx'") +} + +func (s *testDBSuite6) TestCreateSchemaWithInfoPlacement(c *C) { + clearAllBundles(c) + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop database if exists test2") + tk.MustExec("drop database if exists test3") + tk.MustExec("drop placement policy if exists p1") + + tk.MustExec("create placement policy p1 followers=1") + defer tk.MustExec("drop placement policy if exists p1") + tk.MustExec("create database test2 placement policy p1") + defer tk.MustExec("drop database if exists test2") + defer tk.MustExec("drop database if exists test3") + + db, ok := s.getClonedDatabase("test2") + c.Assert(ok, IsTrue) + policy, ok := s.dom.InfoSchema().PolicyByName(model.NewCIStr("p1")) + c.Assert(ok, IsTrue) + c.Assert(db.PlacementPolicyRef.ID, Equals, policy.ID) + + db2 := db.Clone() + db2.Name = model.NewCIStr("test3") + tk.MustExec("alter database test2 placement policy='default'") + tk.MustExec("drop placement policy p1") + tk.MustExec("create placement policy p1 followers=2") + c.Assert(s.dom.DDL().CreateSchemaWithInfo(tk.Se, db2, ddl.OnExistError), IsNil) + tk.MustQuery("show create database test2").Check(testkit.Rows("test2 CREATE DATABASE `test2` /*!40100 DEFAULT CHARACTER SET utf8mb4 */")) + tk.MustQuery("show create database test3").Check(testkit.Rows("test3 CREATE DATABASE `test3` /*!40100 DEFAULT CHARACTER SET utf8mb4 */ /*T![placement] PLACEMENT POLICY=`p1` */")) + tk.MustQuery("show placement where target='DATABASE test3'").Check(testkit.Rows("DATABASE test3 FOLLOWERS=2 SCHEDULED")) + + // The ref id for new table should be the new policy id + db2, ok = s.getClonedDatabase("test3") + c.Assert(ok, IsTrue) + policy2, ok := s.dom.InfoSchema().PolicyByName(model.NewCIStr("p1")) + c.Assert(ok, IsTrue) + c.Assert(db2.PlacementPolicyRef.ID, Equals, policy2.ID) + c.Assert(policy2.ID != policy.ID, IsTrue) + + // Test policy not exists + db2.Name = model.NewCIStr("test4") + db2.PlacementPolicyRef.Name = model.NewCIStr("p2") + err := s.dom.DDL().CreateSchemaWithInfo(tk.Se, db2, ddl.OnExistError) + c.Assert(err.Error(), Equals, "[schema:8239]Unknown placement policy 'p2'") +} + func (s *testDBSuite6) TestDropPlacementPolicyInUse(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/ddl/placement_sql_test.go b/ddl/placement_sql_test.go index f411578550207..c1febbe18218b 100644 --- a/ddl/placement_sql_test.go +++ b/ddl/placement_sql_test.go @@ -659,6 +659,53 @@ func (s *testDBSuite6) TestPlacementMode(c *C) { " PARTITION `p1` VALUES LESS THAN (1000),\n" + " PARTITION `p2` VALUES LESS THAN (10000),\n" + " PARTITION `p3` VALUES LESS THAN (100000))")) + + // create tableWithInfo in ignore mode + tk.MustExec("drop table if exists t2") + tbl, err := s.getClonedTable("test", "t1") + c.Assert(err, IsNil) + c.Assert(tbl.PlacementPolicyRef, NotNil) + tbl.Name = model.NewCIStr("t2") + err = s.dom.DDL().CreateTableWithInfo(tk.Se, model.NewCIStr("test"), tbl, ddl.OnExistError) + c.Assert(err, IsNil) + tk.MustQuery("show create table t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='aaa'")) + + // createTableWithInfo in ignore mode (policy not exists) + tk.MustExec("drop table if exists t2") + tbl, err = s.getClonedTable("test", "t1") + c.Assert(err, IsNil) + c.Assert(tbl.PlacementPolicyRef, NotNil) + tbl.Name = model.NewCIStr("t2") + tbl.PlacementPolicyRef.Name = model.NewCIStr("pxx") + err = s.dom.DDL().CreateTableWithInfo(tk.Se, model.NewCIStr("test"), tbl, ddl.OnExistError) + c.Assert(err, IsNil) + tk.MustQuery("show create table t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='aaa'")) + + // createSchemaWithInfo in ignore mode + tk.MustExec("drop database if exists db2") + db1, ok := s.getClonedDatabase("db1") + c.Assert(ok, IsTrue) + c.Assert(db1.PlacementPolicyRef, NotNil) + db1.Name = model.NewCIStr("db2") + err = s.dom.DDL().CreateSchemaWithInfo(tk.Se, db1, ddl.OnExistError) + c.Assert(err, IsNil) + tk.MustQuery("show create database db2").Check(testkit.Rows("db2 CREATE DATABASE `db2` /*!40100 DEFAULT CHARACTER SET utf8mb4 */")) + + // createSchemaWithInfo in ignore mode (policy not exists) + tk.MustExec("drop database if exists db2") + db1, ok = s.getClonedDatabase("db1") + c.Assert(ok, IsTrue) + c.Assert(db1.PlacementPolicyRef, NotNil) + db1.Name = model.NewCIStr("db2") + db1.PlacementPolicyRef.Name = model.NewCIStr("pxx") + err = s.dom.DDL().CreateSchemaWithInfo(tk.Se, db1, ddl.OnExistError) + c.Assert(err, IsNil) + tk.MustQuery("show create database db2").Check(testkit.Rows("db2 CREATE DATABASE `db2` /*!40100 DEFAULT CHARACTER SET utf8mb4 */")) + } func (s *testDBSuite6) TestPlacementTiflashCheck(c *C) { diff --git a/expression/distsql_builtin.go b/expression/distsql_builtin.go index d1f054a9cb1dd..655031370a10c 100644 --- a/expression/distsql_builtin.go +++ b/expression/distsql_builtin.go @@ -946,6 +946,8 @@ func getSignatureByPB(ctx sessionctx.Context, sigCode tipb.ScalarFuncSig, tp *ti f = &builtinCharSig{base} case tipb.ScalarFuncSig_CharLengthUTF8: f = &builtinCharLengthUTF8Sig{base} + case tipb.ScalarFuncSig_CharLength: + f = &builtinCharLengthBinarySig{base} case tipb.ScalarFuncSig_Concat: f = &builtinConcatSig{base, maxAllowedPacket} case tipb.ScalarFuncSig_ConcatWS: diff --git a/expression/integration_test.go b/expression/integration_test.go index 970a45fd67bba..c0be08752aebb 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -810,6 +810,12 @@ func TestStringBuiltin(t *testing.T) { result = tk.MustQuery("select a,b,concat_ws(',',a,b) from t") result.Check(testkit.Rows("114.57011441 38.04620115 114.57011441,38.04620115", "-38.04620119 38.04620115 -38.04620119,38.04620115")) + + // For issue 31603, only affects unistore. + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 varbinary(100));") + tk.MustExec("insert into t1 values('abc');") + tk.MustQuery("select 1 from t1 where char_length(c1) = 10;").Check(testkit.Rows()) } func TestInvalidStrings(t *testing.T) { diff --git a/parser/ast/ast.go b/parser/ast/ast.go index 1f76a26ce7e78..dda80b07e105e 100644 --- a/parser/ast/ast.go +++ b/parser/ast/ast.go @@ -18,6 +18,7 @@ package ast import ( "io" + "github.com/pingcap/tidb/parser/charset" "github.com/pingcap/tidb/parser/format" "github.com/pingcap/tidb/parser/model" "github.com/pingcap/tidb/parser/types" @@ -37,10 +38,12 @@ type Node interface { // children should be skipped. Otherwise, call its children in particular order that // later elements depends on former elements. Finally, return visitor.Leave. Accept(v Visitor) (node Node, ok bool) - // Text returns the original text of the element. + // Text returns the utf8 encoding text of the element. Text() string + // OriginalText returns the original text of the element. + OriginalText() string // SetText sets original text to the Node. - SetText(text string) + SetText(enc charset.Encoding, text string) // SetOriginTextPosition set the start offset of this node in the origin text. SetOriginTextPosition(offset int) // OriginTextPosition get the start offset of this node in the origin text. diff --git a/parser/ast/base.go b/parser/ast/base.go index a1ecc49559ffb..a7dcc9cefdc55 100644 --- a/parser/ast/base.go +++ b/parser/ast/base.go @@ -14,12 +14,19 @@ package ast import ( + "sync" + + "github.com/pingcap/tidb/parser/charset" "github.com/pingcap/tidb/parser/types" ) // node is the struct implements Node interface except for Accept method. // Node implementations should embed it in. type node struct { + utf8Text string + enc charset.Encoding + once *sync.Once + text string offset int } @@ -35,12 +42,30 @@ func (n *node) OriginTextPosition() int { } // SetText implements Node interface. -func (n *node) SetText(text string) { +func (n *node) SetText(enc charset.Encoding, text string) { + n.enc = enc n.text = text + n.once = &sync.Once{} } // Text implements Node interface. func (n *node) Text() string { + if n.once == nil { + return n.text + } + n.once.Do(func() { + if n.enc == nil { + n.utf8Text = n.text + return + } + utf8Lit, _ := n.enc.Transform(nil, charset.HackSlice(n.text), charset.OpDecodeReplace) + n.utf8Text = charset.HackString(utf8Lit) + }) + return n.utf8Text +} + +// OriginalText implements Node interface. +func (n *node) OriginalText() string { return n.text } diff --git a/parser/ast/base_test.go b/parser/ast/base_test.go new file mode 100644 index 0000000000000..17a79321b1478 --- /dev/null +++ b/parser/ast/base_test.go @@ -0,0 +1,42 @@ +// Copyright 2022 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 ast is the abstract syntax tree parsed from a SQL statement by parser. +// It can be analysed and transformed by optimizer. +package ast + +import ( + "testing" + + "github.com/pingcap/tidb/parser/charset" + "github.com/stretchr/testify/require" +) + +func TestNodeSetText(t *testing.T) { + n := &node{} + tests := []struct { + text string + enc charset.Encoding + expectUTF8Text string + expectText string + }{ + {"你好", nil, "你好", "你好"}, + {"\xd2\xbb", charset.EncodingGBKImpl, "一", "\xd2\xbb"}, + {"\xc1\xd0", charset.EncodingGBKImpl, "列", "\xc1\xd0"}, + } + for _, tt := range tests { + n.SetText(tt.enc, tt.text) + require.Equal(t, tt.expectUTF8Text, n.Text()) + require.Equal(t, tt.expectText, n.OriginalText()) + } +} diff --git a/parser/ast/util_test.go b/parser/ast/util_test.go index d8cb06eee448c..29e813b1131b8 100644 --- a/parser/ast/util_test.go +++ b/parser/ast/util_test.go @@ -120,7 +120,7 @@ type nodeTextCleaner struct { // Enter implements Visitor interface. func (checker *nodeTextCleaner) Enter(in Node) (out Node, skipChildren bool) { - in.SetText("") + in.SetText(nil, "") in.SetOriginTextPosition(0) switch node := in.(type) { case *Constraint: diff --git a/parser/parser.go b/parser/parser.go index f28afd45eb756..3c9d4d333202c 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -12614,7 +12614,7 @@ yynewstate: startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.endOffset(&yyS[yypt-1]) expr := yyS[yypt-2].expr - expr.SetText(parser.src[startOffset:endOffset]) + expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) parser.yyVAL.item = &ast.ColumnOption{ Tp: ast.ColumnOptionGenerated, @@ -13517,7 +13517,7 @@ yynewstate: { startOffset := parser.startOffset(&yyS[yypt-1]) selStmt := yyS[yypt-1].statement.(ast.StmtNode) - selStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.CreateViewStmt{ OrReplace: yyS[yypt-9].item.(bool), ViewName: yyS[yypt-4].item.(*ast.TableName), @@ -13532,7 +13532,7 @@ yynewstate: if yyS[yypt-0].item != nil { x.CheckOption = yyS[yypt-0].item.(model.ViewCheckOption) endOffset := parser.startOffset(&yyS[yypt]) - selStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) + selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) } else { x.CheckOption = model.CheckOptionCascaded } @@ -13790,7 +13790,7 @@ yynewstate: TracePlan: false, } startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(string(parser.src[startOffset:])) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) } case 465: { @@ -13800,7 +13800,7 @@ yynewstate: TracePlan: false, } startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(string(parser.src[startOffset:])) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) } case 466: { @@ -13809,7 +13809,7 @@ yynewstate: TracePlan: true, } startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(string(parser.src[startOffset:])) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) } case 467: { @@ -13819,7 +13819,7 @@ yynewstate: TracePlanTarget: yyS[yypt-1].ident, } startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(string(parser.src[startOffset:])) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) } case 471: { @@ -14599,7 +14599,7 @@ yynewstate: last := fl[len(fl)-1] if last.Expr != nil && last.AsName.O == "" { lastEnd := parser.endOffset(&yyS[yypt-1]) - last.SetText(parser.src[last.Offset:lastEnd]) + last.SetText(parser.lexer.client, parser.src[last.Offset:lastEnd]) } newField := yyS[yypt-0].item.(*ast.SelectField) newField.Offset = parser.startOffset(&yyS[yypt]) @@ -15261,7 +15261,7 @@ yynewstate: startOffset := parser.startOffset(&yyS[yypt-1]) endOffset := parser.endOffset(&yyS[yypt]) expr := yyS[yypt-1].expr - expr.SetText(parser.src[startOffset:endOffset]) + expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) parser.yyVAL.expr = &ast.ParenthesesExpr{Expr: expr} } case 1233: @@ -16409,7 +16409,7 @@ yynewstate: lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { lastEnd := yyS[yypt-1].offset - 1 - lastField.SetText(parser.src[lastField.Offset:lastEnd]) + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) } if yyS[yypt-0].item != nil { st.Where = yyS[yypt-0].item.(ast.ExprNode) @@ -16422,7 +16422,7 @@ yynewstate: lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { lastEnd := parser.endOffset(&yyS[yypt-5]) - lastField.SetText(parser.src[lastField.Offset:lastEnd]) + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) } if yyS[yypt-3].item != nil { st.Where = yyS[yypt-3].item.(ast.ExprNode) @@ -16530,7 +16530,7 @@ yynewstate: lastEnd-- } } - lastField.SetText(src[lastField.Offset:lastEnd]) + lastField.SetText(parser.lexer.client, src[lastField.Offset:lastEnd]) } if yyS[yypt-5].item != nil { st.Where = yyS[yypt-5].item.(ast.ExprNode) @@ -17336,14 +17336,14 @@ yynewstate: parser.setLastSelectFieldText(rs, endOffset) src := parser.src // See the implementation of yyParse function - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} } case 1645: { rs := yyS[yypt-1].statement.(*ast.SetOprStmt) src := parser.src - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} } case 1646: @@ -17353,7 +17353,7 @@ yynewstate: parser.setLastSelectFieldText(rs, endOffset) src := parser.src // See the implementation of yyParse function - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} } case 1647: @@ -17371,11 +17371,11 @@ yynewstate: endOffset := parser.endOffset(&yyS[yypt]) parser.setLastSelectFieldText(rs, endOffset) src := parser.src - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} case *ast.SetOprStmt: src := parser.src - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} } } @@ -18986,7 +18986,7 @@ yynewstate: if yyS[yypt-0].statement != nil { s := yyS[yypt-0].statement if lexer, ok := yylex.(stmtTexter); ok { - s.SetText(lexer.stmtText()) + s.SetText(parser.lexer.client, lexer.stmtText()) } parser.result = append(parser.result, s) } @@ -18996,7 +18996,7 @@ yynewstate: if yyS[yypt-0].statement != nil { s := yyS[yypt-0].statement if lexer, ok := yylex.(stmtTexter); ok { - s.SetText(lexer.stmtText()) + s.SetText(parser.lexer.client, lexer.stmtText()) } parser.result = append(parser.result, s) } @@ -20282,11 +20282,11 @@ yynewstate: startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.startOffset(&yyS[yypt-1]) originStmt := yyS[yypt-2].statement - originStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) startOffset = parser.startOffset(&yyS[yypt]) hintedStmt := yyS[yypt-0].statement - hintedStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.CreateBindingStmt{ OriginNode: originStmt, @@ -20300,7 +20300,7 @@ yynewstate: { startOffset := parser.startOffset(&yyS[yypt]) originStmt := yyS[yypt-0].statement - originStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.DropBindingStmt{ OriginNode: originStmt, @@ -20314,11 +20314,11 @@ yynewstate: startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.startOffset(&yyS[yypt-1]) originStmt := yyS[yypt-2].statement - originStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) startOffset = parser.startOffset(&yyS[yypt]) hintedStmt := yyS[yypt-0].statement - hintedStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.DropBindingStmt{ OriginNode: originStmt, @@ -21213,7 +21213,7 @@ yynewstate: Limit: nil, } startOffset := parser.startOffset(&yyS[yypt]) - x.Stmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) parser.yyVAL.statement = x } @@ -21229,7 +21229,7 @@ yynewstate: Limit: nil, } startOffset := parser.startOffset(&yyS[yypt]) - x.Stmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) parser.yyVAL.statement = x } diff --git a/parser/parser.y b/parser/parser.y index e436a0871d1c6..d20da04e8f352 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -3039,7 +3039,7 @@ ColumnOption: startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.endOffset(&yyS[yypt-1]) expr := $4 - expr.SetText(parser.src[startOffset:endOffset]) + expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) $$ = &ast.ColumnOption{ Tp: ast.ColumnOptionGenerated, @@ -4176,7 +4176,7 @@ CreateViewStmt: { startOffset := parser.startOffset(&yyS[yypt-1]) selStmt := $10.(ast.StmtNode) - selStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.CreateViewStmt{ OrReplace: $2.(bool), ViewName: $7.(*ast.TableName), @@ -4191,7 +4191,7 @@ CreateViewStmt: if $11 != nil { x.CheckOption = $11.(model.ViewCheckOption) endOffset := parser.startOffset(&yyS[yypt]) - selStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) + selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) } else { x.CheckOption = model.CheckOptionCascaded } @@ -4534,7 +4534,7 @@ TraceStmt: TracePlan: false, } startOffset := parser.startOffset(&yyS[yypt]) - $2.SetText(string(parser.src[startOffset:])) + $2.SetText(parser.lexer.client, string(parser.src[startOffset:])) } | "TRACE" "FORMAT" "=" stringLit TraceableStmt { @@ -4544,7 +4544,7 @@ TraceStmt: TracePlan: false, } startOffset := parser.startOffset(&yyS[yypt]) - $5.SetText(string(parser.src[startOffset:])) + $5.SetText(parser.lexer.client, string(parser.src[startOffset:])) } | "TRACE" "PLAN" TraceableStmt { @@ -4553,7 +4553,7 @@ TraceStmt: TracePlan: true, } startOffset := parser.startOffset(&yyS[yypt]) - $3.SetText(string(parser.src[startOffset:])) + $3.SetText(parser.lexer.client, string(parser.src[startOffset:])) } | "TRACE" "PLAN" "TARGET" "=" stringLit TraceableStmt { @@ -4563,7 +4563,7 @@ TraceStmt: TracePlanTarget: $5, } startOffset := parser.startOffset(&yyS[yypt]) - $6.SetText(string(parser.src[startOffset:])) + $6.SetText(parser.lexer.client, string(parser.src[startOffset:])) } ExplainSym: @@ -5496,7 +5496,7 @@ FieldList: last := fl[len(fl)-1] if last.Expr != nil && last.AsName.O == "" { lastEnd := parser.endOffset(&yyS[yypt-1]) - last.SetText(parser.src[last.Offset:lastEnd]) + last.SetText(parser.lexer.client, parser.src[last.Offset:lastEnd]) } newField := $3.(*ast.SelectField) newField.Offset = parser.startOffset(&yyS[yypt]) @@ -6762,7 +6762,7 @@ SimpleExpr: startOffset := parser.startOffset(&yyS[yypt-1]) endOffset := parser.endOffset(&yyS[yypt]) expr := $2 - expr.SetText(parser.src[startOffset:endOffset]) + expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) $$ = &ast.ParenthesesExpr{Expr: expr} } | '(' ExpressionList ',' Expression ')' @@ -8077,7 +8077,7 @@ SelectStmtFromDualTable: lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { lastEnd := yyS[yypt-1].offset - 1 - lastField.SetText(parser.src[lastField.Offset:lastEnd]) + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) } if $3 != nil { st.Where = $3.(ast.ExprNode) @@ -8092,7 +8092,7 @@ SelectStmtFromTable: lastField := st.Fields.Fields[len(st.Fields.Fields)-1] if lastField.Expr != nil && lastField.AsName.O == "" { lastEnd := parser.endOffset(&yyS[yypt-5]) - lastField.SetText(parser.src[lastField.Offset:lastEnd]) + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) } if $4 != nil { st.Where = $4.(ast.ExprNode) @@ -8210,7 +8210,7 @@ SelectStmt: lastEnd-- } } - lastField.SetText(src[lastField.Offset:lastEnd]) + lastField.SetText(parser.lexer.client, src[lastField.Offset:lastEnd]) } if $2 != nil { st.Where = $2.(ast.ExprNode) @@ -9152,14 +9152,14 @@ SubSelect: parser.setLastSelectFieldText(rs, endOffset) src := parser.src // See the implementation of yyParse function - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: rs} } | '(' SetOprStmt ')' { rs := $2.(*ast.SetOprStmt) src := parser.src - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: rs} } | '(' SelectStmtWithClause ')' @@ -9169,7 +9169,7 @@ SubSelect: parser.setLastSelectFieldText(rs, endOffset) src := parser.src // See the implementation of yyParse function - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: rs} } | '(' SubSelect ')' @@ -9187,11 +9187,11 @@ SubSelect: endOffset := parser.endOffset(&yyS[yypt]) parser.setLastSelectFieldText(rs, endOffset) src := parser.src - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: rs} case *ast.SetOprStmt: src := parser.src - rs.SetText(src[yyS[yypt-1].offset:yyS[yypt].offset]) + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) $$ = &ast.SubqueryExpr{Query: rs} } } @@ -11072,7 +11072,7 @@ StatementList: if $1 != nil { s := $1 if lexer, ok := yylex.(stmtTexter); ok { - s.SetText(lexer.stmtText()) + s.SetText(parser.lexer.client, lexer.stmtText()) } parser.result = append(parser.result, s) } @@ -11082,7 +11082,7 @@ StatementList: if $3 != nil { s := $3 if lexer, ok := yylex.(stmtTexter); ok { - s.SetText(lexer.stmtText()) + s.SetText(parser.lexer.client, lexer.stmtText()) } parser.result = append(parser.result, s) } @@ -12594,11 +12594,11 @@ CreateBindingStmt: startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.startOffset(&yyS[yypt-1]) originStmt := $5 - originStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) startOffset = parser.startOffset(&yyS[yypt]) hintedStmt := $7 - hintedStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.CreateBindingStmt{ OriginNode: originStmt, @@ -12621,7 +12621,7 @@ DropBindingStmt: { startOffset := parser.startOffset(&yyS[yypt]) originStmt := $5 - originStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.DropBindingStmt{ OriginNode: originStmt, @@ -12635,11 +12635,11 @@ DropBindingStmt: startOffset := parser.startOffset(&yyS[yypt-2]) endOffset := parser.startOffset(&yyS[yypt-1]) originStmt := $5 - originStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset])) + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) startOffset = parser.startOffset(&yyS[yypt]) hintedStmt := $7 - hintedStmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) x := &ast.DropBindingStmt{ OriginNode: originStmt, @@ -13726,7 +13726,7 @@ PlanReplayerStmt: Limit: nil, } startOffset := parser.startOffset(&yyS[yypt]) - x.Stmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) $$ = x } @@ -13742,7 +13742,7 @@ PlanReplayerStmt: Limit: nil, } startOffset := parser.startOffset(&yyS[yypt]) - x.Stmt.SetText(strings.TrimSpace(parser.src[startOffset:])) + x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) $$ = x } diff --git a/parser/parser_test.go b/parser/parser_test.go index 4c4f8c9b1b05b..a79fcafdab845 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -5882,7 +5882,7 @@ type nodeTextCleaner struct { // Enter implements Visitor interface. func (checker *nodeTextCleaner) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - in.SetText("") + in.SetText(nil, "") in.SetOriginTextPosition(0) switch node := in.(type) { case *ast.CreateTableStmt: diff --git a/parser/yy_parser.go b/parser/yy_parser.go index b2ed65713908c..808b4a216ebeb 100644 --- a/parser/yy_parser.go +++ b/parser/yy_parser.go @@ -217,8 +217,8 @@ func (parser *Parser) setLastSelectFieldText(st *ast.SelectStmt, lastEnd int) { return } lastField := st.Fields.Fields[len(st.Fields.Fields)-1] - if lastField.Offset+len(lastField.Text()) >= len(parser.src)-1 { - lastField.SetText(parser.src[lastField.Offset:lastEnd]) + if lastField.Offset+len(lastField.OriginalText()) >= len(parser.src)-1 { + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) } } diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index d595965a1e81f..755ef9e430430 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -3191,7 +3191,7 @@ func unfoldWildStar(field *ast.SelectField, outputName types.NameSlice, column [ }} colName.SetType(col.GetType()) field := &ast.SelectField{Expr: colName} - field.SetText(name.ColName.O) + field.SetText(nil, name.ColName.O) resultList = append(resultList, field) } } diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 0dfd7ea71c743..c5de336914f66 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -3267,7 +3267,7 @@ func (b *PlanBuilder) resolveGeneratedColumns(ctx context.Context, columns []*ta continue } columnName := &ast.ColumnName{Name: column.Name} - columnName.SetText(column.Name.O) + columnName.SetText(nil, column.Name.O) idx, err := expression.FindFieldName(mockPlan.OutputNames(), columnName) if err != nil { diff --git a/planner/core/preprocess.go b/planner/core/preprocess.go index 7eb2a3c041c52..44f616dec9e89 100644 --- a/planner/core/preprocess.go +++ b/planner/core/preprocess.go @@ -338,7 +338,7 @@ func (p *preprocessor) Enter(in ast.Node) (out ast.Node, skipChildren bool) { func EraseLastSemicolon(stmt ast.StmtNode) { sql := stmt.Text() if len(sql) > 0 && sql[len(sql)-1] == ';' { - stmt.SetText(sql[:len(sql)-1]) + stmt.SetText(nil, sql[:len(sql)-1]) } }