From 54087de518cb44346c54863fed96cbb0fb430a02 Mon Sep 17 00:00:00 2001 From: Jack Ling <34231795+lingcoder@users.noreply.github.com> Date: Wed, 11 Feb 2026 13:51:47 +0800 Subject: [PATCH] test(contrib/drivers/pgsql): add Builder/Subquery/Join/Struct tests (#4680) ## Summary - Port MySQL test coverage for Builder, Subquery, Join, and Struct features to PgSQL driver - Add 4 new test files with 23 test functions covering builder patterns, subquery WHERE/HAVING/Model, all JOIN types, and struct scanning - PgSQL dialect adaptations: double-quoted identifiers, GROUP BY with HAVING, letter-prefixed table names, int64 id assertions, removed `Uid` field ## Test plan - [x] Builder tests pass: `go test -v -run "Test_Model_Builder|Test_Safe_Builder" -count=1` - [x] Subquery tests pass: `go test -v -run "Test_Model_SubQuery" -count=1` - [x] Join tests pass: `go test -v -run "Test_Model_.*Join.*|Test_Model_FieldsPrefix" -count=1` - [x] Struct tests pass: `go test -v -run "Test_Model_Embedded|Test_Struct|Test_Structs|Test_Model_Scan|Test_Scan_Auto" -count=1` - [x] Full PgSQL test suite: 113/113 PASS ref #4689 --- ...pgsql_z_unit_feature_model_builder_test.go | 141 ++++++ .../pgsql_z_unit_feature_model_join_test.go | 177 +++++++ .../pgsql_z_unit_feature_model_struct_test.go | 477 ++++++++++++++++++ ...gsql_z_unit_feature_model_subquery_test.go | 66 +++ 4 files changed, 861 insertions(+) create mode 100644 contrib/drivers/pgsql/pgsql_z_unit_feature_model_builder_test.go create mode 100644 contrib/drivers/pgsql/pgsql_z_unit_feature_model_join_test.go create mode 100644 contrib/drivers/pgsql/pgsql_z_unit_feature_model_struct_test.go create mode 100644 contrib/drivers/pgsql/pgsql_z_unit_feature_model_subquery_test.go diff --git a/contrib/drivers/pgsql/pgsql_z_unit_feature_model_builder_test.go b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_builder_test.go new file mode 100644 index 000000000..fbf5929f9 --- /dev/null +++ b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_builder_test.go @@ -0,0 +1,141 @@ +// 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 pgsql_test + +import ( + "testing" + + "github.com/gogf/gf/v2/encoding/gjson" + "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/gmeta" +) + +func Test_Model_Builder(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + all, err := m.Where( + b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}), + ).All() + t.AssertNil(err) + t.Assert(len(all), 6) + }) + + // Where And + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + all, err := m.Where( + b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}), + ).Where( + b.Where("id", g.Slice{2, 3}).WhereOr("id", g.Slice{5, 6}), + ).Where( + b.Where("id", g.Slice{3}).Where("id", g.Slice{1, 2, 3}), + ).All() + t.AssertNil(err) + t.Assert(len(all), 1) + }) + + // Where Or + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + all, err := m.WhereOr( + b.Where("id", g.Slice{1, 2, 3}).WhereOr("id", g.Slice{4, 5, 6}), + ).WhereOr( + b.Where("id", g.Slice{2, 3}).WhereOr("id", g.Slice{5, 6}), + ).WhereOr( + b.Where("id", g.Slice{3}).Where("id", g.Slice{1, 2, 3}), + ).All() + t.AssertNil(err) + t.Assert(len(all), 6) + }) + + // Where with struct which has a field type of *gtime.Time + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + type Query struct { + Id any + Nickname *gtime.Time + } + + where, args := b.Where(&Query{Id: 1}).Build() + t.Assert(where, `"id"=? AND "nickname" IS NULL`) + t.Assert(args, []any{1}) + }) + + // Where with struct which has a field type of *gjson.Json + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + type Query struct { + Id any + Nickname *gjson.Json + } + + where, args := b.Where(&Query{Id: 1}).Build() + t.Assert(where, `"id"=? AND "nickname" IS NULL`) + t.Assert(args, []any{1}) + }) + + // Where with do struct which has a field type of *gtime.Time and generated by gf cli + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + type Query struct { + gmeta.Meta `orm:"do:true"` + Id any + Nickname *gtime.Time + } + + where, args := b.Where(&Query{Id: 1}).Build() + t.Assert(where, `"id"=?`) + t.Assert(args, []any{1}) + }) + + // Where with do struct which has a field type of *gjson.Json and generated by gf cli + gtest.C(t, func(t *gtest.T) { + m := db.Model(table) + b := m.Builder() + + type Query struct { + gmeta.Meta `orm:"do:true"` + Id any + Nickname *gjson.Json + } + + where, args := b.Where(&Query{Id: 1}).Build() + t.Assert(where, `"id"=?`) + t.Assert(args, []any{1}) + }) +} + +func Test_Safe_Builder(t *testing.T) { + // test whether m.Builder() is chain safe + gtest.C(t, func(t *gtest.T) { + b := db.Model().Builder() + b.Where("id", 1) + _, args := b.Build() + t.AssertNil(args) + + b = b.Where("id", 1) + _, args = b.Build() + t.Assert(args, g.Slice{1}) + }) +} diff --git a/contrib/drivers/pgsql/pgsql_z_unit_feature_model_join_test.go b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_join_test.go new file mode 100644 index 000000000..ce7d6e845 --- /dev/null +++ b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_join_test.go @@ -0,0 +1,177 @@ +// 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 pgsql_test + +import ( + "testing" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_Model_LeftJoinOnField(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnField(table2, "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 2) + }) +} + +func Test_Model_RightJoinOnField(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + RightJoinOnField(table2, "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 2) + }) +} + +func Test_Model_InnerJoinOnField(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + InnerJoinOnField(table2, "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 2) + }) +} + +func Test_Model_LeftJoinOnFields(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnFields(table2, "id", "=", "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 2) + }) +} + +func Test_Model_RightJoinOnFields(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + RightJoinOnFields(table2, "id", "=", "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 2) + }) +} + +func Test_Model_InnerJoinOnFields(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + InnerJoinOnFields(table2, "id", "=", "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 2) + }) +} + +func Test_Model_FieldsPrefix(t *testing.T) { + var ( + table1 = "t_" + gtime.TimestampNanoStr() + "_table1" + table2 = "t_" + gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "id"). + FieldsPrefix(table2, "nickname"). + LeftJoinOnField(table2, "id"). + WhereIn("id", g.Slice{1, 2}). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 2) + t.Assert(r[0]["id"], 1) + t.Assert(r[0]["nickname"], "name_1") + }) +} diff --git a/contrib/drivers/pgsql/pgsql_z_unit_feature_model_struct_test.go b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_struct_test.go new file mode 100644 index 000000000..f2d2bc1fc --- /dev/null +++ b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_struct_test.go @@ -0,0 +1,477 @@ +// 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 pgsql_test + +import ( + "database/sql" + "reflect" + "testing" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "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_Model_Embedded_Insert(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + type Base struct { + Id int `json:"id"` + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + result, err := db.Model(table).Data(User{ + Passport: "john-test", + Password: "123456", + Nickname: "John", + Base: Base{ + Id: 100, + CreateTime: gtime.Now().String(), + }, + }).Insert() + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + value, err := db.Model(table).Fields("passport").Where("id=100").Value() + t.AssertNil(err) + t.Assert(value.String(), "john-test") + }) +} + +func Test_Model_Embedded_MapToStruct(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + type Ids struct { + Id int `json:"id"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id": 100, + "passport": "t1", + "password": "123456", + "nickname": "T1", + "create_time": gtime.Now().String(), + } + result, err := db.Model(table).Data(data).Insert() + t.AssertNil(err) + n, _ := result.RowsAffected() + t.Assert(n, 1) + + one, err := db.Model(table).Where("id=100").One() + t.AssertNil(err) + + user := new(User) + + t.Assert(one.Struct(user), nil) + t.Assert(user.Id, data["id"]) + t.Assert(user.Passport, data["passport"]) + t.Assert(user.Password, data["password"]) + t.Assert(user.Nickname, data["nickname"]) + t.Assert(user.CreateTime, data["create_time"]) + }) +} + +func Test_Struct_Pointer_Attribute(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + type User struct { + Id *int + Passport *string + Password *string + Nickname string + } + + gtest.C(t, func(t *gtest.T) { + one, err := db.Model(table).WherePri(1).One() + t.AssertNil(err) + user := new(User) + err = one.Struct(user) + t.AssertNil(err) + t.Assert(*user.Id, 1) + t.Assert(*user.Passport, "user_1") + t.Assert(*user.Password, "pass_1") + t.Assert(user.Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + user := new(User) + err := db.Model(table).Scan(user, "id=1") + t.AssertNil(err) + t.Assert(*user.Id, 1) + t.Assert(*user.Passport, "user_1") + t.Assert(*user.Password, "pass_1") + t.Assert(user.Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(table).Scan(&user, "id=1") + t.AssertNil(err) + t.Assert(*user.Id, 1) + t.Assert(*user.Passport, "user_1") + t.Assert(*user.Password, "pass_1") + t.Assert(user.Nickname, "name_1") + }) +} + +func Test_Structs_Pointer_Attribute(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + type User struct { + Id *int + Passport *string + Password *string + Nickname string + } + // All + gtest.C(t, func(t *gtest.T) { + one, err := db.Model(table).All("id < 3") + t.AssertNil(err) + users := make([]User, 0) + err = one.Structs(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + one, err := db.Model(table).All("id < 3") + t.AssertNil(err) + users := make([]*User, 0) + err = one.Structs(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + var users []User + one, err := db.Model(table).All("id < 3") + t.AssertNil(err) + err = one.Structs(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + var users []*User + one, err := db.Model(table).All("id < 3") + t.AssertNil(err) + err = one.Structs(&users) + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + // Structs + gtest.C(t, func(t *gtest.T) { + users := make([]User, 0) + err := db.Model(table).Scan(&users, "id < 3") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + users := make([]*User, 0) + err := db.Model(table).Scan(&users, "id < 3") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.Model(table).Scan(&users, "id < 3") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(table).Scan(&users, "id < 3") + t.AssertNil(err) + t.Assert(len(users), 2) + t.Assert(*users[0].Id, 1) + t.Assert(*users[0].Passport, "user_1") + t.Assert(*users[0].Password, "pass_1") + t.Assert(users[0].Nickname, "name_1") + }) +} + +func Test_Struct_Empty(t *testing.T) { + table := createTable() + defer dropTable(table) + + type User struct { + Id int + Passport string + Password string + Nickname string + } + + gtest.C(t, func(t *gtest.T) { + user := new(User) + err := db.Model(table).Where("id=100").Scan(user) + t.Assert(err, sql.ErrNoRows) + t.AssertNE(user, nil) + }) + + gtest.C(t, func(t *gtest.T) { + one, err := db.Model(table).Where("id=100").One() + t.AssertNil(err) + var user *User + t.Assert(one.Struct(&user), nil) + t.Assert(user, nil) + }) + + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(table).Where("id=100").Scan(&user) + t.AssertNil(err) + t.Assert(user, nil) + }) +} + +func Test_Structs_Empty(t *testing.T) { + table := createTable() + defer dropTable(table) + + type User struct { + Id int + Passport string + Password string + Nickname string + } + + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Where("id>100").All() + t.AssertNil(err) + users := make([]User, 0) + t.Assert(all.Structs(&users), nil) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Where("id>100").All() + t.AssertNil(err) + users := make([]User, 10) + t.Assert(all.Structs(&users), sql.ErrNoRows) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Where("id>100").All() + t.AssertNil(err) + var users []User + t.Assert(all.Structs(&users), nil) + }) + + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Where("id>100").All() + t.AssertNil(err) + users := make([]*User, 0) + t.Assert(all.Structs(&users), nil) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Where("id>100").All() + t.AssertNil(err) + users := make([]*User, 10) + t.Assert(all.Structs(&users), sql.ErrNoRows) + }) + gtest.C(t, func(t *gtest.T) { + all, err := db.Model(table).Where("id>100").All() + t.AssertNil(err) + var users []*User + t.Assert(all.Structs(&users), nil) + }) +} + +type MyTime struct { + gtime.Time +} + +type MyTimeSt struct { + CreateTime MyTime +} + +func (st *MyTimeSt) UnmarshalValue(v any) error { + m := gconv.Map(v) + t, err := gtime.StrToTime(gconv.String(m["create_time"])) + if err != nil { + return err + } + st.CreateTime = MyTime{*t} + return nil +} + +func Test_Model_Scan_CustomType_Time(t *testing.T) { + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + st := new(MyTimeSt) + err := db.Model(table).Fields("create_time").Scan(st) + t.AssertNil(err) + t.Assert(st.CreateTime.String(), "2018-10-24 10:00:00") + }) + gtest.C(t, func(t *gtest.T) { + var stSlice []*MyTimeSt + err := db.Model(table).Fields("create_time").Scan(&stSlice) + t.AssertNil(err) + t.Assert(len(stSlice), TableSize) + t.Assert(stSlice[0].CreateTime.String(), "2018-10-24 10:00:00") + t.Assert(stSlice[9].CreateTime.String(), "2018-10-24 10:00:00") + }) +} + +func Test_Model_Scan_CustomType_String(t *testing.T) { + type MyString string + + type MyStringSt struct { + Passport MyString + } + + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + st := new(MyStringSt) + err := db.Model(table).Fields("Passport").WherePri(1).Scan(st) + t.AssertNil(err) + t.Assert(st.Passport, "user_1") + }) + gtest.C(t, func(t *gtest.T) { + var sts []MyStringSt + err := db.Model(table).Fields("Passport").Order("id asc").Scan(&sts) + t.AssertNil(err) + t.Assert(len(sts), TableSize) + t.Assert(sts[0].Passport, "user_1") + }) +} + +type User struct { + Id int + Passport string + Password string + Nickname string + CreateTime *gtime.Time +} + +func (user *User) UnmarshalValue(value any) error { + if record, ok := value.(gdb.Record); ok { + *user = User{ + Id: record["id"].Int(), + Passport: record["passport"].String(), + Password: "", + Nickname: record["nickname"].String(), + CreateTime: record["create_time"].GTime(), + } + return nil + } + return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value)) +} + +func Test_Model_Scan_UnmarshalValue(t *testing.T) { + table := createInitTable() + defer dropTable(table) + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(table).Order("id asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), TableSize) + t.Assert(users[0].Id, 1) + t.Assert(users[0].Passport, "user_1") + t.Assert(users[0].Password, "") + t.Assert(users[0].Nickname, "name_1") + t.Assert(users[0].CreateTime.String(), CreateTime) + + t.Assert(users[9].Id, 10) + t.Assert(users[9].Passport, "user_10") + t.Assert(users[9].Password, "") + t.Assert(users[9].Nickname, "name_10") + t.Assert(users[9].CreateTime.String(), CreateTime) + }) +} + +func Test_Model_Scan_Map(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + var users []*User + err := db.Model(table).Order("id asc").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), TableSize) + t.Assert(users[0].Id, 1) + t.Assert(users[0].Passport, "user_1") + t.Assert(users[0].Password, "") + t.Assert(users[0].Nickname, "name_1") + t.Assert(users[0].CreateTime.String(), CreateTime) + + t.Assert(users[9].Id, 10) + t.Assert(users[9].Passport, "user_10") + t.Assert(users[9].Password, "") + t.Assert(users[9].Nickname, "name_10") + t.Assert(users[9].CreateTime.String(), CreateTime) + }) +} + +func Test_Scan_AutoFilteringByStructAttributes(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + type User struct { + Id int + Passport string + } + gtest.C(t, func(t *gtest.T) { + var user *User + err := db.Model(table).OrderAsc("id").Scan(&user) + t.AssertNil(err) + t.Assert(user.Id, 1) + }) + gtest.C(t, func(t *gtest.T) { + var users []User + err := db.Model(table).OrderAsc("id").Scan(&users) + t.AssertNil(err) + t.Assert(len(users), TableSize) + t.Assert(users[0].Id, 1) + }) +} diff --git a/contrib/drivers/pgsql/pgsql_z_unit_feature_model_subquery_test.go b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_subquery_test.go new file mode 100644 index 000000000..31c790c33 --- /dev/null +++ b/contrib/drivers/pgsql/pgsql_z_unit_feature_model_subquery_test.go @@ -0,0 +1,66 @@ +// 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 pgsql_test + +import ( + "testing" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" +) + +func Test_Model_SubQuery_Where(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).Where( + "id in ?", + db.Model(table).Fields("id").Where("id", g.Slice{1, 3, 5}), + ).OrderAsc("id").All() + t.AssertNil(err) + + t.Assert(len(r), 3) + t.Assert(r[0]["id"], 1) + t.Assert(r[1]["id"], 3) + t.Assert(r[2]["id"], 5) + }) +} + +func Test_Model_SubQuery_Having(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table).Where( + "id in ?", + db.Model(table).Fields("id").Where("id", g.Slice{1, 3, 5}), + ).Group("id").Having( + "id > ?", + db.Model(table).Fields("MAX(id)").Where("id", g.Slice{1, 3}), + ).OrderAsc("id").All() + t.AssertNil(err) + + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 5) + }) +} + +func Test_Model_SubQuery_Model(t *testing.T) { + table := createInitTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + subQuery1 := db.Model(table).Where("id", g.Slice{1, 3, 5}) + subQuery2 := db.Model(table).Where("id", g.Slice{5, 7, 9}) + r, err := db.Model("? AS a, ? AS b", subQuery1, subQuery2).Fields("a.id").Where("a.id=b.id").OrderAsc("id").All() + t.AssertNil(err) + + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 5) + }) +}