Skip to content

Commit 080e08f

Browse files
committed
contrib/drivers/mariadb: add layer 3 features and issue regression tests
- Port feature tests: duplicate-key handling, JSON fields, row-level locking, master-slave config, table metadata, RANGE partition - Port 30 issue regression tests from MySQL baseline - Include 14 testdata SQL files for issue-specific table schemas - Adapt MariaDB-specific behavior (LOCK IN SHARE MODE, SKIP LOCKED requires 10.6+, mariadb driver name and port in link parsing test)
1 parent 6204c13 commit 080e08f

26 files changed

+4121
-6
lines changed

contrib/drivers/mariadb/mariadb_unit_init_test.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ import (
2121
)
2222

2323
const (
24-
TableSize = 10
25-
TableName = "user"
26-
TestSchema1 = "test1"
27-
TestSchema2 = "test2"
28-
TestDbPass = "12345678"
29-
CreateTime = "2018-10-24 10:00:00"
24+
TableSize = 10
25+
TableName = "user"
26+
TestSchema1 = "test1"
27+
TestSchema2 = "test2"
28+
TestPartitionDB = "test3"
29+
TestDbPass = "12345678"
30+
CreateTime = "2018-10-24 10:00:00"
3031
)
3132

3233
var (
3334
db gdb.DB
3435
db2 gdb.DB
36+
db3 gdb.DB
3537
ctx = context.TODO()
3638
)
3739

@@ -59,8 +61,12 @@ func init() {
5961
if _, err := db.Exec(ctx, fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil {
6062
gtest.Error(err)
6163
}
64+
if _, err := db.Exec(ctx, fmt.Sprintf(schemaTemplate, TestPartitionDB)); err != nil {
65+
gtest.Error(err)
66+
}
6267
db = db.Schema(TestSchema1)
6368
db2 = db.Schema(TestSchema2)
69+
db3 = db.Schema(TestPartitionDB)
6470
}
6571

6672
func createTable(table ...string) string {
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
2+
//
3+
// This Source Code Form is subject to the terms of the MIT License.
4+
// If a copy of the MIT was not distributed with this file,
5+
// You can obtain one at https://github.com/gogf/gf.
6+
7+
package mariadb_test
8+
9+
import (
10+
"context"
11+
"fmt"
12+
"testing"
13+
14+
"github.com/gogf/gf/v2/database/gdb"
15+
"github.com/gogf/gf/v2/os/gtime"
16+
"github.com/gogf/gf/v2/test/gtest"
17+
)
18+
19+
func createDuplicateTable(table ...string) string {
20+
var name string
21+
if len(table) > 0 {
22+
name = table[0]
23+
} else {
24+
name = fmt.Sprintf(`duplicate_table_%d`, gtime.TimestampNano())
25+
}
26+
dropTable(name)
27+
if _, err := db.Exec(ctx, fmt.Sprintf(`
28+
CREATE TABLE %s (
29+
id int(10) unsigned NOT NULL AUTO_INCREMENT,
30+
email varchar(100) NOT NULL,
31+
username varchar(45) NULL,
32+
score int(10) unsigned DEFAULT 0,
33+
login_count int(10) unsigned DEFAULT 0,
34+
PRIMARY KEY (id),
35+
UNIQUE KEY uk_email (email)
36+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
37+
`, name)); err != nil {
38+
gtest.Fatal(err)
39+
}
40+
return name
41+
}
42+
43+
func Test_OnDuplicateKeyUpdate_Basic(t *testing.T) {
44+
table := createDuplicateTable()
45+
defer dropTable(table)
46+
47+
gtest.C(t, func(t *gtest.T) {
48+
// First insert
49+
_, err := db.Exec(ctx, fmt.Sprintf(
50+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
51+
table,
52+
), "user1@example.com", "user1", 100)
53+
t.AssertNil(err)
54+
55+
one, err := db.Model(table).Where("email", "user1@example.com").One()
56+
t.AssertNil(err)
57+
t.Assert(one["username"], "user1")
58+
t.Assert(one["score"], 100)
59+
60+
// Duplicate insert - should update
61+
_, err = db.Exec(ctx, fmt.Sprintf(
62+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
63+
table,
64+
), "user1@example.com", "user1_updated", 200)
65+
t.AssertNil(err)
66+
67+
one, err = db.Model(table).Where("email", "user1@example.com").One()
68+
t.AssertNil(err)
69+
t.Assert(one["username"], "user1_updated")
70+
t.Assert(one["score"], 200)
71+
72+
// Verify only one record exists
73+
count, err := db.Model(table).Count()
74+
t.AssertNil(err)
75+
t.Assert(count, 1)
76+
})
77+
}
78+
79+
func Test_OnDuplicateKeyUpdate_Increment(t *testing.T) {
80+
table := createDuplicateTable()
81+
defer dropTable(table)
82+
83+
gtest.C(t, func(t *gtest.T) {
84+
// First insert
85+
_, err := db.Exec(ctx, fmt.Sprintf(
86+
"INSERT INTO %s (email, username, login_count) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE login_count = login_count + 1",
87+
table,
88+
), "user1@example.com", "user1", 1)
89+
t.AssertNil(err)
90+
91+
one, err := db.Model(table).Where("email", "user1@example.com").One()
92+
t.AssertNil(err)
93+
t.Assert(one["login_count"], 1)
94+
95+
// Duplicate - increment login_count
96+
_, err = db.Exec(ctx, fmt.Sprintf(
97+
"INSERT INTO %s (email, username, login_count) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE login_count = login_count + 1",
98+
table,
99+
), "user1@example.com", "user1", 1)
100+
t.AssertNil(err)
101+
102+
one, err = db.Model(table).Where("email", "user1@example.com").One()
103+
t.AssertNil(err)
104+
t.Assert(one["login_count"], 2)
105+
106+
// Third time
107+
_, err = db.Exec(ctx, fmt.Sprintf(
108+
"INSERT INTO %s (email, username, login_count) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE login_count = login_count + 1",
109+
table,
110+
), "user1@example.com", "user1", 1)
111+
t.AssertNil(err)
112+
113+
one, err = db.Model(table).Where("email", "user1@example.com").One()
114+
t.AssertNil(err)
115+
t.Assert(one["login_count"], 3)
116+
})
117+
}
118+
119+
func Test_OnDuplicateKeyUpdate_MultipleColumns(t *testing.T) {
120+
table := createDuplicateTable()
121+
defer dropTable(table)
122+
123+
gtest.C(t, func(t *gtest.T) {
124+
// First insert
125+
_, err := db.Exec(ctx, fmt.Sprintf(
126+
"INSERT INTO %s (email, username, score, login_count) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score), login_count = login_count + 1",
127+
table,
128+
), "user1@example.com", "user1", 100, 1)
129+
t.AssertNil(err)
130+
131+
one, err := db.Model(table).Where("email", "user1@example.com").One()
132+
t.AssertNil(err)
133+
t.Assert(one["username"], "user1")
134+
t.Assert(one["score"], 100)
135+
t.Assert(one["login_count"], 1)
136+
137+
// Duplicate - update multiple columns
138+
_, err = db.Exec(ctx, fmt.Sprintf(
139+
"INSERT INTO %s (email, username, score, login_count) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score), login_count = login_count + 1",
140+
table,
141+
), "user1@example.com", "user1_v2", 200, 1)
142+
t.AssertNil(err)
143+
144+
one, err = db.Model(table).Where("email", "user1@example.com").One()
145+
t.AssertNil(err)
146+
t.Assert(one["username"], "user1_v2")
147+
t.Assert(one["score"], 200)
148+
t.Assert(one["login_count"], 2)
149+
})
150+
}
151+
152+
func Test_OnDuplicateKeyUpdate_Batch(t *testing.T) {
153+
table := createDuplicateTable()
154+
defer dropTable(table)
155+
156+
gtest.C(t, func(t *gtest.T) {
157+
// Insert multiple records
158+
_, err := db.Exec(ctx, fmt.Sprintf(
159+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
160+
table,
161+
), "user1@example.com", "user1", 100,
162+
"user2@example.com", "user2", 200,
163+
"user3@example.com", "user3", 300)
164+
t.AssertNil(err)
165+
166+
count, err := db.Model(table).Count()
167+
t.AssertNil(err)
168+
t.Assert(count, 3)
169+
170+
// Update with duplicate - should update specific records
171+
_, err = db.Exec(ctx, fmt.Sprintf(
172+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?), (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
173+
table,
174+
), "user1@example.com", "user1_updated", 150,
175+
"user2@example.com", "user2_updated", 250)
176+
t.AssertNil(err)
177+
178+
// Still 3 records
179+
count, err = db.Model(table).Count()
180+
t.AssertNil(err)
181+
t.Assert(count, 3)
182+
183+
// Verify updates
184+
one, err := db.Model(table).Where("email", "user1@example.com").One()
185+
t.AssertNil(err)
186+
t.Assert(one["username"], "user1_updated")
187+
t.Assert(one["score"], 150)
188+
189+
one, err = db.Model(table).Where("email", "user2@example.com").One()
190+
t.AssertNil(err)
191+
t.Assert(one["username"], "user2_updated")
192+
t.Assert(one["score"], 250)
193+
194+
// user3 unchanged
195+
one, err = db.Model(table).Where("email", "user3@example.com").One()
196+
t.AssertNil(err)
197+
t.Assert(one["username"], "user3")
198+
t.Assert(one["score"], 300)
199+
})
200+
}
201+
202+
func Test_OnDuplicateKeyUpdate_ConditionalUpdate(t *testing.T) {
203+
table := createDuplicateTable()
204+
defer dropTable(table)
205+
206+
gtest.C(t, func(t *gtest.T) {
207+
// First insert
208+
_, err := db.Exec(ctx, fmt.Sprintf(
209+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE score = IF(VALUES(score) > score, VALUES(score), score)",
210+
table,
211+
), "user1@example.com", "user1", 100)
212+
t.AssertNil(err)
213+
214+
one, err := db.Model(table).Where("email", "user1@example.com").One()
215+
t.AssertNil(err)
216+
t.Assert(one["score"], 100)
217+
218+
// Try to update with lower score - should not update
219+
_, err = db.Exec(ctx, fmt.Sprintf(
220+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE score = IF(VALUES(score) > score, VALUES(score), score)",
221+
table,
222+
), "user1@example.com", "user1", 50)
223+
t.AssertNil(err)
224+
225+
one, err = db.Model(table).Where("email", "user1@example.com").One()
226+
t.AssertNil(err)
227+
t.Assert(one["score"], 100) // Still 100
228+
229+
// Update with higher score - should update
230+
_, err = db.Exec(ctx, fmt.Sprintf(
231+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE score = IF(VALUES(score) > score, VALUES(score), score)",
232+
table,
233+
), "user1@example.com", "user1", 150)
234+
t.AssertNil(err)
235+
236+
one, err = db.Model(table).Where("email", "user1@example.com").One()
237+
t.AssertNil(err)
238+
t.Assert(one["score"], 150) // Updated to 150
239+
})
240+
}
241+
242+
func Test_OnDuplicateKeyUpdate_WithTransaction(t *testing.T) {
243+
table := createDuplicateTable()
244+
defer dropTable(table)
245+
246+
gtest.C(t, func(t *gtest.T) {
247+
// Transaction with ON DUPLICATE KEY UPDATE
248+
err := db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
249+
// First insert
250+
_, err := tx.Exec(fmt.Sprintf(
251+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
252+
table,
253+
), "user1@example.com", "user1", 100)
254+
if err != nil {
255+
return err
256+
}
257+
258+
// Duplicate in same transaction
259+
_, err = tx.Exec(fmt.Sprintf(
260+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
261+
table,
262+
), "user1@example.com", "user1_updated", 200)
263+
return err
264+
})
265+
t.AssertNil(err)
266+
267+
// Verify final state
268+
one, err := db.Model(table).Where("email", "user1@example.com").One()
269+
t.AssertNil(err)
270+
t.Assert(one["username"], "user1_updated")
271+
t.Assert(one["score"], 200)
272+
273+
count, err := db.Model(table).Count()
274+
t.AssertNil(err)
275+
t.Assert(count, 1)
276+
})
277+
}
278+
279+
func Test_OnDuplicateKeyUpdate_MixedInsertUpdate(t *testing.T) {
280+
table := createDuplicateTable()
281+
defer dropTable(table)
282+
283+
gtest.C(t, func(t *gtest.T) {
284+
// First batch insert
285+
_, err := db.Exec(ctx, fmt.Sprintf(
286+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?), (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
287+
table,
288+
), "user1@example.com", "user1", 100,
289+
"user2@example.com", "user2", 200)
290+
t.AssertNil(err)
291+
292+
count, err := db.Model(table).Count()
293+
t.AssertNil(err)
294+
t.Assert(count, 2)
295+
296+
// Mixed batch: one duplicate, one new
297+
_, err = db.Exec(ctx, fmt.Sprintf(
298+
"INSERT INTO %s (email, username, score) VALUES (?, ?, ?), (?, ?, ?) ON DUPLICATE KEY UPDATE username = VALUES(username), score = VALUES(score)",
299+
table,
300+
), "user1@example.com", "user1_updated", 150,
301+
"user3@example.com", "user3", 300)
302+
t.AssertNil(err)
303+
304+
// Should have 3 records now
305+
count, err = db.Model(table).Count()
306+
t.AssertNil(err)
307+
t.Assert(count, 3)
308+
309+
// Verify user1 was updated
310+
one, err := db.Model(table).Where("email", "user1@example.com").One()
311+
t.AssertNil(err)
312+
t.Assert(one["username"], "user1_updated")
313+
t.Assert(one["score"], 150)
314+
315+
// Verify user3 was inserted
316+
one, err = db.Model(table).Where("email", "user3@example.com").One()
317+
t.AssertNil(err)
318+
t.Assert(one["username"], "user3")
319+
t.Assert(one["score"], 300)
320+
})
321+
}

0 commit comments

Comments
 (0)