Skip to content

Commit

Permalink
cherry pick pingcap#31779 to release-5.0
Browse files Browse the repository at this point in the history
Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
  • Loading branch information
mjonss authored and ti-srebot committed Feb 18, 2022
1 parent 6416f8d commit f556c0f
Show file tree
Hide file tree
Showing 5 changed files with 351 additions and 6 deletions.
227 changes: 227 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3887,9 +3887,236 @@ func (s *testIntegrationSuite) TestIssue27797(c *C) {
func (s *testIntegrationSuite) TestIssue24095(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test;")
<<<<<<< HEAD
tk.MustExec("drop table if exists t;")
tk.MustExec("create table t (id int, value decimal(10,5));")
tk.MustExec("desc format = 'brief' select count(*) from t join (select t.id, t.value v1 from t join t t1 on t.id = t1.id order by t.value limit 1) v on v.id = t.id and v.v1 = t.value;")
=======

tk.MustExec("drop table if exists t1, t2;")
tk.MustExec("create table t1(c1 int, c2 int, c3 int, primary key(c1), key(c2));")
tk.MustExec("insert into t1 values(1, 1, 1);")
tk.MustExec("insert into t1 values(2, 2, 2);")
tk.MustExec("create table t2(c1 int, c2 int, c3 int);")
tk.MustExec("insert into t2 values(1, 1, 1);")
tk.MustExec("insert into t2 values(2, 2, 2);")

tk.MustExec("drop table if exists tt1, tt2;")
tk.MustExec("create table tt1 (c_int int, c_str varchar(40), c_datetime datetime, c_decimal decimal(12, 6), primary key(c_int), key(c_int), key(c_str), unique key(c_decimal), key(c_datetime));")
tk.MustExec("create table tt2 like tt1 ;")
tk.MustExec(`insert into tt1 (c_int, c_str, c_datetime, c_decimal) values (6, 'sharp payne', '2020-06-07 10:40:39', 6.117000) ,
(7, 'objective kare', '2020-02-05 18:47:26', 1.053000) ,
(8, 'thirsty pasteur', '2020-01-02 13:06:56', 2.506000) ,
(9, 'blissful wilbur', '2020-06-04 11:34:04', 9.144000) ,
(10, 'reverent mclean', '2020-02-12 07:36:26', 7.751000) ;`)
tk.MustExec(`insert into tt2 (c_int, c_str, c_datetime, c_decimal) values (6, 'beautiful joliot', '2020-01-16 01:44:37', 5.627000) ,
(7, 'hopeful blackburn', '2020-05-23 21:44:20', 7.890000) ,
(8, 'ecstatic davinci', '2020-02-01 12:27:17', 5.648000) ,
(9, 'hopeful lewin', '2020-05-05 05:58:25', 7.288000) ,
(10, 'sharp jennings', '2020-01-28 04:35:03', 9.758000) ;`)

var input []string
var output []struct {
SQL string
Plan []string
Res []string
}
s.testData.GetTestCases(c, &input, &output)
for i, tt := range input {
s.testData.OnRecord(func() {
output[i].SQL = tt
output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery("explain format=brief " + tt).Rows())
output[i].Res = s.testData.ConvertRowsToStrings(tk.MustQuery(tt).Rows())
})
tk.MustQuery("explain format=brief " + tt).Check(testkit.Rows(output[i].Plan...))
tk.MustQuery(tt).Check(testkit.Rows(output[i].Res...))
}

}

func (s *testIntegrationSuite) TestIssue20510(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t1, t2")
tk.MustExec("CREATE TABLE t1 (a int PRIMARY KEY, b int)")
tk.MustExec("CREATE TABLE t2 (a int PRIMARY KEY, b int)")
tk.MustExec("INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (4,2)")
tk.MustExec("INSERT INTO t2 VALUES (1,2), (2,2)")

tk.MustQuery("explain format=brief SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a WHERE not(0+(t1.a=30 and t2.b=1));").Check(testkit.Rows(
"Selection 8000.00 root not(plus(0, and(eq(test.t1.a, 30), eq(test.t2.b, 1))))",
"└─MergeJoin 10000.00 root left outer join, left key:test.t1.a, right key:test.t2.a",
" ├─TableReader(Build) 8000.00 root data:Selection",
" │ └─Selection 8000.00 cop[tikv] not(istrue_with_null(plus(0, and(eq(test.t2.a, 30), eq(test.t2.b, 1)))))",
" │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:true, stats:pseudo",
" └─TableReader(Probe) 10000.00 root data:TableFullScan",
" └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:true, stats:pseudo"))
tk.MustQuery("SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a WHERE not(0+(t1.a=30 and t2.b=1));").Check(testkit.Rows(
"1 1 1 2",
"2 1 2 2",
"3 1 <nil> <nil>",
"4 2 <nil> <nil>",
))
}

func (s *testIntegrationSuite) TestIssue31035(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1;")
tk.MustExec("create table t1(c1 longtext, c2 decimal(37, 4), unique key(c1(10)), unique key(c2));")
tk.MustExec("insert into t1 values('眐', -962541614831459.7458);")
tk.MustQuery("select * from t1 order by c2 + 10;").Check(testkit.Rows("眐 -962541614831459.7458"))
}

// TestDNFCondSelectivityWithConst test selectivity calculation with DNF conditions with one is const.
// Close https://github.com/pingcap/tidb/issues/31096
func (s *testIntegrationSuite) TestDNFCondSelectivityWithConst(c *C) {
testKit := testkit.NewTestKit(c, s.store)
testKit.MustExec("use test")
testKit.MustExec("drop table if exists t1")
testKit.MustExec("create table t1(a int, b int, c int);")
testKit.MustExec("insert into t1 value(10,10,10)")
for i := 0; i < 7; i++ {
testKit.MustExec("insert into t1 select * from t1")
}
testKit.MustExec("insert into t1 value(1,1,1)")
testKit.MustExec("analyze table t1")

testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or b=1;").Check(testkit.Rows(
"TableReader 1.99 root data:Selection",
"└─Selection 1.99 cop[tikv] or(eq(test.t1.a, 1), eq(test.t1.b, 1))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustQuery("explain format = 'brief' select * from t1 where 0=1 or a=1 or b=1;").Check(testkit.Rows(
"TableReader 1.99 root data:Selection",
"└─Selection 1.99 cop[tikv] or(0, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustQuery("explain format = 'brief' select * from t1 where null or a=1 or b=1;").Check(testkit.Rows(
"TableReader 1.99 root data:Selection",
"└─Selection 1.99 cop[tikv] or(0, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or false or b=1;").Check(testkit.Rows(
"TableReader 1.99 root data:Selection",
"└─Selection 1.99 cop[tikv] or(eq(test.t1.a, 1), or(0, eq(test.t1.b, 1)))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or b=1 or \"false\";").Check(testkit.Rows(
"TableReader 1.99 root data:Selection",
"└─Selection 1.99 cop[tikv] or(eq(test.t1.a, 1), or(eq(test.t1.b, 1), 0))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustQuery("explain format = 'brief' select * from t1 where 1=1 or a=1 or b=1;").Check(testkit.Rows(
"TableReader 129.00 root data:Selection",
"└─Selection 129.00 cop[tikv] or(1, or(eq(test.t1.a, 1), eq(test.t1.b, 1)))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustQuery("explain format = 'brief' select * from t1 where a=1 or b=1 or 1=1;").Check(testkit.Rows(
"TableReader 129.00 root data:Selection",
"└─Selection 129.00 cop[tikv] or(eq(test.t1.a, 1), or(eq(test.t1.b, 1), 1))",
" └─TableFullScan 129.00 cop[tikv] table:t1 keep order:false"))
testKit.MustExec("drop table if exists t1")
}

func (s *testIntegrationSuite) TestIssue31202(c *C) {
store, dom := s.store, s.dom
tk := testkit.NewTestKit(c, store)

tk.MustExec("use test")
tk.MustExec("create table t31202(a int primary key, b int);")

tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31202", L: "t31202"})
c.Assert(err, IsNil)
// Set the hacked TiFlash replica for explain tests.
tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true}

tk.MustQuery("explain format = 'brief' select * from t31202;").Check(testkit.Rows(
"TableReader 10000.00 root data:TableFullScan",
"└─TableFullScan 10000.00 cop[tiflash] table:t31202 keep order:false, stats:pseudo"))

tk.MustQuery("explain format = 'brief' select * from t31202 use index (primary);").Check(testkit.Rows(
"TableReader 10000.00 root data:TableFullScan",
"└─TableFullScan 10000.00 cop[tikv] table:t31202 keep order:false, stats:pseudo"))
tk.MustExec("drop table if exists t31202")
}

func (s *testIntegrationSuite) TestNaturalJoinUpdateSameTable(c *C) {
tk := testkit.NewTestKit(c, s.store)

tk.MustExec("create database natural_join_update")
defer tk.MustExec("drop database natural_join_update")
tk.MustExec("use natural_join_update")
tk.MustExec("create table t1(a int, b int)")
tk.MustExec("insert into t1 values (1,1),(2,2)")
tk.MustExec("update t1 as a natural join t1 b SET a.a = 2, b.b = 3")
tk.MustQuery("select * from t1").Sort().Check(testkit.Rows("2 3", "2 3"))
tk.MustExec("drop table t1")
tk.MustExec("create table t1 (a int primary key, b int)")
tk.MustExec("insert into t1 values (1,1),(2,2)")
tk.MustGetErrCode(`update t1 as a natural join t1 b SET a.a = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict)
tk.MustExec("drop table t1")
tk.MustExec("create table t1 (a int, b int) partition by hash (a) partitions 3")
tk.MustExec("insert into t1 values (1,1),(2,2)")
tk.MustGetErrCode(`update t1 as a natural join t1 b SET a.a = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict)
tk.MustExec("drop table t1")
tk.MustExec("create table t1 (A int, b int) partition by hash (b) partitions 3")
tk.MustExec("insert into t1 values (1,1),(2,2)")
tk.MustGetErrCode(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict)
_, err := tk.Exec(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`)
c.Assert(err, NotNil)
c.Assert(err, ErrorMatches, ".planner:1706.Primary key/partition key update is not allowed since the table is updated both as 'a' and 'B'.")
tk.MustExec("drop table t1")
tk.MustExec("create table t1 (A int, b int) partition by RANGE COLUMNS (b) (partition `pNeg` values less than (0),partition `pPos` values less than MAXVALUE)")
tk.MustExec("insert into t1 values (1,1),(2,2)")
tk.MustGetErrCode(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict)
tk.MustExec("drop table t1")
}

func (s *testIntegrationSuite) TestAggPushToCopForCachedTable(c *C) {
store, _ := s.store, s.dom
tk := testkit.NewTestKit(c, store)

tk.MustExec("use test")
tk.MustExec(`create table t32157(
process_code varchar(8) NOT NULL,
ctrl_class varchar(2) NOT NULL,
ctrl_type varchar(1) NOT NULL,
oper_no varchar(12) DEFAULT NULL,
modify_date datetime DEFAULT NULL,
d_c_flag varchar(2) NOT NULL,
PRIMARY KEY (process_code,ctrl_class,d_c_flag));`)
tk.MustExec("insert into t32157 values ('GDEP0071', '05', '1', '10000', '2016-06-29 00:00:00', 'C')")
tk.MustExec("insert into t32157 values ('GDEP0071', '05', '0', '0000', '2016-06-01 00:00:00', 'D')")
tk.MustExec("alter table t32157 cache")

tk.MustQuery("explain format = 'brief' select /*+AGG_TO_COP()*/ count(*) from t32157 ignore index(primary) where process_code = 'GDEP0071'").Check(testkit.Rows(
"StreamAgg 1.00 root funcs:count(1)->Column#8]\n" +
"[└─TableReader 10.00 root data:Selection]\n" +
"[ └─Selection 10.00 cop[tikv] eq(test.t32157.process_code, \"GDEP0071\")]\n" +
"[ └─TableFullScan 10000.00 cop[tikv] table:t32157 keep order:false, stats:pseudo"))

var readFromCacheNoPanic bool
for i := 0; i < 10; i++ {
tk.MustQuery("select /*+AGG_TO_COP()*/ count(*) from t32157 ignore index(primary) where process_code = 'GDEP0071'").Check(testkit.Rows("2"))
if tk.Se.GetSessionVars().StmtCtx.ReadFromTableCache {
readFromCacheNoPanic = true
break
}
}
c.Assert(readFromCacheNoPanic, IsTrue)

tk.MustExec("drop table if exists t31202")
}

func (s *testIntegrationSuite) TestIssue31240(c *C) {
store, dom := s.store, s.dom
tk := testkit.NewTestKit(c, store)

tk.MustExec("use test")
tk.MustExec("create table t31240(a int, b int);")
tk.MustExec("set @@tidb_allow_mpp = 0")

tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31240", L: "t31240"})
c.Assert(err, IsNil)
// Set the hacked TiFlash replica for explain tests.
tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true}
>>>>>>> 313960e49... planner, table: Disallow update self (natural) join on partitioning columns (#31629) (#31779)

var input []string
var output []struct {
Expand Down
24 changes: 18 additions & 6 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4374,8 +4374,9 @@ func (b *PlanBuilder) buildUpdate(ctx context.Context, update *ast.UpdateStmt) (
}

type tblUpdateInfo struct {
name string
pkUpdated bool
name string
pkUpdated bool
partitionColUpdated bool
}

// CheckUpdateList checks all related columns in updatable state.
Expand All @@ -4384,7 +4385,12 @@ func CheckUpdateList(assignFlags []int, updt *Update) error {
for _, content := range updt.TblColPosInfos {
tbl := updt.tblID2Table[content.TblID]
flags := assignFlags[content.Start:content.End]
var update, updatePK bool
var update, updatePK, updatePartitionCol bool
var partitionColumnNames []model.CIStr
if pt, ok := tbl.(table.PartitionedTable); ok && pt != nil {
partitionColumnNames = pt.GetPartitionColumnNames()
}

for i, col := range tbl.WritableCols() {
if flags[i] >= 0 && col.State != model.StatePublic {
return ErrUnknownColumn.GenWithStackByArgs(col.Name, clauseMsg[fieldList])
Expand All @@ -4394,19 +4400,25 @@ func CheckUpdateList(assignFlags []int, updt *Update) error {
if mysql.HasPriKeyFlag(col.Flag) {
updatePK = true
}
for _, partColName := range partitionColumnNames {
if col.Name.L == partColName.L {
updatePartitionCol = true
}
}
}
}
if update {
// Check for multi-updates on primary key,
// see https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html#error_er_multi_update_key_conflict
if otherTable, ok := updateFromOtherAlias[tbl.Meta().ID]; ok {
if otherTable.pkUpdated || updatePK {
if otherTable.pkUpdated || updatePK || otherTable.partitionColUpdated || updatePartitionCol {
return ErrMultiUpdateKeyConflict.GenWithStackByArgs(otherTable.name, updt.names[content.Start].TblName.O)
}
} else {
updateFromOtherAlias[tbl.Meta().ID] = tblUpdateInfo{
name: updt.names[content.Start].TblName.O,
pkUpdated: updatePK,
name: updt.names[content.Start].TblName.O,
pkUpdated: updatePK,
partitionColUpdated: updatePartitionCol,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ type PartitionedTable interface {
GetPartition(physicalID int64) PhysicalTable
GetPartitionByRow(sessionctx.Context, []types.Datum) (PhysicalTable, error)
GetAllPartitionIDs() []int64
GetPartitionColumnNames() []model.CIStr
}

// TableFromMeta builds a table.Table from *model.TableInfo.
Expand Down
23 changes: 23 additions & 0 deletions table/tables/partition.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,29 @@ func (t *partitionedTable) PartitionExpr() (*PartitionExpr, error) {
return t.partitionExpr, nil
}

func (t *partitionedTable) GetPartitionColumnNames() []model.CIStr {
// PARTITION BY {LIST|RANGE} COLUMNS uses columns directly without expressions
pi := t.Meta().Partition
if len(pi.Columns) > 0 {
return pi.Columns
}

partitionCols := expression.ExtractColumns(t.partitionExpr.Expr)
colIDs := make([]int64, 0, len(partitionCols))
for _, col := range partitionCols {
colIDs = append(colIDs, col.ID)
}
colNames := make([]model.CIStr, 0, len(partitionCols))
for _, colID := range colIDs {
for _, col := range t.Cols() {
if col.ID == colID {
colNames = append(colNames, col.Name)
}
}
}
return colNames
}

// PartitionRecordKey is exported for test.
func PartitionRecordKey(pid int64, handle int64) kv.Key {
recordPrefix := tablecodec.GenTableRecordPrefix(pid)
Expand Down
Loading

0 comments on commit f556c0f

Please sign in to comment.