Skip to content

Commit 4fded5f

Browse files
authored
planner: fix cannot binding for deleting multi table with alias (#57255)
close #56726
1 parent 2ca497e commit 4fded5f

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

pkg/parser/ast/dml.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ type TableName struct {
282282
TableSample *TableSample
283283
// AS OF is used to see the data as it was at a specific point in time.
284284
AsOf *AsOfClause
285+
// IsAlias is true if this table name is an alias.
286+
// sometime, we need to distinguish the table name is an alias or not.
287+
// for example ```delete tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id```
288+
// ```tt1``` is a alias name. so we need to set IsAlias to true and restore the table name without database name.
289+
IsAlias bool
285290
}
286291

287292
func (*TableName) resultSet() {}
@@ -293,7 +298,7 @@ func (n *TableName) restoreName(ctx *format.RestoreCtx) {
293298
if n.Schema.String() != "" {
294299
ctx.WriteName(n.Schema.String())
295300
ctx.WritePlain(".")
296-
} else if ctx.DefaultDB != "" {
301+
} else if ctx.DefaultDB != "" && !n.IsAlias {
297302
// Try CTE, for a CTE table name, we shouldn't write the database name.
298303
if !ctx.IsCTETableName(n.Name.L) {
299304
ctx.WriteName(ctx.DefaultDB)

pkg/planner/core/preprocess.go

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,9 @@ func (p *preprocessor) checkBindGrammar(originNode, hintedNode ast.StmtNode, def
571571
TableInfo: tableInfo,
572572
})
573573
}
574-
574+
aliasChecker := &aliasChecker{}
575+
originNode.Accept(aliasChecker)
576+
hintedNode.Accept(aliasChecker)
575577
originSQL := parser.NormalizeForBinding(utilparser.RestoreWithDefaultDB(originNode, defaultDB, originNode.Text()), false)
576578
hintedSQL := parser.NormalizeForBinding(utilparser.RestoreWithDefaultDB(hintedNode, defaultDB, hintedNode.Text()), false)
577579
if originSQL != hintedSQL {
@@ -1991,3 +1993,59 @@ func (p *preprocessor) skipLockMDL() bool {
19911993
// skip lock mdl for ANALYZE statement.
19921994
return p.flag&inImportInto > 0 || p.flag&inAnalyze > 0
19931995
}
1996+
1997+
// aliasChecker is used to check the alias of the table in delete statement.
1998+
//
1999+
// for example: delete tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id
2000+
// `delete tt1` will be transformed to `delete current_database.t1` by default.
2001+
// because `tt1` cannot be used as alias in delete statement.
2002+
// so we have to set `tt1` as alias by aliasChecker.
2003+
type aliasChecker struct{}
2004+
2005+
func (*aliasChecker) Enter(in ast.Node) (ast.Node, bool) {
2006+
if deleteStmt, ok := in.(*ast.DeleteStmt); ok {
2007+
// 1. check the tableRefs of deleteStmt to find the alias
2008+
var aliases []*pmodel.CIStr
2009+
if deleteStmt.TableRefs != nil && deleteStmt.TableRefs.TableRefs != nil {
2010+
tableRefs := deleteStmt.TableRefs.TableRefs
2011+
if val := getTableRefsAlias(tableRefs.Left); val != nil {
2012+
aliases = append(aliases, val)
2013+
}
2014+
if val := getTableRefsAlias(tableRefs.Right); val != nil {
2015+
aliases = append(aliases, val)
2016+
}
2017+
}
2018+
// 2. check the Tables to tag the alias
2019+
if deleteStmt.Tables != nil && deleteStmt.Tables.Tables != nil {
2020+
for _, table := range deleteStmt.Tables.Tables {
2021+
if table.Schema.String() != "" {
2022+
continue
2023+
}
2024+
for _, alias := range aliases {
2025+
if table.Name.L == alias.L {
2026+
table.IsAlias = true
2027+
break
2028+
}
2029+
}
2030+
}
2031+
}
2032+
return in, true
2033+
}
2034+
return in, false
2035+
}
2036+
2037+
func getTableRefsAlias(tableRefs ast.ResultSetNode) *pmodel.CIStr {
2038+
switch v := tableRefs.(type) {
2039+
case *ast.Join:
2040+
if v.Left != nil {
2041+
return getTableRefsAlias(v.Left)
2042+
}
2043+
case *ast.TableSource:
2044+
return &v.AsName
2045+
}
2046+
return nil
2047+
}
2048+
2049+
func (*aliasChecker) Leave(in ast.Node) (ast.Node, bool) {
2050+
return in, true
2051+
}

pkg/planner/core/preprocess_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,14 @@ func TestPreprocessCTE(t *testing.T) {
414414
require.Equal(t, tc.after, rs.String())
415415
}
416416
}
417+
418+
func TestPreprocessDeleteFromWithAlias(t *testing.T) {
419+
// https://github.com/pingcap/tidb/issues/56726
420+
store := testkit.CreateMockStore(t)
421+
tk := testkit.NewTestKit(t, store)
422+
tk.MustExec("use test")
423+
tk.MustExec("create table t1(id int);")
424+
tk.MustExec(" create table t2(id int);")
425+
tk.MustExec("delete tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id;")
426+
tk.MustExec("create global binding for delete tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id using delete /*+ MAX_EXECUTION_TIME(10)*/ tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id;")
427+
}

0 commit comments

Comments
 (0)