Skip to content

Commit f785905

Browse files
authored
fix insert for subquery aliases with on duplicate update expr (#2855)
1 parent 710ac92 commit f785905

File tree

3 files changed

+63
-25
lines changed

3 files changed

+63
-25
lines changed

enginetest/queries/insert_queries.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,33 @@ var InsertScripts = []ScriptTest{
17121712
},
17131713
},
17141714
},
1715+
{
1716+
Name: "Insert on duplicate key references table in subquery with different schema lengths",
1717+
SetUpScript: []string{
1718+
"create table a (i int primary key, j int, k int)",
1719+
"insert into a values (1, 2, 3)",
1720+
"create table b (i int primary key)",
1721+
"insert into b values (1)",
1722+
},
1723+
Assertions: []ScriptTestAssertion{
1724+
{
1725+
Query: "insert into a select * from (select i from b) as bb on duplicate key update a.i = bb.i + 100;",
1726+
ExpectedErrStr: "number of values does not match number of columns provided",
1727+
},
1728+
{
1729+
Query: "insert into a (i) select * from (select i from b) as bb on duplicate key update a.i = bb.i + 100;",
1730+
Expected: []sql.Row{
1731+
{types.NewOkResult(2)},
1732+
},
1733+
},
1734+
{
1735+
Query: "select * from a",
1736+
Expected: []sql.Row{
1737+
{101, 2, 3},
1738+
},
1739+
},
1740+
},
1741+
},
17151742
{
17161743
Name: "Insert on duplicate key references table in aliased subquery",
17171744
SetUpScript: []string{

sql/analyzer/fix_exec_indexes.go

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ import (
1818
"fmt"
1919
"strings"
2020

21-
"github.com/dolthub/go-mysql-server/sql/planbuilder"
22-
2321
"github.com/dolthub/go-mysql-server/sql"
2422
"github.com/dolthub/go-mysql-server/sql/expression"
2523
"github.com/dolthub/go-mysql-server/sql/plan"
24+
"github.com/dolthub/go-mysql-server/sql/planbuilder"
2625
"github.com/dolthub/go-mysql-server/sql/transform"
2726
)
2827

@@ -429,42 +428,51 @@ func (s *idxScope) visitSelf(n sql.Node) error {
429428
s.expressions = append(s.expressions, fixExprToScope(e, scopes...))
430429
}
431430
case *plan.InsertInto:
432-
rightSchema := make(sql.Schema, len(n.Destination.Schema())*2)
433431
// schema = [oldrow][newrow]
434-
for oldRowIdx, c := range n.Destination.Schema() {
435-
rightSchema[oldRowIdx] = c
436-
newRowIdx := len(n.Destination.Schema()) + oldRowIdx
437-
if values, ok := n.Source.(*plan.Values); ok {
438-
// The source table is either named via VALUES(...) AS ... or has
439-
// the default value planbuilder.OnDupValuesPrefix
432+
destSch := n.Destination.Schema()
433+
srcSch := n.Source.Schema()
434+
rightSchema := make(sql.Schema, len(destSch)*2)
435+
if values, isValues := n.Source.(*plan.Values); isValues {
436+
for oldRowIdx, c := range destSch {
440437
newC := c.Copy()
441438
newC.Source = values.AliasName
442439
if values.ColumnNames != nil {
443440
newC.Name = values.ColumnNames[newC.Name]
444441
}
442+
443+
newRowIdx := len(destSch) + oldRowIdx
444+
rightSchema[oldRowIdx] = c
445445
rightSchema[newRowIdx] = newC
446-
} else if len(n.Destination.Schema()) != len(n.Source.Schema()) {
447-
newC := c.Copy()
448-
newC.Source = planbuilder.OnDupValuesPrefix
449-
rightSchema[newRowIdx] = newC
450-
} else {
446+
}
447+
} else {
448+
for oldRowIdx, c := range destSch {
451449
// find source index that aligns with dest column
452-
var matched bool
450+
var newC *sql.Column
453451
for j, sourceCol := range n.ColumnNames {
454452
if strings.EqualFold(c.Name, sourceCol) {
455-
rightSchema[newRowIdx] = n.Source.Schema()[j]
456-
matched = true
453+
newC = srcSch[j]
457454
break
458455
}
459456
}
460-
if !matched {
461-
// todo: this is only used for load data. load data errors
462-
// without a fallback, and fails to resolve defaults if I
463-
// define the columns upfront.
464-
rightSchema[newRowIdx] = n.Source.Schema()[oldRowIdx]
457+
// unable to find column, use copy with OnDupValuesPrefix or fallback
458+
if newC == nil {
459+
if len(destSch) != len(srcSch) {
460+
newC = c.Copy()
461+
newC.Source = planbuilder.OnDupValuesPrefix
462+
} else {
463+
// todo: this is only used for load data. load data errors
464+
// without a fallback, and fails to resolve defaults if I
465+
// define the columns upfront.
466+
newC = srcSch[oldRowIdx]
467+
}
465468
}
469+
newRowIdx := len(destSch) + oldRowIdx
470+
471+
rightSchema[oldRowIdx] = c
472+
rightSchema[newRowIdx] = newC
466473
}
467474
}
475+
468476
rightScope := &idxScope{}
469477
rightScope.addSchema(rightSchema)
470478
dstScope := s.childScopes[0]

sql/planbuilder/dml.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,8 @@ func (b *Builder) buildInsert(inScope *scope, i *ast.Insert) (outScope *scope) {
120120
combinedScope.insertColumnAliases = inScope.insertColumnAliases
121121
for i, c := range destScope.cols {
122122
combinedScope.newColumn(c)
123-
if len(srcScope.cols) == len(destScope.cols) {
124-
combinedScope.newColumn(srcScope.cols[i])
125-
} else {
123+
// if the srcScope is empty, it is a values statement
124+
if len(srcScope.cols) == 0 {
126125
// The to-be-inserted values can be referenced via the provided alias.
127126
c.table = combinedScope.insertTableAlias
128127
if len(combinedScope.insertColumnAliases) > 0 {
@@ -131,6 +130,10 @@ func (b *Builder) buildInsert(inScope *scope, i *ast.Insert) (outScope *scope) {
131130
c.originalCol = aliasColumnName
132131
}
133132
combinedScope.newColumn(c)
133+
continue
134+
}
135+
if i < len(srcScope.cols) {
136+
combinedScope.newColumn(srcScope.cols[i])
134137
}
135138
}
136139
onDupExprs = b.buildOnDupUpdateExprs(combinedScope, destScope, ast.AssignmentExprs(i.OnDup))

0 commit comments

Comments
 (0)