Skip to content

Commit af13080

Browse files
committed
test(mysql): add complex query enhancement tests
Add comprehensive tests for JOIN, SubQuery, and complex WHERE conditions. ## New Test Coverage ### Join Tests (mysql_z_unit_feature_model_join_test.go) - Inner/Left/Right Join operations - Multiple table joins with complex conditions - Join with aggregate functions and grouping ### SubQuery Tests (mysql_z_unit_feature_model_subquery_test.go) - Subquery in WHERE clause (IN, NOT IN, ANY, ALL) - Subquery in FROM clause - Subquery in SELECT clause - Correlated subqueries - Nested subqueries (3+ levels) ### Complex WHERE Tests (mysql_z_unit_model_where_test.go) - WherePrefix with various operators (LT, LTE, GT, GTE, Between, NotBetween) - WhereOr/WhereAnd combinations - Complex nested condition groups ## Total Added ~26 test functions across 3 files, 1040+ lines Ref gogf#4689
1 parent c8a11f7 commit af13080

3 files changed

Lines changed: 1041 additions & 0 deletions

File tree

contrib/drivers/mysql/mysql_z_unit_feature_model_join_test.go

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,339 @@ func Test_Model_FieldsPrefix(t *testing.T) {
175175
t.Assert(r[0]["nickname"], "name_1")
176176
})
177177
}
178+
179+
// Test_Model_Join_FiveTables tests complex join with 5+ tables
180+
func Test_Model_Join_FiveTables(t *testing.T) {
181+
var (
182+
table1 = gtime.TimestampNanoStr() + "_table1"
183+
table2 = gtime.TimestampNanoStr() + "_table2"
184+
table3 = gtime.TimestampNanoStr() + "_table3"
185+
table4 = gtime.TimestampNanoStr() + "_table4"
186+
table5 = gtime.TimestampNanoStr() + "_table5"
187+
)
188+
createInitTable(table1)
189+
defer dropTable(table1)
190+
createInitTable(table2)
191+
defer dropTable(table2)
192+
createInitTable(table3)
193+
defer dropTable(table3)
194+
createInitTable(table4)
195+
defer dropTable(table4)
196+
createInitTable(table5)
197+
defer dropTable(table5)
198+
199+
gtest.C(t, func(t *gtest.T) {
200+
r, err := db.Model(table1).As("t1").
201+
FieldsPrefix("t1", "id", "nickname").
202+
FieldsPrefix("t2", "passport").
203+
InnerJoin(table2+" AS t2", "t1.id = t2.id").
204+
InnerJoin(table3+" AS t3", "t2.id = t3.id").
205+
InnerJoin(table4+" AS t4", "t3.id = t4.id").
206+
InnerJoin(table5+" AS t5", "t4.id = t5.id").
207+
Where("t1.id IN(?)", g.Slice{1, 2, 3}).
208+
Order("t1.id asc").
209+
All()
210+
t.AssertNil(err)
211+
212+
t.Assert(len(r), 3)
213+
t.Assert(r[0]["id"], "1")
214+
t.Assert(r[0]["nickname"], "name_1")
215+
t.Assert(r[0]["passport"], "user_1")
216+
t.Assert(r[2]["id"], "3")
217+
})
218+
219+
gtest.C(t, func(t *gtest.T) {
220+
// 6 tables with mixed join types
221+
table6 := gtime.TimestampNanoStr() + "_table6"
222+
createInitTable(table6)
223+
defer dropTable(table6)
224+
225+
r, err := db.Model(table1).As("t1").
226+
Fields("t1.id").
227+
InnerJoin(table2+" AS t2", "t1.id = t2.id").
228+
LeftJoin(table3+" AS t3", "t2.id = t3.id").
229+
InnerJoin(table4+" AS t4", "t3.id = t4.id").
230+
RightJoin(table5+" AS t5", "t4.id = t5.id").
231+
LeftJoin(table6+" AS t6", "t5.id = t6.id").
232+
Where("t1.id", 5).
233+
One()
234+
t.AssertNil(err)
235+
236+
t.Assert(r["id"], "5")
237+
})
238+
}
239+
240+
// Test_Model_Join_SelfJoin tests self-join scenarios
241+
func Test_Model_Join_SelfJoin(t *testing.T) {
242+
table := createInitTable()
243+
defer dropTable(table)
244+
245+
gtest.C(t, func(t *gtest.T) {
246+
// Self-join to find pairs where a.id < b.id
247+
r, err := db.Model(table).As("a").
248+
Fields("a.id AS a_id", "b.id AS b_id").
249+
InnerJoin(table+" AS b", "a.id < b.id").
250+
Where("a.id", 1).
251+
Where("b.id <=", 3).
252+
Order("b.id asc").
253+
All()
254+
t.AssertNil(err)
255+
256+
t.Assert(len(r), 2)
257+
t.Assert(r[0]["a_id"], "1")
258+
t.Assert(r[0]["b_id"], "2")
259+
t.Assert(r[1]["b_id"], "3")
260+
})
261+
262+
gtest.C(t, func(t *gtest.T) {
263+
// Self-join with multiple conditions
264+
r, err := db.Model(table).As("a").
265+
Fields("a.id", "a.nickname", "b.nickname AS other_nickname").
266+
LeftJoin(table+" AS b", "a.id = b.id - 1").
267+
Where("a.id IN(?)", g.Slice{1, 2}).
268+
Order("a.id asc").
269+
All()
270+
t.AssertNil(err)
271+
272+
t.Assert(len(r), 2)
273+
t.Assert(r[0]["id"], "1")
274+
t.Assert(r[0]["nickname"], "name_1")
275+
t.Assert(r[0]["other_nickname"], "name_2")
276+
t.Assert(r[1]["id"], "2")
277+
t.Assert(r[1]["other_nickname"], "name_3")
278+
})
279+
}
280+
281+
// Test_Model_Join_LeftJoinNull tests LEFT JOIN NULL handling
282+
func Test_Model_Join_LeftJoinNull(t *testing.T) {
283+
var (
284+
table1 = gtime.TimestampNanoStr() + "_table1"
285+
table2 = gtime.TimestampNanoStr() + "_table2"
286+
)
287+
createInitTable(table1)
288+
defer dropTable(table1)
289+
290+
// Create table2 with only partial data
291+
createTable(table2)
292+
defer dropTable(table2)
293+
_, err := db.Insert(ctx, table2, g.List{
294+
{"id": 1, "passport": "user_1", "nickname": "name_1"},
295+
{"id": 2, "passport": "user_2", "nickname": "name_2"},
296+
})
297+
if err != nil {
298+
gtest.Fatal(err)
299+
}
300+
301+
gtest.C(t, func(t *gtest.T) {
302+
// LEFT JOIN - table1 has all records, table2 only has id 1,2
303+
r, err := db.Model(table1).As("t1").
304+
FieldsPrefix("t1", "id").
305+
FieldsPrefix("t2", "nickname").
306+
LeftJoin(table2+" AS t2", "t1.id = t2.id").
307+
Where("t1.id IN(?)", g.Slice{1, 2, 3}).
308+
Order("t1.id asc").
309+
All()
310+
t.AssertNil(err)
311+
312+
t.Assert(len(r), 3)
313+
t.Assert(r[0]["id"], "1")
314+
t.Assert(r[0]["nickname"], "name_1") // matched
315+
t.Assert(r[1]["id"], "2")
316+
t.Assert(r[1]["nickname"], "name_2") // matched
317+
t.Assert(r[2]["id"], "3")
318+
// r[2]["nickname"] should be NULL/empty from t2
319+
})
320+
321+
gtest.C(t, func(t *gtest.T) {
322+
// Find records where RIGHT table is NULL
323+
r, err := db.Model(table1).As("t1").
324+
FieldsPrefix("t1", "id", "nickname").
325+
LeftJoin(table2+" AS t2", "t1.id = t2.id").
326+
Where("t2.id IS NULL").
327+
Where("t1.id IN(?)", g.Slice{1, 2, 3, 4}).
328+
Order("t1.id asc").
329+
All()
330+
t.AssertNil(err)
331+
332+
// Should return id 3,4 (not in table2)
333+
t.Assert(len(r), 2)
334+
t.Assert(r[0]["id"], "3")
335+
t.Assert(r[0]["nickname"], "name_3")
336+
t.Assert(r[1]["id"], "4")
337+
})
338+
}
339+
340+
// Test_Model_Join_RightJoinNull tests RIGHT JOIN NULL handling
341+
func Test_Model_Join_RightJoinNull(t *testing.T) {
342+
var (
343+
table1 = gtime.TimestampNanoStr() + "_table1"
344+
table2 = gtime.TimestampNanoStr() + "_table2"
345+
)
346+
// table1 has partial data
347+
createTable(table1)
348+
defer dropTable(table1)
349+
_, err := db.Insert(ctx, table1, g.List{
350+
{"id": 1, "passport": "user_1", "nickname": "name_1"},
351+
{"id": 2, "passport": "user_2", "nickname": "name_2"},
352+
})
353+
if err != nil {
354+
gtest.Fatal(err)
355+
}
356+
357+
// table2 has all data
358+
createInitTable(table2)
359+
defer dropTable(table2)
360+
361+
gtest.C(t, func(t *gtest.T) {
362+
// RIGHT JOIN - table1 only has id 1,2, table2 has all
363+
r, err := db.Model(table1).As("t1").
364+
FieldsPrefix("t2", "id").
365+
FieldsPrefix("t1", "nickname").
366+
RightJoin(table2+" AS t2", "t1.id = t2.id").
367+
Where("t2.id IN(?)", g.Slice{1, 2, 3}).
368+
Order("t2.id asc").
369+
All()
370+
t.AssertNil(err)
371+
372+
t.Assert(len(r), 3)
373+
t.Assert(r[0]["id"], "1")
374+
t.Assert(r[0]["nickname"], "name_1") // matched
375+
t.Assert(r[1]["id"], "2")
376+
t.Assert(r[1]["nickname"], "name_2") // matched
377+
t.Assert(r[2]["id"], "3")
378+
// r[2]["nickname"] should be NULL/empty from t1
379+
})
380+
381+
gtest.C(t, func(t *gtest.T) {
382+
// Find records where LEFT table is NULL
383+
r, err := db.Model(table1).As("t1").
384+
FieldsPrefix("t2", "id", "nickname").
385+
RightJoin(table2+" AS t2", "t1.id = t2.id").
386+
Where("t1.id IS NULL").
387+
Where("t2.id IN(?)", g.Slice{1, 2, 3, 4}).
388+
Order("t2.id asc").
389+
All()
390+
t.AssertNil(err)
391+
392+
// Should return id 3,4 (not in table1)
393+
t.Assert(len(r), 2)
394+
t.Assert(r[0]["id"], "3")
395+
t.Assert(r[0]["nickname"], "name_3")
396+
t.Assert(r[1]["id"], "4")
397+
})
398+
}
399+
400+
// Test_Model_Join_OnVsWhere tests difference between ON and WHERE conditions
401+
func Test_Model_Join_OnVsWhere(t *testing.T) {
402+
var (
403+
table1 = gtime.TimestampNanoStr() + "_table1"
404+
table2 = gtime.TimestampNanoStr() + "_table2"
405+
)
406+
createInitTable(table1)
407+
defer dropTable(table1)
408+
createInitTable(table2)
409+
defer dropTable(table2)
410+
411+
gtest.C(t, func(t *gtest.T) {
412+
// INNER JOIN: ON and WHERE behave the same
413+
r1, err := db.Model(table1).As("t1").
414+
Fields("t1.id").
415+
InnerJoin(table2+" AS t2", "t1.id = t2.id AND t2.id <= 3").
416+
Order("t1.id asc").
417+
All()
418+
t.AssertNil(err)
419+
420+
r2, err := db.Model(table1).As("t1").
421+
Fields("t1.id").
422+
InnerJoin(table2+" AS t2", "t1.id = t2.id").
423+
Where("t2.id <=", 3).
424+
Order("t1.id asc").
425+
All()
426+
t.AssertNil(err)
427+
428+
// For INNER JOIN, results should be identical
429+
t.Assert(len(r1), 3)
430+
t.Assert(len(r2), 3)
431+
t.Assert(r1[0]["id"], r2[0]["id"])
432+
})
433+
434+
gtest.C(t, func(t *gtest.T) {
435+
// LEFT JOIN: ON filter in join condition vs WHERE filter after join
436+
// ON condition: filters t2 before join (keeps all t1 rows)
437+
r1, err := db.Model(table1).As("t1").
438+
FieldsPrefix("t1", "id").
439+
FieldsPrefix("t2", "nickname").
440+
LeftJoin(table2+" AS t2", "t1.id = t2.id AND t2.id <= 2").
441+
Where("t1.id <=", 4).
442+
Order("t1.id asc").
443+
All()
444+
t.AssertNil(err)
445+
446+
// WHERE condition: filters result after join (removes t1 rows where t2 is NULL)
447+
r2, err := db.Model(table1).As("t1").
448+
FieldsPrefix("t1", "id").
449+
FieldsPrefix("t2", "nickname").
450+
LeftJoin(table2+" AS t2", "t1.id = t2.id").
451+
Where("t1.id <=", 4).
452+
Where("t2.id <=", 2).
453+
Order("t1.id asc").
454+
All()
455+
t.AssertNil(err)
456+
457+
// r1: all t1 rows (1,2,3,4), t2 data only for id 1,2
458+
t.Assert(len(r1), 4)
459+
t.Assert(r1[0]["id"], "1")
460+
t.Assert(r1[0]["nickname"], "name_1")
461+
t.Assert(r1[2]["id"], "3")
462+
// r1[2]["nickname"] is NULL from t2
463+
464+
// r2: only rows where t2.id <= 2, so only id 1,2
465+
t.Assert(len(r2), 2)
466+
t.Assert(r2[0]["id"], "1")
467+
t.Assert(r2[1]["id"], "2")
468+
})
469+
}
470+
471+
// Test_Model_Join_ComplexConditions tests joins with complex ON conditions
472+
func Test_Model_Join_ComplexConditions(t *testing.T) {
473+
var (
474+
table1 = gtime.TimestampNanoStr() + "_table1"
475+
table2 = gtime.TimestampNanoStr() + "_table2"
476+
)
477+
createInitTable(table1)
478+
defer dropTable(table1)
479+
createInitTable(table2)
480+
defer dropTable(table2)
481+
482+
gtest.C(t, func(t *gtest.T) {
483+
// Multiple AND conditions in ON clause
484+
r, err := db.Model(table1).As("t1").
485+
Fields("t1.id", "t1.nickname").
486+
InnerJoin(
487+
table2+" AS t2",
488+
"t1.id = t2.id AND t1.nickname = t2.nickname AND t1.id BETWEEN 2 AND 4",
489+
).
490+
Order("t1.id asc").
491+
All()
492+
t.AssertNil(err)
493+
494+
t.Assert(len(r), 3)
495+
t.Assert(r[0]["id"], "2")
496+
t.Assert(r[2]["id"], "4")
497+
})
498+
499+
gtest.C(t, func(t *gtest.T) {
500+
// OR conditions in ON clause (need to use Where for OR in join)
501+
r, err := db.Model(table1).As("t1").
502+
Fields("t1.id").
503+
InnerJoin(table2+" AS t2", "t1.id = t2.id").
504+
Where("t2.id = 1 OR t2.id = 5").
505+
Order("t1.id asc").
506+
All()
507+
t.AssertNil(err)
508+
509+
t.Assert(len(r), 2)
510+
t.Assert(r[0]["id"], "1")
511+
t.Assert(r[1]["id"], "5")
512+
})
513+
}

0 commit comments

Comments
 (0)