Skip to content

Commit a7e6b38

Browse files
[release-21.0] bugfix: INSERT IGNORE not inserting rows (vitessio#18151) (vitessio#18164)
Signed-off-by: Andres Taylor <[email protected]> Co-authored-by: vitess-bot[bot] <108069721+vitess-bot[bot]@users.noreply.github.com>
1 parent ed06baa commit a7e6b38

File tree

5 files changed

+101
-13
lines changed

5 files changed

+101
-13
lines changed

go/test/endtoend/vtgate/queries/dml/dml_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ func TestUniqueLookupDuplicateEntries(t *testing.T) {
7777

7878
// TestUniqueLookupDuplicateIgnore tests the insert ignore on lookup table.
7979
func TestUniqueLookupDuplicateIgnore(t *testing.T) {
80-
t.Skip("this is fixed in https://github.com/vitessio/vitess/pull/18151, it will be backported to 22.0")
8180
mcmp, closer := start(t)
8281
defer closer()
8382

@@ -94,21 +93,20 @@ func TestUniqueLookupDuplicateIgnore(t *testing.T) {
9493

9594
// insert duplicate row in multi-row insert - lookup single shard
9695
// Current behavior does not work as expected—one of the rows should be inserted.
97-
// The lookup table is updated, but the main table is not. This is a bug in Vitess.
9896
// The issue occurs because the table has two vindex columns (`num` and `col`), both of which ignore nulls during vindex insertion.
9997
// In the `INSERT IGNORE` case, after the vindex create API call, a verify call checks if the row exists in the lookup table.
10098
// - If the row exists, it is inserted into the main table.
10199
// - If the row does not exist, the main table insertion is skipped.
102100
// Since the `col` column is null, the row is not inserted into the lookup table, causing the main table insertion to be ignored.
103101
qr = utils.Exec(t, mcmp.VtConn, "insert ignore into s_tbl(id, num) values (3,20), (4,20)")
104-
assert.EqualValues(t, 0, qr.RowsAffected)
105-
utils.AssertMatches(t, mcmp.VtConn, "select id, num from s_tbl order by id", `[[INT64(1) INT64(10)]]`)
102+
assert.EqualValues(t, 1, qr.RowsAffected)
103+
utils.AssertMatches(t, mcmp.VtConn, "select id, num from s_tbl order by id", `[[INT64(1) INT64(10)] [INT64(3) INT64(20)]]`)
106104
utils.AssertMatches(t, mcmp.VtConn, "select num, hex(keyspace_id) from num_vdx_tbl order by num", `[[INT64(10) VARCHAR("166B40B44ABA4BD6")] [INT64(20) VARCHAR("4EB190C9A2FA169C")]]`)
107105

108106
// insert duplicate row in multi-row insert - vindex values are not null
109107
qr = utils.Exec(t, mcmp.VtConn, "insert ignore into s_tbl(id, num, col) values (3,20, 30), (4,20, 40)")
110-
assert.EqualValues(t, 1, qr.RowsAffected)
111-
utils.AssertMatches(t, mcmp.VtConn, "select id, num, col from s_tbl order by id", `[[INT64(1) INT64(10) NULL] [INT64(3) INT64(20) INT64(30)]]`)
108+
assert.EqualValues(t, 0, qr.RowsAffected)
109+
utils.AssertMatches(t, mcmp.VtConn, "select id, num, col from s_tbl order by id", `[[INT64(1) INT64(10) NULL] [INT64(3) INT64(20) NULL]]`)
112110
utils.AssertMatches(t, mcmp.VtConn, "select num, hex(keyspace_id) from num_vdx_tbl order by num", `[[INT64(10) VARCHAR("166B40B44ABA4BD6")] [INT64(20) VARCHAR("4EB190C9A2FA169C")]]`)
113111
utils.AssertMatches(t, mcmp.VtConn, "select col, hex(keyspace_id) from col_vdx_tbl order by col", `[[INT64(30) VARCHAR("4EB190C9A2FA169C")]]`)
114112

go/test/endtoend/vtgate/queries/dml/insert_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"fmt"
2121
"testing"
2222

23+
"github.com/stretchr/testify/require"
24+
2325
"github.com/stretchr/testify/assert"
2426

2527
"vitess.io/vitess/go/test/endtoend/utils"
@@ -518,3 +520,28 @@ func TestInsertJson(t *testing.T) {
518520

519521
mcmp.Exec(`insert into j_tbl(id, jdoc) values (10, '{}'), (20, "[]")`)
520522
}
523+
524+
func TestInsertIgnoreNullAndInsertNull(t *testing.T) {
525+
mcmp, closer := start(t)
526+
defer closer()
527+
528+
mcmp.Exec("insert ignore into s_tbl(id) values (1)") // the remaining columns are be set to NULL
529+
mcmp.Exec("select id, num, col from s_tbl")
530+
mcmp.Exec("insert ignore into s_tbl(id, num, col) values (2, 2, 2), (3, NULL, 3), (4, 4, NULL)")
531+
mcmp.Exec("insert into s_tbl(id, num, col) values (5, 5, 5), (6, NULL, 6), (7, 7, NULL)")
532+
mcmp.Exec("select id, num, col from s_tbl")
533+
utils.AssertMatches(t, mcmp.VtConn, "select num, hex(keyspace_id) from num_vdx_tbl order by num",
534+
`[[INT64(2) VARCHAR("06E7EA22CE92708F")] [INT64(4) VARCHAR("D2FD8867D50D2DFE")] [INT64(5) VARCHAR("70BB023C810CA87A")] [INT64(7) VARCHAR("FB8BAAAD918119B8")]]`)
535+
utils.AssertMatches(t, mcmp.VtConn, "select col, id, hex(keyspace_id) from col_vdx_tbl order by col, id",
536+
`[[INT64(2) INT64(2) VARCHAR("06E7EA22CE92708F")] [INT64(3) INT64(3) VARCHAR("4EB190C9A2FA169C")] [INT64(5) INT64(5) VARCHAR("70BB023C810CA87A")] [INT64(6) INT64(6) VARCHAR("F098480AC4C4BE71")]]`)
537+
538+
mcmp.Exec("insert ignore into name_tbl(id, name) values (1, 'foo'), (2, 'bar')")
539+
mcmp.Exec("select id, name from name_tbl order by id")
540+
541+
// This should contain the one row that was inserted above
542+
utils.AssertMatches(t, mcmp.VtConn, "select name, id, hex(keyspace_id) from name_vdx_tbl order by name, id",
543+
`[[VARCHAR("bar") INT64(2) VARCHAR("06E7EA22CE92708F")] [VARCHAR("foo") INT64(1) VARCHAR("166B40B44ABA4BD6")]]`)
544+
545+
_, err := utils.ExecAllowError(t, mcmp.VtConn, "insert ignore into name_tbl(id, name) values (22, NULL)") // this should fail, since we have a vindex that does not ignore nulls
546+
require.ErrorContains(t, err, "Column 'name,id' cannot be null")
547+
}

go/test/endtoend/vtgate/queries/dml/sharded_schema.sql

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
create table s_tbl
22
(
3-
id bigint,
4-
num bigint,
5-
col bigint,
3+
id bigint,
4+
num bigint,
5+
col bigint,
66
unique key (num),
77
primary key (id)
88
) Engine = InnoDB;
9-
9+
create table name_tbl
10+
(
11+
id bigint,
12+
name varchar(50),
13+
primary key (id)
14+
) Engine = InnoDB;
1015
create table num_vdx_tbl
1116
(
1217
num bigint,
@@ -20,6 +25,14 @@ create table col_vdx_tbl
2025
id bigint,
2126
keyspace_id varbinary(20),
2227
primary key (col, id)
28+
) Engine = InnoDB
29+
;
30+
create table name_vdx_tbl
31+
(
32+
name varchar(50),
33+
id bigint,
34+
keyspace_id varbinary(20),
35+
primary key (name, id)
2336
) Engine = InnoDB;
2437

2538
create table user_tbl
@@ -100,7 +113,7 @@ create table lkp_mixed_idx
100113

101114
create table j_tbl
102115
(
103-
id bigint,
116+
id bigint,
104117
jdoc json,
105118
primary key (id)
106119
) Engine = InnoDB;

go/test/endtoend/vtgate/queries/dml/vschema.json

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
"hash": {
55
"type": "hash"
66
},
7+
"hash_varchar": {
8+
"type": "unicode_loose_xxhash"
9+
},
710
"num_vdx": {
811
"type": "consistent_lookup_unique",
912
"params": {
@@ -24,6 +27,15 @@
2427
},
2528
"owner": "s_tbl"
2629
},
30+
"name_vdx": {
31+
"type": "consistent_lookup",
32+
"params": {
33+
"table": "name_vdx_tbl",
34+
"from": "name,id",
35+
"to": "keyspace_id"
36+
},
37+
"owner": "name_tbl"
38+
},
2739
"oid_vdx": {
2840
"type": "consistent_lookup_unique",
2941
"params": {
@@ -76,11 +88,29 @@
7688
"name": "num_vdx"
7789
},
7890
{
79-
"columns": ["col", "id"],
91+
"columns": [
92+
"col",
93+
"id"
94+
],
8095
"name": "col_vdx"
8196
}
8297
]
8398
},
99+
"name_tbl": {
100+
"column_vindexes": [
101+
{
102+
"column": "id",
103+
"name": "hash"
104+
},
105+
{
106+
"columns": [
107+
"name",
108+
"id"
109+
],
110+
"name": "name_vdx"
111+
}
112+
]
113+
},
84114
"num_vdx_tbl": {
85115
"column_vindexes": [
86116
{
@@ -97,6 +127,14 @@
97127
}
98128
]
99129
},
130+
"name_vdx_tbl": {
131+
"column_vindexes": [
132+
{
133+
"column": "name",
134+
"name": "hash_varchar"
135+
}
136+
]
137+
},
100138
"user_tbl": {
101139
"auto_increment": {
102140
"column": "id",

go/vt/vtgate/engine/insert_common.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,26 @@ func (ic *InsertCommon) processOwned(ctx context.Context, vcursor VCursor, vinde
230230
var createIndexes []int
231231
var createKeys []sqltypes.Row
232232
var createKsids []ksID
233+
var vindexNull []bool
233234

234235
for rowNum, rowColumnKeys := range vindexColumnsKeys {
235236
if ksids[rowNum] == nil {
236237
continue
237238
}
239+
keyContainsNull := false
240+
for _, columnKey := range rowColumnKeys {
241+
if columnKey.IsNull() {
242+
// if any of the keys contains a null, we know the vindex will ignore this row,
243+
// so it's safe to
244+
keyContainsNull = true
245+
break
246+
}
247+
}
248+
238249
createIndexes = append(createIndexes, rowNum)
239250
createKeys = append(createKeys, rowColumnKeys)
240251
createKsids = append(createKsids, ksids[rowNum])
252+
vindexNull = append(vindexNull, keyContainsNull)
241253
}
242254
if createKeys == nil {
243255
return nil
@@ -254,7 +266,7 @@ func (ic *InsertCommon) processOwned(ctx context.Context, vcursor VCursor, vinde
254266
return err
255267
}
256268
for i, v := range verified {
257-
if !v {
269+
if !v && !vindexNull[i] {
258270
ksids[createIndexes[i]] = nil
259271
}
260272
}

0 commit comments

Comments
 (0)