From 02abc515a36956e8e414cda19d233b57b305e375 Mon Sep 17 00:00:00 2001 From: Jack Ling <34231795+lingcoder@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:51:52 +0800 Subject: [PATCH] test(contrib/drivers/gaussdb): add soft time, with, scanlist test coverage (#4686) ## Summary - Port 3 test files and 4 testdata SQL files from PgSQL driver to GaussDB driver - Add `gaussdb_z_unit_feature_soft_time_test.go` (15 tests): soft time create/update/delete, bool/int/datetime soft delete - Add `gaussdb_z_unit_feature_with_test.go` (6 tests): With/WithAll ORM relation queries, multiple dependency levels - Add `gaussdb_z_unit_feature_scanlist_test.go` (9 tests): ScanList for 1:1, 1:N, N:N relation mapping - Add 4 testdata SQL files for With relation tests - **30 new test functions**, ~3,941 net new lines ## Test plan - [x] `go build ./...` passes - [x] `gofmt` and `gci` applied - [x] No remaining `pgsql` references in new files - [ ] Run full test suite against GaussDB instance ref #4689 --------- Co-authored-by: John Guo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../gaussdb_z_unit_feature_scanlist_test.go | 1004 +++++++++++ .../gaussdb_z_unit_feature_soft_time_test.go | 1301 ++++++++++++++ .../gaussdb_z_unit_feature_with_test.go | 1592 +++++++++++++++++ .../testdata/with_multiple_depends.sql | 30 + .../gaussdb/testdata/with_tpl_user.sql | 4 + .../gaussdb/testdata/with_tpl_user_detail.sql | 4 + .../gaussdb/testdata/with_tpl_user_scores.sql | 5 + 7 files changed, 3940 insertions(+) create mode 100644 contrib/drivers/gaussdb/gaussdb_z_unit_feature_scanlist_test.go create mode 100644 contrib/drivers/gaussdb/gaussdb_z_unit_feature_soft_time_test.go create mode 100644 contrib/drivers/gaussdb/gaussdb_z_unit_feature_with_test.go create mode 100644 contrib/drivers/gaussdb/testdata/with_multiple_depends.sql create mode 100644 contrib/drivers/gaussdb/testdata/with_tpl_user.sql create mode 100644 contrib/drivers/gaussdb/testdata/with_tpl_user_detail.sql create mode 100644 contrib/drivers/gaussdb/testdata/with_tpl_user_scores.sql diff --git a/contrib/drivers/gaussdb/gaussdb_z_unit_feature_scanlist_test.go b/contrib/drivers/gaussdb/gaussdb_z_unit_feature_scanlist_test.go new file mode 100644 index 000000000..ed81c966d --- /dev/null +++ b/contrib/drivers/gaussdb/gaussdb_z_unit_feature_scanlist_test.go @@ -0,0 +1,1004 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gaussdb_test + +import ( + "context" + "fmt" + "testing" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/gconv" +) + +func Test_Table_Relation_One(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL, + course varchar(45) NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `orm:"uid"` + Name string `orm:"name"` + } + + type EntityUserDetail struct { + Uid int `orm:"uid"` + Address string `orm:"address"` + } + + type EntityUserScores struct { + Id int `orm:"id"` + Uid int `orm:"uid"` + Score int `orm:"score"` + Course string `orm:"course"` + } + + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + var err error + gtest.C(t, func(t *gtest.T) { + err = db.Transaction(context.TODO(), func(ctx context.Context, tx gdb.TX) error { + r, err := tx.Model(tableUser).Data(g.Map{ + "name": "john", + }).Insert() + if err != nil { + return err + } + uid, err := r.LastInsertId() + if err != nil { + return err + } + _, err = tx.Model(tableUserDetail).Data(g.Map{ + "uid": uid, + "address": "Beijing DongZhiMen #66", + }).Insert() + if err != nil { + return err + } + _, err = tx.Model(tableUserScores).Data(g.Slice{ + g.Map{"uid": uid, "score": 100, "course": "math"}, + g.Map{"uid": uid, "score": 99, "course": "physics"}, + }).Insert() + return err + }) + t.AssertNil(err) + }) + // Data check. + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(tableUser).All() + t.AssertNil(err) + t.Assert(r.Len(), 1) + t.Assert(r[0]["uid"].Int(), 1) + t.Assert(r[0]["name"].String(), "john") + + r, err = db.Model(tableUserDetail).Where("uid", r[0]["uid"].Int()).All() + t.AssertNil(err) + t.Assert(r.Len(), 1) + t.Assert(r[0]["uid"].Int(), 1) + t.Assert(r[0]["address"].String(), `Beijing DongZhiMen #66`) + + r, err = db.Model(tableUserScores).Where("uid", r[0]["uid"].Int()).All() + t.AssertNil(err) + t.Assert(r.Len(), 2) + t.Assert(r[0]["uid"].Int(), 1) + t.Assert(r[1]["uid"].Int(), 1) + t.Assert(r[0]["course"].String(), `math`) + t.Assert(r[1]["course"].String(), `physics`) + }) + // Entity query. + gtest.C(t, func(t *gtest.T) { + var user Entity + // SELECT * FROM `user` WHERE `name`='john' + err := db.Model(tableUser).Scan(&user.User, "name", "john") + t.AssertNil(err) + + // SELECT * FROM `user_detail` WHERE `uid`=1 + err = db.Model(tableUserDetail).Scan(&user.UserDetail, "uid", user.User.Uid) + t.AssertNil(err) + + // SELECT * FROM `user_scores` WHERE `uid`=1 + err = db.Model(tableUserScores).Scan(&user.UserScores, "uid", user.User.Uid) + t.AssertNil(err) + + t.Assert(user.User, EntityUser{ + Uid: 1, + Name: "john", + }) + t.Assert(user.UserDetail, EntityUserDetail{ + Uid: 1, + Address: "Beijing DongZhiMen #66", + }) + t.Assert(user.UserScores, []EntityUserScores{ + {Id: 1, Uid: 1, Course: "math", Score: 100}, + {Id: 2, Uid: 1, Course: "physics", Score: 99}, + }) + }) +} + +func Test_Table_Relation_Many(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } + } + }) + + // MapKeyValue. + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + t.Assert(all.Len(), 2) + t.Assert(len(all.MapKeyValue("uid")), 2) + t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) + t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) + all, err = db.Model(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() + t.AssertNil(err) + t.Assert(all.Len(), 10) + t.Assert(len(all.MapKeyValue("uid")), 2) + t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5) + t.Assert(len(all.MapKeyValue("uid")["4"].Slice()), 5) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["uid"], 3) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["score"], 1) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5) + }) + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid:Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid:Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Model ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Model(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + // Detail + err = db.Model(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid:Uid") + t.AssertNil(err) + // Scores + err = db.Model(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid:Uid") + t.AssertNil(err) + + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_Many_ModelScanList(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } + } + }) + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + err := db.Model(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + + // Detail + err = db.Model(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + + // Scores + err = db.Model(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid:Uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_EmptyData(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 0) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid:uid") + t.AssertNil(err) + + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid:uid") + t.AssertNil(err) + }) +} + +func Test_Table_Relation_NoneEqualDataSize(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail and Scores are not inserted. + } + }) + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, nil) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 0) + }) + + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "User") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + // Detail + all, err = db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "User", "Uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, nil) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "User", "UID") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 0) + }) + + // Model ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Model(tableUser). + Where("uid", g.Slice{3, 4}). + Order("uid asc"). + ScanList(&users, "User") + t.AssertNil(err) + // Detail + err = db.Model(tableUserDetail). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("uid asc"). + ScanList(&users, "UserDetail", "User", "uid") + t.AssertNil(err) + // Scores + err = db.Model(tableUserScores). + Where("uid", gdb.ListItemValues(users, "User", "Uid")). + Order("id asc"). + ScanList(&users, "UserScores", "User", "uid") + t.AssertNil(err) + + t.Assert(len(users), 2) + t.Assert(users[0].User, &EntityUser{3, "name_3"}) + t.Assert(users[1].User, &EntityUser{4, "name_4"}) + + t.Assert(users[0].UserDetail, nil) + + t.Assert(len(users[0].UserScores), 0) + }) +} + +func Test_Table_Relation_EmbeddedStruct1(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + *EntityUser + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + *EntityUser + *EntityUserDetail + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } + } + }) + + gtest.C(t, func(t *gtest.T) { + var ( + err error + scores []*EntityUserScores + ) + // SELECT * FROM `user_scores` + err = db.Model(tableUserScores).Scan(&scores) + t.AssertNil(err) + + // SELECT * FROM `user_scores` WHERE `uid` IN(1,2,3,4,5) + err = db.Model(tableUser). + Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). + ScanList(&scores, "EntityUser", "uid:Uid") + t.AssertNil(err) + + // SELECT * FROM `user_detail` WHERE `uid` IN(1,2,3,4,5) + err = db.Model(tableUserDetail). + Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). + ScanList(&scores, "EntityUserDetail", "uid:Uid") + t.AssertNil(err) + + // Assertions. + t.Assert(len(scores), 25) + t.Assert(scores[0].Id, 1) + t.Assert(scores[0].Uid, 1) + t.Assert(scores[0].Name, "name_1") + t.Assert(scores[0].Address, "address_1") + t.Assert(scores[24].Id, 25) + t.Assert(scores[24].Uid, 5) + t.Assert(scores[24].Name, "name_5") + t.Assert(scores[24].Address, "address_5") + }) +} + +func Test_Table_Relation_EmbeddedStruct2(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampNanoStr() + tableUserDetail = "user_detail_" + gtime.TimestampNanoStr() + tableUserScores = "user_scores_" + gtime.TimestampNanoStr() + ) + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + type Entity struct { + *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + t.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + t.AssertNil(err) + } + } + }) + + // MapKeyValue. + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All() + t.AssertNil(err) + t.Assert(all.Len(), 2) + t.Assert(len(all.MapKeyValue("uid")), 2) + t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3) + t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4) + all, err = db.Model(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All() + t.AssertNil(err) + t.Assert(all.Len(), 10) + t.Assert(len(all.MapKeyValue("uid")), 2) + t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5) + t.Assert(len(all.MapKeyValue("uid")["4"].Slice()), 5) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["uid"], 3) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["score"], 1) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3) + t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5) + }) + + // Result ScanList with struct elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []Entity + // User + err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].EntityUser, &EntityUser{3, "name_3"}) + t.Assert(users[1].EntityUser, &EntityUser{4, "name_4"}) + // Detail + all, err := db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // Result ScanList with pointer elements and pointer attributes. + gtest.C(t, func(t *gtest.T) { + var users []*Entity + // User + err := db.Model(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].EntityUser, &EntityUser{3, "name_3"}) + t.Assert(users[1].EntityUser, &EntityUser{4, "name_4"}) + // Detail + all, err := db.Model(tableUserDetail).Where("uid", gdb.ListItemValues(users, "Uid")).Order("uid asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserDetail", "uid") + t.AssertNil(err) + t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"}) + t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"}) + // Scores + all, err = db.Model(tableUserScores).Where("uid", gdb.ListItemValues(users, "Uid")).Order("id asc").All() + t.AssertNil(err) + err = all.ScanList(&users, "UserScores", "uid") + t.AssertNil(err) + t.Assert(len(users[0].UserScores), 5) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Score, 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} diff --git a/contrib/drivers/gaussdb/gaussdb_z_unit_feature_soft_time_test.go b/contrib/drivers/gaussdb/gaussdb_z_unit_feature_soft_time_test.go new file mode 100644 index 000000000..46bc67c7f --- /dev/null +++ b/contrib/drivers/gaussdb/gaussdb_z_unit_feature_soft_time_test.go @@ -0,0 +1,1301 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gaussdb_test + +import ( + "fmt" + "testing" + "time" + + // "github.com/gogf/gf/v2/database/gdb" // FIXME: Uncomment when boolean soft delete tests are enabled + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" +) + +// CreateAt/UpdateAt/DeleteAt. +func Test_SoftTime_CreateUpdateDelete1(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(6) DEFAULT NULL, + update_at timestamp(6) DEFAULT NULL, + delete_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["delete_at"].String(), "") + t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save (GaussDB uses OnConflict instead of REPLACE) + dataSave := g.Map{ + "id": 1, + "name": "name_10", + } + r, err = db.Model(table).Data(dataSave).OnConflict("id").Save() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneSave, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["delete_at"].String(), "") + t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) + t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := g.Map{ + "name": "name_1000", + } + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["delete_at"].String(), "") + t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Model(table).Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one4), 0) + one5, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(i, 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Model(table).Unscoped().Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one6), 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 0) + }) +} + +// CreateAt/UpdateAt/DeleteAt with timestamp(0). +func Test_SoftTime_CreateUpdateDelete2(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(0) DEFAULT NULL, + update_at timestamp(0) DEFAULT NULL, + delete_at timestamp(0) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["delete_at"].String(), "") + t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := g.Map{ + "id": 1, + "name": "name_10", + } + r, err = db.Model(table).Data(dataSave).OnConflict("id").Save() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneSave, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["delete_at"].String(), "") + t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) + t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := g.Map{ + "name": "name_1000", + } + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["delete_at"].String(), "") + t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Model(table).Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one4), 0) + one5, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(i, 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Model(table).Unscoped().Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one6), 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 0) + }) +} + +// CreatedAt/UpdatedAt/DeletedAt. +func Test_SoftTime_CreatedUpdatedDeleted_Map(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + created_at timestamp(6) DEFAULT NULL, + updated_at timestamp(6) DEFAULT NULL, + deleted_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["deleted_at"].String(), "") + t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := g.Map{ + "id": 1, + "name": "name_10", + } + r, err = db.Model(table).Data(dataSave).OnConflict("id").Save() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneSave, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["deleted_at"].String(), "") + t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := g.Map{ + "name": "name_1000", + } + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["deleted_at"].String(), "") + t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Model(table).Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one4), 0) + one5, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(i, 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Model(table).Unscoped().Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one6), 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 0) + }) +} + +// CreatedAt/UpdatedAt/DeletedAt with struct. +func Test_SoftTime_CreatedUpdatedDeleted_Struct(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + created_at timestamp(6) DEFAULT NULL, + updated_at timestamp(6) DEFAULT NULL, + deleted_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + type User struct { + Id int + Name string + CreatedAT *gtime.Time + UpdatedAT *gtime.Time + DeletedAT *gtime.Time + } + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := User{ + Id: 1, + Name: "name_1", + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["deleted_at"].String(), "") + t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := User{ + Id: 1, + Name: "name_10", + } + r, err = db.Model(table).Data(dataSave).OmitEmpty().OnConflict("id").Save() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneSave, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["deleted_at"].String(), "") + t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := User{ + Name: "name_1000", + } + r, err = db.Model(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["deleted_at"].String(), "") + t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-4) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Model(table).Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one4), 0) + one5, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(i, 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Model(table).Unscoped().Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one6), 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 0) + }) +} + +func Test_SoftUpdateTime(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + num integer DEFAULT NULL, + create_at timestamp(6) DEFAULT NULL, + update_at timestamp(6) DEFAULT NULL, + delete_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "num": 10, + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["num"].Int(), 10) + + // Update. + r, err = db.Model(table).Data("num=num+1").Where("id=?", 1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + }) +} + +func Test_SoftUpdateTime_WithDO(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + num integer DEFAULT NULL, + created_at timestamp(6) DEFAULT NULL, + updated_at timestamp(6) DEFAULT NULL, + deleted_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "num": 10, + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInserted, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInserted["id"].Int(), 1) + t.Assert(oneInserted["num"].Int(), 10) + + // Update. + time.Sleep(2 * time.Second) + type User struct { + g.Meta `orm:"do:true"` + Id any + Num any + CreatedAt any + UpdatedAt any + DeletedAt any + } + r, err = db.Model(table).Data(User{ + Num: 100, + }).Where("id=?", 1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdated, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdated["num"].Int(), 100) + t.Assert(oneUpdated["created_at"].String(), oneInserted["created_at"].String()) + t.AssertNE(oneUpdated["updated_at"].String(), oneInserted["updated_at"].String()) + }) +} + +func Test_SoftDelete(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(6) DEFAULT NULL, + update_at timestamp(6) DEFAULT NULL, + delete_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + // db.SetDebug(true) + gtest.C(t, func(t *gtest.T) { + for i := 1; i <= 10; i++ { + data := g.Map{ + "id": i, + "name": fmt.Sprintf("name_%d", i), + } + r, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + } + }) + gtest.C(t, func(t *gtest.T) { + one, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.AssertNE(one["create_at"].String(), "") + t.AssertNE(one["update_at"].String(), "") + t.Assert(one["delete_at"].String(), "") + }) + gtest.C(t, func(t *gtest.T) { + one, err := db.Model(table).WherePri(10).One() + t.AssertNil(err) + t.AssertNE(one["create_at"].String(), "") + t.AssertNE(one["update_at"].String(), "") + t.Assert(one["delete_at"].String(), "") + }) + gtest.C(t, func(t *gtest.T) { + ids := g.SliceInt{1, 3, 5} + r, err := db.Model(table).Where("id", ids).Delete() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 3) + + count, err := db.Model(table).Where("id", ids).Count() + t.AssertNil(err) + t.Assert(count, 0) + + all, err := db.Model(table).Unscoped().Where("id", ids).All() + t.AssertNil(err) + t.Assert(len(all), 3) + t.AssertNE(all[0]["create_at"].String(), "") + t.AssertNE(all[0]["update_at"].String(), "") + t.AssertNE(all[0]["delete_at"].String(), "") + t.AssertNE(all[1]["create_at"].String(), "") + t.AssertNE(all[1]["update_at"].String(), "") + t.AssertNE(all[1]["delete_at"].String(), "") + t.AssertNE(all[2]["create_at"].String(), "") + t.AssertNE(all[2]["update_at"].String(), "") + t.AssertNE(all[2]["delete_at"].String(), "") + }) +} + +func Test_SoftDelete_Join(t *testing.T) { + table1 := "time_test_table1_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(6) DEFAULT NULL, + update_at timestamp(6) DEFAULT NULL, + delete_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table1)); err != nil { + gtest.Error(err) + } + defer dropTable(table1) + + table2 := "time_test_table2_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + createat timestamp(6) DEFAULT NULL, + updateat timestamp(6) DEFAULT NULL, + deleteat timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table2)); err != nil { + gtest.Error(err) + } + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + // db.SetDebug(true) + dataInsert1 := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Model(table1).Data(dataInsert1).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + dataInsert2 := g.Map{ + "id": 1, + "name": "name_2", + } + r, err = db.Model(table2).Data(dataInsert2).Insert() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").One() + t.AssertNil(err) + t.Assert(one["name"], "name_1") + + // Soft deleting. + r, err = db.Model(table1).Where("1=1").Delete() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + one, err = db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").One() + t.AssertNil(err) + t.Assert(one.IsEmpty(), true) + + one, err = db.Model(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").One() + t.AssertNil(err) + t.Assert(one.IsEmpty(), true) + }) +} + +func Test_SoftDelete_WhereAndOr(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(6) DEFAULT NULL, + update_at timestamp(6) DEFAULT NULL, + delete_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + // db.SetDebug(true) + // Add datas. + gtest.C(t, func(t *gtest.T) { + for i := 1; i <= 10; i++ { + data := g.Map{ + "id": i, + "name": fmt.Sprintf("name_%d", i), + } + r, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + } + }) + gtest.C(t, func(t *gtest.T) { + ids := g.SliceInt{1, 3, 5} + r, err := db.Model(table).Where("id", ids).Delete() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 3) + + count, err := db.Model(table).Where("id", 1).WhereOr("id", 3).Count() + t.AssertNil(err) + t.Assert(count, 0) + }) +} + +func Test_CreateUpdateTime_Struct(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(6) DEFAULT NULL, + update_at timestamp(6) DEFAULT NULL, + delete_at timestamp(6) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + // db.SetDebug(true) + // defer db.SetDebug(false) + + type Entity struct { + Id uint64 `orm:"id,primary" json:"id"` + Name string `orm:"name" json:"name"` + CreateAt *gtime.Time `orm:"create_at" json:"create_at"` + UpdateAt *gtime.Time `orm:"update_at" json:"update_at"` + DeleteAt *gtime.Time `orm:"delete_at" json:"delete_at"` + } + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := &Entity{ + Id: 1, + Name: "name_1", + CreateAt: nil, + UpdateAt: nil, + DeleteAt: nil, + } + r, err := db.Model(table).Data(dataInsert).OmitEmpty().Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["delete_at"].String(), "") + t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + time.Sleep(2 * time.Second) + + // Save + dataSave := &Entity{ + Id: 1, + Name: "name_10", + CreateAt: nil, + UpdateAt: nil, + DeleteAt: nil, + } + r, err = db.Model(table).Data(dataSave).OmitEmpty().OnConflict("id").Save() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneSave, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["delete_at"].String(), "") + t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) + t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + time.Sleep(2 * time.Second) + + // Update + dataUpdate := &Entity{ + Id: 1, + Name: "name_1000", + CreateAt: nil, + UpdateAt: nil, + DeleteAt: nil, + } + r, err = db.Model(table).Data(dataUpdate).WherePri(1).OmitEmpty().Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["delete_at"].String(), "") + t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Model(table).Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one4), 0) + one5, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(i, 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Model(table).Unscoped().Delete("id", 1) + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one6), 0) + i, err = db.Model(table).Unscoped().Count() + t.AssertNil(err) + t.Assert(i, 0) + }) +} + +func Test_SoftTime_CreateUpdateDelete_UnixTimestamp(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at integer DEFAULT NULL, + update_at integer DEFAULT NULL, + delete_at integer DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + // insert + gtest.C(t, func(t *gtest.T) { + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(one["name"].String(), "name_1") + t.AssertGT(one["create_at"].Int64(), 0) + t.AssertGT(one["update_at"].Int64(), 0) + t.Assert(one["delete_at"].Int64(), 0) + t.Assert(len(one["create_at"].String()), 10) + t.Assert(len(one["update_at"].String()), 10) + }) + + // sleep some seconds to make update time greater than create time. + time.Sleep(2 * time.Second) + + // update + gtest.C(t, func(t *gtest.T) { + // update: map + dataInsert := g.Map{ + "name": "name_11", + } + r, err := db.Model(table).Data(dataInsert).WherePri(1).Update() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(one["name"].String(), "name_11") + t.AssertGT(one["create_at"].Int64(), 0) + t.AssertGT(one["update_at"].Int64(), 0) + t.Assert(one["delete_at"].Int64(), 0) + t.Assert(len(one["create_at"].String()), 10) + t.Assert(len(one["update_at"].String()), 10) + + var ( + lastCreateTime = one["create_at"].Int64() + lastUpdateTime = one["update_at"].Int64() + ) + + time.Sleep(2 * time.Second) + + // update: string + r, err = db.Model(table).Data("name='name_111'").WherePri(1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + one, err = db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(one["name"].String(), "name_111") + t.Assert(one["create_at"].Int64(), lastCreateTime) + t.AssertGT(one["update_at"].Int64(), lastUpdateTime) + t.Assert(one["delete_at"].Int64(), 0) + }) + + // delete + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).WherePri(1).Delete() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(len(one), 0) + + one, err = db.Model(table).Unscoped().WherePri(1).One() + t.AssertNil(err) + t.Assert(one["name"].String(), "name_111") + t.AssertGT(one["create_at"].Int64(), 0) + t.AssertGT(one["update_at"].Int64(), 0) + t.AssertGT(one["delete_at"].Int64(), 0) + }) +} + +// FIXME: GaussDB boolean type soft delete is not supported yet. +// The framework generates "delete_at=0" condition for soft delete query, +// but GaussDB boolean cannot compare with integer directly. +// Uncomment this test after the framework supports GaussDB boolean soft delete. +// +// func Test_SoftTime_CreateUpdateDelete_Bool_Deleted(t *testing.T) { +// table := "soft_time_test_table_" + gtime.TimestampNanoStr() +// if _, err := db.Exec(ctx, fmt.Sprintf(` +// CREATE TABLE %s ( +// id integer NOT NULL, +// name varchar(45) DEFAULT NULL, +// create_at integer DEFAULT NULL, +// update_at integer DEFAULT NULL, +// delete_at boolean DEFAULT NULL, +// PRIMARY KEY (id) +// ); +// `, table)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(table) +// +// //db.SetDebug(true) +// // insert +// gtest.C(t, func(t *gtest.T) { +// dataInsert := g.Map{ +// "id": 1, +// "name": "name_1", +// } +// r, err := db.Model(table).Data(dataInsert).Insert() +// t.AssertNil(err) +// n, _ := r.RowsAffected() +// t.Assert(n, 1) +// +// one, err := db.Model(table).WherePri(1).One() +// t.AssertNil(err) +// t.Assert(one["name"].String(), "name_1") +// t.AssertGT(one["create_at"].Int64(), 0) +// t.AssertGT(one["update_at"].Int64(), 0) +// t.Assert(one["delete_at"].Bool(), false) +// t.Assert(len(one["create_at"].String()), 10) +// t.Assert(len(one["update_at"].String()), 10) +// }) +// +// // delete +// gtest.C(t, func(t *gtest.T) { +// r, err := db.Model(table).WherePri(1).Delete() +// t.AssertNil(err) +// n, _ := r.RowsAffected() +// t.Assert(n, 1) +// +// one, err := db.Model(table).WherePri(1).One() +// t.AssertNil(err) +// t.Assert(len(one), 0) +// +// one, err = db.Model(table).Unscoped().WherePri(1).One() +// t.AssertNil(err) +// t.Assert(one["name"].String(), "name_1") +// t.AssertGT(one["create_at"].Int64(), 0) +// t.AssertGT(one["update_at"].Int64(), 0) +// t.Assert(one["delete_at"].Bool(), true) +// }) +// } + +// FIXME: GaussDB boolean type soft delete is not supported yet. +// The framework generates "delete_at=0" condition for soft delete query, +// but GaussDB boolean cannot compare with integer directly. +// Uncomment this test after the framework supports GaussDB boolean soft delete. +// +// func Test_SoftTime_CreateUpdateDelete_Option_SoftTimeTypeTimestampMilli(t *testing.T) { +// table := "soft_time_test_table_" + gtime.TimestampNanoStr() +// if _, err := db.Exec(ctx, fmt.Sprintf(` +// CREATE TABLE %s ( +// id integer NOT NULL, +// name varchar(45) DEFAULT NULL, +// create_at bigint DEFAULT NULL, +// update_at bigint DEFAULT NULL, +// delete_at boolean DEFAULT NULL, +// PRIMARY KEY (id) +// ); +// `, table)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(table) +// +// var softTimeOption = gdb.SoftTimeOption{ +// SoftTimeType: gdb.SoftTimeTypeTimestampMilli, +// } +// +// // insert +// gtest.C(t, func(t *gtest.T) { +// dataInsert := g.Map{ +// "id": 1, +// "name": "name_1", +// } +// r, err := db.Model(table).SoftTime(softTimeOption).Data(dataInsert).Insert() +// t.AssertNil(err) +// n, _ := r.RowsAffected() +// t.Assert(n, 1) +// +// one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One() +// t.AssertNil(err) +// t.Assert(one["name"].String(), "name_1") +// t.Assert(len(one["create_at"].String()), 13) +// t.Assert(len(one["update_at"].String()), 13) +// t.Assert(one["delete_at"].Bool(), false) +// }) +// +// // delete +// gtest.C(t, func(t *gtest.T) { +// r, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).Delete() +// t.AssertNil(err) +// n, _ := r.RowsAffected() +// t.Assert(n, 1) +// +// one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One() +// t.AssertNil(err) +// t.Assert(len(one), 0) +// +// one, err = db.Model(table).Unscoped().WherePri(1).One() +// t.AssertNil(err) +// t.Assert(one["name"].String(), "name_1") +// t.AssertGT(one["create_at"].Int64(), 0) +// t.AssertGT(one["update_at"].Int64(), 0) +// t.Assert(one["delete_at"].Bool(), true) +// }) +// } + +// FIXME: GaussDB boolean type soft delete is not supported yet. +// The framework generates "delete_at=0" condition for soft delete query, +// but GaussDB boolean cannot compare with integer directly. +// Uncomment this test after the framework supports GaussDB boolean soft delete. +// +// func Test_SoftTime_CreateUpdateDelete_Option_SoftTimeTypeTimestampNano(t *testing.T) { +// table := "soft_time_test_table_" + gtime.TimestampNanoStr() +// if _, err := db.Exec(ctx, fmt.Sprintf(` +// CREATE TABLE %s ( +// id integer NOT NULL, +// name varchar(45) DEFAULT NULL, +// create_at bigint DEFAULT NULL, +// update_at bigint DEFAULT NULL, +// delete_at boolean DEFAULT NULL, +// PRIMARY KEY (id) +// ); +// `, table)); err != nil { +// gtest.Error(err) +// } +// defer dropTable(table) +// +// var softTimeOption = gdb.SoftTimeOption{ +// SoftTimeType: gdb.SoftTimeTypeTimestampNano, +// } +// +// // insert +// gtest.C(t, func(t *gtest.T) { +// dataInsert := g.Map{ +// "id": 1, +// "name": "name_1", +// } +// r, err := db.Model(table).SoftTime(softTimeOption).Data(dataInsert).Insert() +// t.AssertNil(err) +// n, _ := r.RowsAffected() +// t.Assert(n, 1) +// +// one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One() +// t.AssertNil(err) +// t.Assert(one["name"].String(), "name_1") +// t.Assert(len(one["create_at"].String()), 19) +// t.Assert(len(one["update_at"].String()), 19) +// t.Assert(one["delete_at"].Bool(), false) +// }) +// +// // delete +// gtest.C(t, func(t *gtest.T) { +// r, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).Delete() +// t.AssertNil(err) +// n, _ := r.RowsAffected() +// t.Assert(n, 1) +// +// one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One() +// t.AssertNil(err) +// t.Assert(len(one), 0) +// +// one, err = db.Model(table).Unscoped().WherePri(1).One() +// t.AssertNil(err) +// t.Assert(one["name"].String(), "name_1") +// t.AssertGT(one["create_at"].Int64(), 0) +// t.AssertGT(one["update_at"].Int64(), 0) +// t.Assert(one["delete_at"].Bool(), true) +// }) +// } + +func Test_SoftTime_CreateUpdateDelete_Specified(t *testing.T) { + table := "soft_time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id integer NOT NULL, + name varchar(45) DEFAULT NULL, + create_at timestamp(0) DEFAULT NULL, + update_at timestamp(0) DEFAULT NULL, + delete_at timestamp(0) DEFAULT NULL, + PRIMARY KEY (id) +); + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + "create_at": gtime.NewFromStr("2024-05-30 20:00:00"), + "update_at": gtime.NewFromStr("2024-05-30 20:00:00"), + } + r, err := db.Model(table).Data(dataInsert).Insert() + t.AssertNil(err) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["delete_at"].String(), "") + t.Assert(oneInsert["create_at"].String(), "2024-05-30 20:00:00") + t.Assert(oneInsert["update_at"].String(), "2024-05-30 20:00:00") + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := g.Map{ + "id": 1, + "name": "name_10", + "update_at": gtime.NewFromStr("2024-05-30 20:15:00"), + } + r, err = db.Model(table).Data(dataSave).OnConflict("id").Save() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneSave, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["delete_at"].String(), "") + t.Assert(oneSave["create_at"].String(), "2024-05-30 20:00:00") + t.Assert(oneSave["update_at"].String(), "2024-05-30 20:15:00") + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := g.Map{ + "name": "name_1000", + "update_at": gtime.NewFromStr("2024-05-30 20:30:00"), + } + r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["delete_at"].String(), "") + t.Assert(oneUpdate["create_at"].String(), "2024-05-30 20:00:00") + t.Assert(oneUpdate["update_at"].String(), "2024-05-30 20:30:00") + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Insert with delete_at + dataInsertDelete := g.Map{ + "id": 2, + "name": "name_2", + "create_at": gtime.NewFromStr("2024-05-30 20:00:00"), + "update_at": gtime.NewFromStr("2024-05-30 20:00:00"), + "delete_at": gtime.NewFromStr("2024-05-30 20:00:00"), + } + r, err = db.Model(table).Data(dataInsertDelete).Insert() + t.AssertNil(err) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + // Delete Select + oneDelete, err := db.Model(table).WherePri(2).One() + t.AssertNil(err) + t.Assert(len(oneDelete), 0) + oneDeleteUnscoped, err := db.Model(table).Unscoped().WherePri(2).One() + t.AssertNil(err) + t.Assert(oneDeleteUnscoped["id"].Int(), 2) + t.Assert(oneDeleteUnscoped["name"].String(), "name_2") + t.Assert(oneDeleteUnscoped["delete_at"].String(), "2024-05-30 20:00:00") + t.Assert(oneDeleteUnscoped["create_at"].String(), "2024-05-30 20:00:00") + t.Assert(oneDeleteUnscoped["update_at"].String(), "2024-05-30 20:00:00") + }) +} diff --git a/contrib/drivers/gaussdb/gaussdb_z_unit_feature_with_test.go b/contrib/drivers/gaussdb/gaussdb_z_unit_feature_with_test.go new file mode 100644 index 000000000..50c59d992 --- /dev/null +++ b/contrib/drivers/gaussdb/gaussdb_z_unit_feature_with_test.go @@ -0,0 +1,1592 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gaussdb_test + +import ( + "fmt" + "testing" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gmeta" +) + +func Test_Table_Relation_With_Scan(t *testing.T) { + var ( + tableUser = "with_scan_user" + tableUserDetail = "with_scan_user_detail" + tableUserScores = "with_scan_user_score" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:with_scan_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScore struct { + gmeta.Meta `orm:"table:with_scan_user_score"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:with_scan_user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScore `orm:"with:uid=id"` + } + + // Initialize the data. + gtest.C(t, func(t *gtest.T) { + for i := 1; i <= 5; i++ { + // User. + user := User{ + Name: fmt.Sprintf(`name_%d`, i), + } + lastInsertId, err := db.Model(tableUser).Data(user).OmitEmpty().InsertAndGetId() + t.AssertNil(err) + // Detail. + userDetail := UserDetail{ + Uid: int(lastInsertId), + Address: fmt.Sprintf(`address_%d`, lastInsertId), + } + _, err = db.Model(tableUserDetail).Data(userDetail).OmitEmpty().Insert() + t.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + userScore := UserScore{ + Uid: int(lastInsertId), + Score: j, + } + _, err = db.Model(tableUserScores).Data(userScore).OmitEmpty().Insert() + t.AssertNil(err) + } + } + }) + + // Scan pointer. + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser). + With(User{}.UserDetail). + With(User{}.UserScores). + Where("id", 3). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + + // Scan struct. + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser). + With(user.UserDetail). + With(user.UserScores). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) + + // With part attribute: UserDetail. + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser). + With(user.UserDetail). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 0) + }) + + // With part attribute: UserScores. + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser). + With(user.UserScores). + Where("id", 4). + Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.Assert(user.UserDetail, nil) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_With(t *testing.T) { + var ( + tableUser = "with_rel_user" + tableUserDetail = "with_rel_user_detail" + tableUserScores = "with_rel_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:with_rel_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:with_rel_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:with_rel_user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser). + With(User{}.UserDetail). + With(User{}.UserScores). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) + + // With part attribute: UserDetail. + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser). + With(User{}.UserDetail). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 0) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 0) + }) + + // With part attribute: UserScores. + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser). + With(User{}.UserScores). + Where("id", []int{3, 4}). + Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.Assert(users[0].UserDetail, nil) + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.Assert(users[1].UserDetail, nil) + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll(t *testing.T) { + var ( + tableUser = "withall_user" + tableUserDetail = "withall_user_detail" + tableUserScores = "withall_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:withall_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:withall_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:withall_user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_List(t *testing.T) { + var ( + tableUser = "withall_list_user" + tableUserDetail = "withall_list_user_detail" + tableUserScores = "withall_list_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:withall_list_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:withall_list_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:withall_list_user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.AssertNE(users[0].UserDetail, nil) + t.Assert(users[0].UserDetail.Uid, 3) + t.Assert(users[0].UserDetail.Address, "address_3") + t.Assert(len(users[0].UserScores), 5) + t.Assert(users[0].UserScores[0].Uid, 3) + t.Assert(users[0].UserScores[0].Score, 1) + t.Assert(users[0].UserScores[4].Uid, 3) + t.Assert(users[0].UserScores[4].Score, 5) + + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 5) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 1) + t.Assert(users[1].UserScores[4].Uid, 4) + t.Assert(users[1].UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAllCondition_List(t *testing.T) { + var ( + tableUser = "withall_cond_user" + tableUserDetail = "withall_cond_user_detail" + tableUserScores = "withall_cond_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:withall_cond_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:withall_cond_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:withall_cond_user"` + Id int `json:"id"` + Name string `json:"name"` + UserDetail *UserDetail `orm:"with:uid=id, where:uid > 3"` + UserScores []*UserScores `orm:"with:uid=id, where:score>1 and score<5, order:score desc"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + db.SetDebug(true) + defer db.SetDebug(false) + + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(users[0].Id, 3) + t.Assert(users[0].Name, "name_3") + t.Assert(users[0].UserDetail, nil) + t.Assert(users[1].Id, 4) + t.Assert(users[1].Name, "name_4") + t.AssertNE(users[1].UserDetail, nil) + t.Assert(users[1].UserDetail.Uid, 4) + t.Assert(users[1].UserDetail.Address, "address_4") + t.Assert(len(users[1].UserScores), 3) + t.Assert(users[1].UserScores[0].Uid, 4) + t.Assert(users[1].UserScores[0].Score, 4) + t.Assert(users[1].UserScores[2].Uid, 4) + t.Assert(users[1].UserScores[2].Score, 2) + }) +} + +func Test_Table_Relation_WithAll_Embedded_With_SelfMaintained_Attributes(t *testing.T) { + var ( + tableUser = "withall_emsm_user" + tableUserDetail = "withall_emsm_user_detail" + tableUserScores = "withall_emsm_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:withall_emsm_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:withall_emsm_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + gmeta.Meta `orm:"table:withall_emsm_user"` + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_Embedded_Without_SelfMaintained_Attributes(t *testing.T) { + var ( + tableUser = "withall_emns_user" + tableUserDetail = "withall_emns_user_detail" + tableUserScores = "withall_emns_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:withall_emns_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:withall_emns_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + // For Test Only + type UserEmbedded struct { + Id int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:withall_emns_user"` + *UserDetail `orm:"with:uid=id"` + UserEmbedded + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + db.SetDebug(true) + defer db.SetDebug(false) + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_Embedded_WithoutMeta(t *testing.T) { + var ( + tableUser = "withall_nometa_user" + tableUserDetail = "user_detail" + tableUserScores = "user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetailBase struct { + Uid int `json:"uid"` + Address string `json:"address"` + } + + type UserDetail struct { + UserDetailBase + } + + type UserScores struct { + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type User struct { + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + UserScores []*UserScores `orm:"with:uid=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 3) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 3) + t.Assert(user.UserScores[4].Score, 5) + }) + + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].Uid, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].Uid, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_AttributeStructAlsoHasWithTag(t *testing.T) { + var ( + tableUser = "withall_nested_user" + tableUserDetail = "withall_nested_user_detail" + tableUserScores = "withall_nested_user_scores" + ) + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user.sql"), tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_detail.sql"), tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(gtest.DataContent("with_tpl_user_scores.sql"), tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserScores struct { + gmeta.Meta `orm:"table:withall_nested_user_scores"` + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + type UserDetail struct { + gmeta.Meta `orm:"table:withall_nested_user_detail"` + Uid int `json:"uid"` + Address string `json:"address"` + UserScores []*UserScores `orm:"with:uid"` + } + + type User struct { + gmeta.Meta `orm:"table:withall_nested_user"` + *UserDetail `orm:"with:uid=id"` + Id int `json:"id"` + Name string `json:"name"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 3) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 3) + t.Assert(user.UserDetail.Address, `address_3`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 3) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 3) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) + + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.Uid, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserDetail.UserScores), 5) + t.Assert(user.UserDetail.UserScores[0].Uid, 4) + t.Assert(user.UserDetail.UserScores[0].Score, 1) + t.Assert(user.UserDetail.UserScores[4].Uid, 4) + t.Assert(user.UserDetail.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_With_MultipleDepends1(t *testing.T) { + defer func() { + dropTable("table_a") + dropTable("table_b") + dropTable("table_c") + }() + for _, v := range gstr.SplitAndTrim(gfile.GetContents(gtest.DataPath("with_multiple_depends.sql")), ";") { + if _, err := db.Exec(ctx, v); err != nil { + gtest.Error(err) + } + } + + type TableC struct { + gmeta.Meta `orm:"table_c"` + Id int `orm:"id,primary" json:"id"` + TableBId int `orm:"table_b_id" json:"table_b_id"` + } + + type TableB struct { + gmeta.Meta `orm:"table_b"` + Id int `orm:"id,primary" json:"id"` + TableAId int `orm:"table_a_id" json:"table_a_id"` + TableC *TableC `orm:"with:table_b_id=id" json:"table_c"` + } + + type TableA struct { + gmeta.Meta `orm:"table_a"` + Id int `orm:"id,primary" json:"id"` + TableB *TableB `orm:"with:table_a_id=id" json:"table_b"` + } + + db.SetDebug(true) + defer db.SetDebug(false) + + // Struct. + gtest.C(t, func(t *gtest.T) { + var tableA *TableA + err := db.Model("table_a").WithAll().Scan(&tableA) + t.AssertNil(err) + t.AssertNE(tableA, nil) + t.Assert(tableA.Id, 1) + + t.AssertNE(tableA.TableB, nil) + t.AssertNE(tableA.TableB.TableC, nil) + t.Assert(tableA.TableB.TableAId, 1) + t.Assert(tableA.TableB.TableC.Id, 100) + t.Assert(tableA.TableB.TableC.TableBId, 10) + }) + + // Structs + gtest.C(t, func(t *gtest.T) { + var tableA []*TableA + err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA) + t.AssertNil(err) + t.Assert(len(tableA), 2) + t.AssertNE(tableA[0].TableB, nil) + t.AssertNE(tableA[1].TableB, nil) + t.AssertNE(tableA[0].TableB.TableC, nil) + t.AssertNE(tableA[1].TableB.TableC, nil) + + t.Assert(tableA[0].Id, 1) + t.Assert(tableA[0].TableB.Id, 10) + t.Assert(tableA[0].TableB.TableC.Id, 100) + + t.Assert(tableA[1].Id, 2) + t.Assert(tableA[1].TableB.Id, 20) + t.Assert(tableA[1].TableB.TableC.Id, 300) + }) +} + +func Test_Table_Relation_With_MultipleDepends2(t *testing.T) { + defer func() { + dropTable("table_a") + dropTable("table_b") + dropTable("table_c") + }() + for _, v := range gstr.SplitAndTrim(gfile.GetContents(gtest.DataPath("with_multiple_depends.sql")), ";") { + if _, err := db.Exec(ctx, v); err != nil { + gtest.Error(err) + } + } + + type TableC struct { + gmeta.Meta `orm:"table_c"` + Id int `orm:"id,primary" json:"id"` + TableBId int `orm:"table_b_id" json:"table_b_id"` + } + + type TableB struct { + gmeta.Meta `orm:"table_b"` + Id int `orm:"id,primary" json:"id"` + TableAId int `orm:"table_a_id" json:"table_a_id"` + TableC []*TableC `orm:"with:table_b_id=id" json:"table_c"` + } + + type TableA struct { + gmeta.Meta `orm:"table_a"` + Id int `orm:"id,primary" json:"id"` + TableB []*TableB `orm:"with:table_a_id=id" json:"table_b"` + } + + db.SetDebug(true) + defer db.SetDebug(false) + + // Struct. + gtest.C(t, func(t *gtest.T) { + var tableA *TableA + err := db.Model("table_a").WithAll().Scan(&tableA) + t.AssertNil(err) + t.AssertNE(tableA, nil) + t.Assert(tableA.Id, 1) + + t.Assert(len(tableA.TableB), 2) + t.Assert(tableA.TableB[0].Id, 10) + t.Assert(tableA.TableB[1].Id, 30) + + t.Assert(len(tableA.TableB[0].TableC), 2) + t.Assert(len(tableA.TableB[1].TableC), 1) + t.Assert(tableA.TableB[0].TableC[0].Id, 100) + t.Assert(tableA.TableB[0].TableC[0].TableBId, 10) + t.Assert(tableA.TableB[0].TableC[1].Id, 200) + t.Assert(tableA.TableB[0].TableC[1].TableBId, 10) + t.Assert(tableA.TableB[1].TableC[0].Id, 400) + t.Assert(tableA.TableB[1].TableC[0].TableBId, 30) + }) + + // Structs + gtest.C(t, func(t *gtest.T) { + var tableA []*TableA + err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA) + t.AssertNil(err) + t.Assert(len(tableA), 2) + + t.Assert(len(tableA[0].TableB), 2) + t.Assert(tableA[0].TableB[0].Id, 10) + t.Assert(tableA[0].TableB[1].Id, 30) + + t.Assert(len(tableA[0].TableB[0].TableC), 2) + t.Assert(len(tableA[0].TableB[1].TableC), 1) + t.Assert(tableA[0].TableB[0].TableC[0].Id, 100) + t.Assert(tableA[0].TableB[0].TableC[0].TableBId, 10) + t.Assert(tableA[0].TableB[0].TableC[1].Id, 200) + t.Assert(tableA[0].TableB[0].TableC[1].TableBId, 10) + t.Assert(tableA[0].TableB[1].TableC[0].Id, 400) + t.Assert(tableA[0].TableB[1].TableC[0].TableBId, 30) + + t.Assert(tableA[1].TableB[0].TableC[0].Id, 300) + t.Assert(tableA[1].TableB[0].TableC[0].TableBId, 20) + + t.Assert(tableA[1].TableB[1].Id, 40) + t.Assert(tableA[1].TableB[1].TableAId, 2) + t.Assert(tableA[1].TableB[1].TableC, nil) + }) +} + +func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) { + defer func() { + dropTable("table_a") + dropTable("table_b") + dropTable("table_c") + }() + for _, v := range gstr.SplitAndTrim(gfile.GetContents(gtest.DataPath("with_multiple_depends.sql")), ";") { + if _, err := db.Exec(ctx, v); err != nil { + gtest.Error(err) + } + } + + type TableC struct { + gmeta.Meta `orm:"table_c"` + Id int `orm:"id,primary" json:"id"` + TableBId int `orm:"table_b_id" json:"table_b_id"` + } + + type TableB struct { + gmeta.Meta `orm:"table_b"` + Id int `orm:"id,primary" json:"id"` + TableAId int `orm:"table_a_id" json:"table_a_id"` + *TableC `orm:"with:table_b_id=id" json:"table_c"` + } + + type TableA struct { + gmeta.Meta `orm:"table_a"` + Id int `orm:"id,primary" json:"id"` + *TableB `orm:"with:table_a_id=id" json:"table_b"` + } + + db.SetDebug(true) + defer db.SetDebug(false) + + // Struct. + gtest.C(t, func(t *gtest.T) { + var tableA *TableA + err := db.Model("table_a").WithAll().Scan(&tableA) + t.AssertNil(err) + t.AssertNE(tableA, nil) + t.Assert(tableA.Id, 1) + + t.AssertNE(tableA.TableB, nil) + t.AssertNE(tableA.TableB.TableC, nil) + t.Assert(tableA.TableB.TableAId, 1) + t.Assert(tableA.TableB.TableC.Id, 100) + t.Assert(tableA.TableB.TableC.TableBId, 10) + }) + + // Structs + gtest.C(t, func(t *gtest.T) { + var tableA []*TableA + err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA) + t.AssertNil(err) + t.Assert(len(tableA), 2) + t.AssertNE(tableA[0].TableB, nil) + t.AssertNE(tableA[1].TableB, nil) + t.AssertNE(tableA[0].TableB.TableC, nil) + t.AssertNE(tableA[1].TableB.TableC, nil) + + t.Assert(tableA[0].Id, 1) + t.Assert(tableA[0].TableB.Id, 10) + t.Assert(tableA[0].TableB.TableC.Id, 100) + + t.Assert(tableA[1].Id, 2) + t.Assert(tableA[1].TableB.Id, 20) + t.Assert(tableA[1].TableB.TableC.Id, 300) + }) +} + +func Test_Table_Relation_WithAll_Embedded_Meta_NameMatchingRule(t *testing.T) { + var ( + tableUser = "with_embed_user" + tableUserDetail = "with_embed_user_detail" + tableUserScores = "with_embed_user_scores" + ) + // Drop tables first to ensure clean state + dropTable(tableUser) + dropTable(tableUserDetail) + dropTable(tableUserScores) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +id SERIAL PRIMARY KEY, +name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +user_id SERIAL PRIMARY KEY, +address varchar(45) NOT NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +id SERIAL PRIMARY KEY, +user_id integer NOT NULL, +score integer NOT NULL +); + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type UserDetail struct { + gmeta.Meta `orm:"table:with_embed_user_detail"` + UserID int `json:"user_id"` + Address string `json:"address"` + } + + type UserScores struct { + gmeta.Meta `orm:"table:with_embed_user_scores"` + ID int `json:"id"` + UserID int `json:"user_id"` + Score int `json:"score"` + } + + // For Test Only + type UserEmbedded struct { + ID int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:with_embed_user"` + UserEmbedded + UserDetail UserDetail `orm:"with:user_id=id"` + UserScores []*UserScores `orm:"with:user_id=id"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "user_id": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.AssertNil(err) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(ctx, tableUserScores, g.Map{ + "user_id": i, + "score": j, + }) + gtest.AssertNil(err) + } + } + + gtest.C(t, func(t *gtest.T) { + var user User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user) + t.AssertNil(err) + t.Assert(user.ID, 4) + t.AssertNE(user.UserDetail, nil) + t.Assert(user.UserDetail.UserID, 4) + t.Assert(user.UserDetail.Address, `address_4`) + t.Assert(len(user.UserScores), 5) + t.Assert(user.UserScores[0].UserID, 4) + t.Assert(user.UserScores[0].Score, 1) + t.Assert(user.UserScores[4].UserID, 4) + t.Assert(user.UserScores[4].Score, 5) + }) +} + +func Test_Table_Relation_WithAll_Unscoped(t *testing.T) { + var ( + tableUser = "with_unscoped_user" + tableUserDetail = "with_unscoped_user_detail" + ) + // Drop tables first to ensure clean state + dropTable(tableUser) + dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +id SERIAL PRIMARY KEY, +name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +user_id SERIAL PRIMARY KEY, +address varchar(45) NOT NULL, +deleted_at timestamp default NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + type UserDetail struct { + gmeta.Meta `orm:"table:with_unscoped_user_detail"` + UserID int `json:"user_id"` + Address string `json:"address"` + DeletedAt *gtime.Time `json:"deleted_at"` + } + + // For Test Only + type UserEmbedded struct { + ID int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:with_unscoped_user"` + UserEmbedded + UserDetail *UserDetail `orm:"with:user_id=id"` + } + type UserWithDeletedDetail struct { + gmeta.Meta `orm:"table:with_unscoped_user"` + UserEmbedded + UserDetail *UserDetail `orm:"with:user_id=id, unscoped:true"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "user_id": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + // Delete detail where i = 3 + if i == 3 { + _, err = db.Delete(ctx, tableUserDetail, g.Map{ + "user_id": i, + }) + } + gtest.AssertNil(err) + } + + gtest.C(t, func(t *gtest.T) { + var user0 User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user0) + t.AssertNil(err) + t.Assert(user0.ID, 4) + t.AssertNE(user0.UserDetail, nil) + t.AssertNil(user0.UserDetail.DeletedAt) + t.Assert(user0.UserDetail.UserID, 4) + t.Assert(user0.UserDetail.Address, `address_4`) + + var user1 User + err = db.Model(tableUser).WithAll().Where("id", 3).Scan(&user1) + t.AssertNil(err) + t.Assert(user1.ID, 3) + t.AssertNil(user1.UserDetail) + + var user2 UserWithDeletedDetail + err = db.Model(tableUser).WithAll().Where("id", 3).Scan(&user2) + t.AssertNil(err) + t.Assert(user2.ID, 3) + t.AssertNE(user2.UserDetail, nil) + t.AssertNE(user2.UserDetail.DeletedAt, nil) + t.Assert(user2.UserDetail.UserID, 3) + t.Assert(user2.UserDetail.Address, `address_3`) + + // Unscoped outside test + var user3 User + err = db.Model(tableUser).Unscoped().WithAll().Where("id", 3).Scan(&user3) + t.AssertNil(err) + t.Assert(user3.ID, 3) + t.AssertNil(user3.UserDetail) + }) +} + +func Test_Table_Relation_WithAll_Order(t *testing.T) { + var ( + tableUser = "with_order_user" + tableUserDetail = "with_order_user_detail" + ) + // Drop tables first to ensure clean state + dropTable(tableUser) + dropTable(tableUserDetail) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +id SERIAL PRIMARY KEY, +name varchar(45) NOT NULL +); + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( +user_id SERIAL PRIMARY KEY, +address varchar(45) NOT NULL, +deleted_at timestamp default NULL +); + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + type UserDetail struct { + gmeta.Meta `orm:"table:with_order_user_detail"` + UserID int `json:"user_id"` + Address string `json:"address"` + DeletedAt *gtime.Time `json:"deleted_at"` + } + + // For Test Only + type UserEmbedded struct { + ID int `json:"id"` + Name string `json:"name"` + } + + type User struct { + gmeta.Meta `orm:"table:with_order_user"` + UserEmbedded + UserDetail *UserDetail `orm:"with:user_id=id"` + } + type UserWithDeletedDetail struct { + gmeta.Meta `orm:"table:with_order_user"` + UserEmbedded + UserDetail *UserDetail `orm:"with:user_id=id, order:user_id asc,address desc, unscoped:true"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(ctx, tableUser, g.Map{ + "id": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.AssertNil(err) + // Detail. + _, err = db.Insert(ctx, tableUserDetail, g.Map{ + "user_id": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + // Delete detail where i = 3 + if i == 3 { + _, err = db.Delete(ctx, tableUserDetail, g.Map{ + "user_id": i, + }) + } + gtest.AssertNil(err) + } + + gtest.C(t, func(t *gtest.T) { + var user0 User + err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user0) + t.AssertNil(err) + t.Assert(user0.ID, 4) + t.AssertNE(user0.UserDetail, nil) + t.AssertNil(user0.UserDetail.DeletedAt) + t.Assert(user0.UserDetail.UserID, 4) + t.Assert(user0.UserDetail.Address, `address_4`) + + var user1 User + err = db.Model(tableUser).WithAll().Where("id", 3).Scan(&user1) + t.AssertNil(err) + t.Assert(user1.ID, 3) + t.AssertNil(user1.UserDetail) + + var user2 UserWithDeletedDetail + err = db.Model(tableUser).WithAll().Where("id", 3).Scan(&user2) + t.AssertNil(err) + t.Assert(user2.ID, 3) + t.AssertNE(user2.UserDetail, nil) + t.AssertNE(user2.UserDetail.DeletedAt, nil) + t.Assert(user2.UserDetail.UserID, 3) + t.Assert(user2.UserDetail.Address, `address_3`) + + // Unscoped outside test + var user3 User + err = db.Model(tableUser).Unscoped().WithAll().Where("id", 3).Scan(&user3) + t.AssertNil(err) + t.Assert(user3.ID, 3) + t.AssertNil(user3.UserDetail) + }) +} diff --git a/contrib/drivers/gaussdb/testdata/with_multiple_depends.sql b/contrib/drivers/gaussdb/testdata/with_multiple_depends.sql new file mode 100644 index 000000000..15d0af814 --- /dev/null +++ b/contrib/drivers/gaussdb/testdata/with_multiple_depends.sql @@ -0,0 +1,30 @@ + +CREATE TABLE table_a ( + id SERIAL PRIMARY KEY, + alias varchar(255) DEFAULT '' +); + +INSERT INTO table_a VALUES (1, 'table_a_test1'); +INSERT INTO table_a VALUES (2, 'table_a_test2'); + +CREATE TABLE table_b ( + id SERIAL PRIMARY KEY, + table_a_id integer NOT NULL, + alias varchar(255) DEFAULT '' +); + +INSERT INTO table_b VALUES (10, 1, 'table_b_test1'); +INSERT INTO table_b VALUES (20, 2, 'table_b_test2'); +INSERT INTO table_b VALUES (30, 1, 'table_b_test3'); +INSERT INTO table_b VALUES (40, 2, 'table_b_test4'); + +CREATE TABLE table_c ( + id SERIAL PRIMARY KEY, + table_b_id integer NOT NULL, + alias varchar(255) DEFAULT '' +); + +INSERT INTO table_c VALUES (100, 10, 'table_c_test1'); +INSERT INTO table_c VALUES (200, 10, 'table_c_test2'); +INSERT INTO table_c VALUES (300, 20, 'table_c_test3'); +INSERT INTO table_c VALUES (400, 30, 'table_c_test4'); diff --git a/contrib/drivers/gaussdb/testdata/with_tpl_user.sql b/contrib/drivers/gaussdb/testdata/with_tpl_user.sql new file mode 100644 index 000000000..8cf3c9376 --- /dev/null +++ b/contrib/drivers/gaussdb/testdata/with_tpl_user.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS %s ( + id SERIAL PRIMARY KEY, + name varchar(45) NOT NULL +); diff --git a/contrib/drivers/gaussdb/testdata/with_tpl_user_detail.sql b/contrib/drivers/gaussdb/testdata/with_tpl_user_detail.sql new file mode 100644 index 000000000..b14e7850c --- /dev/null +++ b/contrib/drivers/gaussdb/testdata/with_tpl_user_detail.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS %s ( + uid SERIAL PRIMARY KEY, + address varchar(45) NOT NULL +); diff --git a/contrib/drivers/gaussdb/testdata/with_tpl_user_scores.sql b/contrib/drivers/gaussdb/testdata/with_tpl_user_scores.sql new file mode 100644 index 000000000..4584f79b0 --- /dev/null +++ b/contrib/drivers/gaussdb/testdata/with_tpl_user_scores.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS %s ( + id SERIAL PRIMARY KEY, + uid integer NOT NULL, + score integer NOT NULL +);