Skip to content

Commit cf2978a

Browse files
authored
planner: fix cannot binding for deleting multi table with alias (#57255) (#57420)
close #56726
1 parent 152012b commit cf2978a

File tree

3 files changed

+76
-1
lines changed

3 files changed

+76
-1
lines changed

pkg/parser/ast/dml.go

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

290295
func (*TableName) resultSet() {}
@@ -296,7 +301,7 @@ func (n *TableName) restoreName(ctx *format.RestoreCtx) {
296301
if n.Schema.String() != "" {
297302
ctx.WriteName(n.Schema.String())
298303
ctx.WritePlain(".")
299-
} else if ctx.DefaultDB != "" {
304+
} else if ctx.DefaultDB != "" && !n.IsAlias {
300305
// Try CTE, for a CTE table name, we shouldn't write the database name.
301306
if !ctx.IsCTETableName(n.Name.L) {
302307
ctx.WriteName(ctx.DefaultDB)

pkg/planner/core/preprocess.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,9 @@ func (p *preprocessor) checkBindGrammar(originNode, hintedNode ast.StmtNode, def
553553
tn.TableInfo = tableInfo
554554
tn.DBInfo = dbInfo
555555
}
556+
aliasChecker := &aliasChecker{}
557+
originNode.Accept(aliasChecker)
558+
hintedNode.Accept(aliasChecker)
556559

557560
originSQL := parser.NormalizeForBinding(utilparser.RestoreWithDefaultDB(originNode, defaultDB, originNode.Text()))
558561
hintedSQL := parser.NormalizeForBinding(utilparser.RestoreWithDefaultDB(hintedNode, defaultDB, hintedNode.Text()))
@@ -1928,3 +1931,59 @@ func (p *preprocessor) skipLockMDL() bool {
19281931
// skip lock mdl for ANALYZE statement.
19291932
return p.flag&inImportInto > 0 || p.flag&inAnalyze > 0
19301933
}
1934+
1935+
// aliasChecker is used to check the alias of the table in delete statement.
1936+
//
1937+
// for example: delete tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id
1938+
// `delete tt1` will be transformed to `delete current_database.t1` by default.
1939+
// because `tt1` cannot be used as alias in delete statement.
1940+
// so we have to set `tt1` as alias by aliasChecker.
1941+
type aliasChecker struct{}
1942+
1943+
func (*aliasChecker) Enter(in ast.Node) (ast.Node, bool) {
1944+
if deleteStmt, ok := in.(*ast.DeleteStmt); ok {
1945+
// 1. check the tableRefs of deleteStmt to find the alias
1946+
var aliases []*model.CIStr
1947+
if deleteStmt.TableRefs != nil && deleteStmt.TableRefs.TableRefs != nil {
1948+
tableRefs := deleteStmt.TableRefs.TableRefs
1949+
if val := getTableRefsAlias(tableRefs.Left); val != nil {
1950+
aliases = append(aliases, val)
1951+
}
1952+
if val := getTableRefsAlias(tableRefs.Right); val != nil {
1953+
aliases = append(aliases, val)
1954+
}
1955+
}
1956+
// 2. check the Tables to tag the alias
1957+
if deleteStmt.Tables != nil && deleteStmt.Tables.Tables != nil {
1958+
for _, table := range deleteStmt.Tables.Tables {
1959+
if table.Schema.String() != "" {
1960+
continue
1961+
}
1962+
for _, alias := range aliases {
1963+
if table.Name.L == alias.L {
1964+
table.IsAlias = true
1965+
break
1966+
}
1967+
}
1968+
}
1969+
}
1970+
return in, true
1971+
}
1972+
return in, false
1973+
}
1974+
1975+
func getTableRefsAlias(tableRefs ast.ResultSetNode) *model.CIStr {
1976+
switch v := tableRefs.(type) {
1977+
case *ast.Join:
1978+
if v.Left != nil {
1979+
return getTableRefsAlias(v.Left)
1980+
}
1981+
case *ast.TableSource:
1982+
return &v.AsName
1983+
}
1984+
return nil
1985+
}
1986+
1987+
func (*aliasChecker) Leave(in ast.Node) (ast.Node, bool) {
1988+
return in, true
1989+
}

pkg/planner/core/preprocess_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,3 +408,14 @@ func TestPreprocessCTE(t *testing.T) {
408408
require.Equal(t, tc.after, rs.String())
409409
}
410410
}
411+
412+
func TestPreprocessDeleteFromWithAlias(t *testing.T) {
413+
// https://github.com/pingcap/tidb/issues/56726
414+
store := testkit.CreateMockStore(t)
415+
tk := testkit.NewTestKit(t, store)
416+
tk.MustExec("use test")
417+
tk.MustExec("create table t1(id int);")
418+
tk.MustExec(" create table t2(id int);")
419+
tk.MustExec("delete tt1 from t1 tt1,(select max(id) id from t2)tt2 where tt1.id<=tt2.id;")
420+
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;")
421+
}

0 commit comments

Comments
 (0)