From 3e73e2d2cc0b2b8439b1cd890e8878de57f6be58 Mon Sep 17 00:00:00 2001 From: Jack Ling <34231795+lingcoder@users.noreply.github.com> Date: Thu, 15 Jan 2026 10:25:40 +0800 Subject: [PATCH] fix(database/gdb): skip field filtering when table/alias is unknown in FieldsPrefix (#4602) ## Summary - Fix FieldsPrefix silently dropping fields when called before LeftJoin - When table/alias is unknown, skip filtering and return fields directly ## Test plan - [x] Added unit test Test_Issue4595 in pgsql driver - [x] Test covers: FieldsPrefix before LeftJoin, Fields with prefix, FieldsPrefix after LeftJoin Closes #4595 --- .../drivers/pgsql/pgsql_z_unit_issue_test.go | 91 +++++++++++++++++++ database/gdb/gdb_model_utility.go | 5 + 2 files changed, 96 insertions(+) diff --git a/contrib/drivers/pgsql/pgsql_z_unit_issue_test.go b/contrib/drivers/pgsql/pgsql_z_unit_issue_test.go index 6fd5817fa..b56ae911a 100644 --- a/contrib/drivers/pgsql/pgsql_z_unit_issue_test.go +++ b/contrib/drivers/pgsql/pgsql_z_unit_issue_test.go @@ -205,3 +205,94 @@ func Test_Issue4033(t *testing.T) { t.AssertNil(err) }) } + +// https://github.com/gogf/gf/issues/4595 +// FieldsPrefix silently drops fields when using table alias before LeftJoin. +func Test_Issue4595(t *testing.T) { + var ( + tableUser = fmt.Sprintf(`%s_%d`, TablePrefix+"issue4595_user", gtime.TimestampNano()) + tableUserDetail = fmt.Sprintf(`%s_%d`, TablePrefix+"issue4595_user_detail", gtime.TimestampNano()) + ) + + // Create user table + if _, err := db.Exec(ctx, fmt.Sprintf(` + CREATE TABLE %s ( + id bigserial PRIMARY KEY, + name varchar(100), + email varchar(100) + );`, tableUser, + )); err != nil { + gtest.Fatal(err) + } + defer dropTable(tableUser) + + // Create user_detail table + if _, err := db.Exec(ctx, fmt.Sprintf(` + CREATE TABLE %s ( + id bigserial PRIMARY KEY, + user_id bigint, + phone varchar(20), + address varchar(200) + );`, tableUserDetail, + )); err != nil { + gtest.Fatal(err) + } + defer dropTable(tableUserDetail) + + // Insert test data + if _, err := db.Exec(ctx, fmt.Sprintf(` + INSERT INTO %s (id, name, email) VALUES (1, 'john', 'john@example.com'); + INSERT INTO %s (id, user_id, phone, address) VALUES (1, 1, '1234567890', '123 Main St'); + `, tableUser, tableUserDetail)); err != nil { + gtest.Fatal(err) + } + + gtest.C(t, func(t *gtest.T) { + // Test case 1: FieldsPrefix called before LeftJoin + // Both t1 and t2 fields should be present + r, err := db.Model(tableUser).As("t1"). + FieldsPrefix("t2", "phone", "address"). + FieldsPrefix("t1", "id", "name", "email"). + LeftJoin(tableUserDetail, "t2", "t1.id=t2.user_id"). + All() + + t.AssertNil(err) + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 1) + t.Assert(r[0]["name"], "john") + t.Assert(r[0]["email"], "john@example.com") + t.Assert(r[0]["phone"], "1234567890") + t.Assert(r[0]["address"], "123 Main St") + }) + + gtest.C(t, func(t *gtest.T) { + // Test case 2: Using Fields() with prefix + r, err := db.Model(tableUser).As("t1"). + Fields("t2.phone", "t2.address", "t1.id", "t1.name", "t1.email"). + LeftJoin(tableUserDetail, "t2", "t1.id=t2.user_id"). + All() + t.AssertNil(err) + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 1) + t.Assert(r[0]["name"], "john") + t.Assert(r[0]["email"], "john@example.com") + t.Assert(r[0]["phone"], "1234567890") + t.Assert(r[0]["address"], "123 Main St") + }) + + gtest.C(t, func(t *gtest.T) { + // Test case 3: FieldsPrefix called after LeftJoin + r, err := db.Model(tableUser).As("t1"). + LeftJoin(tableUserDetail, "t2", "t1.id=t2.user_id"). + FieldsPrefix("t2", "phone", "address"). + FieldsPrefix("t1", "id", "name", "email"). + All() + t.AssertNil(err) + t.Assert(len(r), 1) + t.Assert(r[0]["id"], 1) + t.Assert(r[0]["name"], "john") + t.Assert(r[0]["email"], "john@example.com") + t.Assert(r[0]["phone"], "1234567890") + t.Assert(r[0]["address"], "123 Main St") + }) +} diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 4da7dc69e..1ced41c39 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -68,6 +68,11 @@ func (m *Model) mappingAndFilterToTableFields(table string, fields []any, filter if fieldsTable != "" { hasTable, _ := m.db.GetCore().HasTable(fieldsTable) if !hasTable { + if fieldsTable != m.tablesInit { + // Table/alias unknown (e.g., FieldsPrefix called before LeftJoin), skip filtering. + return fields + } + // HasTable cache miss for main table, fallback to use main table for field mapping. fieldsTable = m.tablesInit } }