Skip to content

Commit 1b30949

Browse files
committed
fix(database/gdb,pgsql,gaussdb,dm,clickhouse,cmd/gf): add schema/search_path support for Tables/TableFields and cache isolation
1 parent 7321170 commit 1b30949

19 files changed

Lines changed: 1388 additions & 73 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ node_modules
2525
output
2626
.example/
2727
.golangci.bck.yml
28-
*.exe
28+
*.exe
29+
*.out

cmd/gf/internal/cmd/cmd_z_init_test.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import (
1515
)
1616

1717
var (
18-
ctx = context.Background()
19-
testDB gdb.DB
20-
testPgDB gdb.DB
21-
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?loc=Local&parseTime=true"
22-
linkPg = "pgsql:postgres:12345678@tcp(127.0.0.1:5432)/test"
18+
ctx = context.Background()
19+
testDB gdb.DB
20+
testPgDB gdb.DB
21+
testGaussDB gdb.DB
22+
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?loc=Local&parseTime=true"
23+
linkPg = "pgsql:postgres:12345678@tcp(127.0.0.1:5432)/test"
24+
linkGaussDB = "gaussdb:gaussdb:UTpass@1234@tcp(127.0.0.1:9950)/postgres"
2325
)
2426

2527
func init() {
@@ -34,6 +36,10 @@ func init() {
3436
testPgDB, _ = gdb.New(gdb.ConfigNode{
3537
Link: linkPg,
3638
})
39+
// GaussDB connection (optional, may not be available in all environments)
40+
testGaussDB, _ = gdb.New(gdb.ConfigNode{
41+
Link: linkGaussDB,
42+
})
3743
}
3844

3945
func dropTableWithDb(db gdb.DB, table string) {

cmd/gf/internal/cmd/cmd_z_unit_gen_dao_issue_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,3 +855,179 @@ func Test_Gen_Dao_Issue4629_TablesPattern_PgSql(t *testing.T) {
855855
t.Assert(gfile.Exists(gfile.Join(path, "dao", "user_log.go")), false)
856856
})
857857
}
858+
859+
// https://github.com/gogf/gf/issues/4495
860+
// Test that gen dao works correctly with PostgreSQL non-public schemas
861+
// using search_path in connection string.
862+
func Test_Gen_Dao_Issue4495_PgSchema(t *testing.T) {
863+
if testPgDB == nil {
864+
t.Skip("PostgreSQL database not available, skipping test")
865+
return
866+
}
867+
gtest.C(t, func(t *gtest.T) {
868+
var (
869+
err error
870+
db = testPgDB
871+
schema = "test_gendao_schema"
872+
table1 = "schema_user"
873+
table2 = "schema_order"
874+
linkPgSP = fmt.Sprintf("pgsql:postgres:12345678@tcp(127.0.0.1:5432)/test?search_path=%s,public", schema)
875+
)
876+
877+
// Create schema
878+
if _, err = db.Exec(ctx, fmt.Sprintf(`CREATE SCHEMA IF NOT EXISTS %s`, schema)); err != nil {
879+
t.Fatal(err)
880+
}
881+
defer db.Exec(ctx, fmt.Sprintf(`DROP SCHEMA %s CASCADE`, schema))
882+
883+
// Create tables in the schema (not in public)
884+
if _, err = db.Exec(ctx, fmt.Sprintf(`
885+
CREATE TABLE %s.%s (
886+
id bigserial PRIMARY KEY,
887+
name varchar(100)
888+
)`, schema, table1)); err != nil {
889+
t.Fatal(err)
890+
}
891+
892+
if _, err = db.Exec(ctx, fmt.Sprintf(`
893+
CREATE TABLE %s.%s (
894+
id bigserial PRIMARY KEY,
895+
user_id bigint,
896+
amount decimal(10,2)
897+
)`, schema, table2)); err != nil {
898+
t.Fatal(err)
899+
}
900+
901+
var (
902+
path = gfile.Temp(guid.S())
903+
group = "test"
904+
in = gendao.CGenDaoInput{
905+
Path: path,
906+
Link: linkPgSP, // Use connection string with search_path
907+
Group: group,
908+
Tables: fmt.Sprintf("%s,%s", table1, table2), // Specify exact tables
909+
}
910+
)
911+
err = gutil.FillStructWithDefault(&in)
912+
t.AssertNil(err)
913+
914+
err = gfile.Mkdir(path)
915+
t.AssertNil(err)
916+
917+
pwd := gfile.Pwd()
918+
err = gfile.Chdir(path)
919+
t.AssertNil(err)
920+
defer gfile.Chdir(pwd)
921+
defer gfile.RemoveAll(path)
922+
923+
_, err = gendao.CGenDao{}.Dao(ctx, in)
924+
t.AssertNil(err)
925+
926+
// Should generate 2 dao files from the schema
927+
generatedFiles, err := gfile.ScanDir(gfile.Join(path, "dao"), "*.go", false)
928+
t.AssertNil(err)
929+
t.Assert(len(generatedFiles), 2)
930+
931+
// Verify the correct files are generated
932+
t.Assert(gfile.Exists(gfile.Join(path, "dao", "schema_user.go")), true)
933+
t.Assert(gfile.Exists(gfile.Join(path, "dao", "schema_order.go")), true)
934+
935+
// Verify entity files contain correct field types
936+
entityUserContent := gfile.GetContents(gfile.Join(path, "model", "entity", "schema_user.go"))
937+
t.Assert(gstr.Contains(entityUserContent, "Id"), true)
938+
t.Assert(gstr.Contains(entityUserContent, "Name"), true)
939+
940+
entityOrderContent := gfile.GetContents(gfile.Join(path, "model", "entity", "schema_order.go"))
941+
t.Assert(gstr.Contains(entityOrderContent, "Id"), true)
942+
t.Assert(gstr.Contains(entityOrderContent, "UserId"), true)
943+
t.Assert(gstr.Contains(entityOrderContent, "Amount"), true)
944+
})
945+
}
946+
947+
// https://github.com/gogf/gf/issues/4495
948+
// Test that gen dao works correctly with GaussDB non-public schemas
949+
// using search_path in connection string.
950+
func Test_Gen_Dao_Issue4495_GaussDBSchema(t *testing.T) {
951+
if testGaussDB == nil {
952+
t.Skip("GaussDB database not available, skipping test")
953+
return
954+
}
955+
gtest.C(t, func(t *gtest.T) {
956+
var (
957+
err error
958+
db = testGaussDB
959+
schema = "test_gendao_schema"
960+
table1 = "schema_user"
961+
table2 = "schema_order"
962+
linkGaussDBSP = fmt.Sprintf("gaussdb:gaussdb:UTpass@1234@tcp(127.0.0.1:9950)/postgres?search_path=%s,public", schema)
963+
)
964+
965+
// Create schema
966+
if _, err = db.Exec(ctx, fmt.Sprintf(`CREATE SCHEMA IF NOT EXISTS %s`, schema)); err != nil {
967+
t.Fatal(err)
968+
}
969+
defer db.Exec(ctx, fmt.Sprintf(`DROP SCHEMA %s CASCADE`, schema))
970+
971+
// Create tables in the schema (not in public)
972+
if _, err = db.Exec(ctx, fmt.Sprintf(`
973+
CREATE TABLE %s.%s (
974+
id bigserial PRIMARY KEY,
975+
name varchar(100)
976+
)`, schema, table1)); err != nil {
977+
t.Fatal(err)
978+
}
979+
980+
if _, err = db.Exec(ctx, fmt.Sprintf(`
981+
CREATE TABLE %s.%s (
982+
id bigserial PRIMARY KEY,
983+
user_id bigint,
984+
amount decimal(10,2)
985+
)`, schema, table2)); err != nil {
986+
t.Fatal(err)
987+
}
988+
989+
var (
990+
path = gfile.Temp(guid.S())
991+
group = "test"
992+
in = gendao.CGenDaoInput{
993+
Path: path,
994+
Link: linkGaussDBSP, // Use connection string with search_path
995+
Group: group,
996+
Tables: fmt.Sprintf("%s,%s", table1, table2), // Specify exact tables
997+
}
998+
)
999+
err = gutil.FillStructWithDefault(&in)
1000+
t.AssertNil(err)
1001+
1002+
err = gfile.Mkdir(path)
1003+
t.AssertNil(err)
1004+
1005+
pwd := gfile.Pwd()
1006+
err = gfile.Chdir(path)
1007+
t.AssertNil(err)
1008+
defer gfile.Chdir(pwd)
1009+
defer gfile.RemoveAll(path)
1010+
1011+
_, err = gendao.CGenDao{}.Dao(ctx, in)
1012+
t.AssertNil(err)
1013+
1014+
// Should generate 2 dao files from the schema
1015+
generatedFiles, err := gfile.ScanDir(gfile.Join(path, "dao"), "*.go", false)
1016+
t.AssertNil(err)
1017+
t.Assert(len(generatedFiles), 2)
1018+
1019+
// Verify the correct files are generated
1020+
t.Assert(gfile.Exists(gfile.Join(path, "dao", "schema_user.go")), true)
1021+
t.Assert(gfile.Exists(gfile.Join(path, "dao", "schema_order.go")), true)
1022+
1023+
// Verify entity files contain correct field types
1024+
entityUserContent := gfile.GetContents(gfile.Join(path, "model", "entity", "schema_user.go"))
1025+
t.Assert(gstr.Contains(entityUserContent, "Id"), true)
1026+
t.Assert(gstr.Contains(entityUserContent, "Name"), true)
1027+
1028+
entityOrderContent := gfile.GetContents(gfile.Join(path, "model", "entity", "schema_order.go"))
1029+
t.Assert(gstr.Contains(entityOrderContent, "Id"), true)
1030+
t.Assert(gstr.Contains(entityOrderContent, "UserId"), true)
1031+
t.Assert(gstr.Contains(entityOrderContent, "Amount"), true)
1032+
})
1033+
}

contrib/drivers/clickhouse/clickhouse_table_fields.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
3131
return nil, err
3232
}
3333
var (
34+
// Filter by database to ensure consistency with Tables() method.
35+
// Use d.GetConfig().Name as database name, same as Tables().
3436
getColumnsSql = fmt.Sprintf(
35-
"select %s from `system`.columns c where `table` = '%s'",
36-
tableFieldsColumns, table,
37+
"select %s from `system`.columns c where `database` = '%s' and `table` = '%s'",
38+
tableFieldsColumns, d.GetConfig().Name, table,
3739
)
3840
)
3941
result, err = d.DoSelect(ctx, link, getColumnsSql)

contrib/drivers/clickhouse/clickhouse_z_unit_db_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,87 @@ func Test_DB_TableFields(t *testing.T) {
332332
gtest.AssertNQ(field, nil)
333333
})
334334
}
335+
336+
// https://github.com/gogf/gf/issues/4495
337+
// Test TableFields() correctly filters by database name.
338+
func Test_Issue4495_TableFields_DatabaseFilter(t *testing.T) {
339+
table := createInitTable("issue4495_test")
340+
defer dropTable(table)
341+
342+
gtest.C(t, func(t *gtest.T) {
343+
// TableFields should return fields only for tables in the configured database
344+
fields, err := db.TableFields(ctx, "issue4495_test")
345+
gtest.AssertNil(err)
346+
gtest.AssertEQ(len(fields), 5)
347+
348+
// Verify field names
349+
_, hasId := fields["id"]
350+
_, hasPassport := fields["passport"]
351+
_, hasPassword := fields["password"]
352+
_, hasNickname := fields["nickname"]
353+
_, hasCreateTime := fields["create_time"]
354+
gtest.AssertEQ(hasId, true)
355+
gtest.AssertEQ(hasPassport, true)
356+
gtest.AssertEQ(hasPassword, true)
357+
gtest.AssertEQ(hasNickname, true)
358+
gtest.AssertEQ(hasCreateTime, true)
359+
})
360+
}
361+
362+
// https://github.com/gogf/gf/issues/4495
363+
// Test Tables() returns tables from the configured database.
364+
func Test_Issue4495_Tables_DatabaseFilter(t *testing.T) {
365+
table1 := createTable("issue4495_t1")
366+
table2 := createTable("issue4495_t2")
367+
defer dropTable(table1)
368+
defer dropTable(table2)
369+
370+
gtest.C(t, func(t *gtest.T) {
371+
tables, err := db.Tables(ctx)
372+
gtest.AssertNil(err)
373+
374+
// Should contain our created tables
375+
found1 := false
376+
found2 := false
377+
for _, tbl := range tables {
378+
if tbl == "issue4495_t1" {
379+
found1 = true
380+
}
381+
if tbl == "issue4495_t2" {
382+
found2 = true
383+
}
384+
}
385+
gtest.AssertEQ(found1, true)
386+
gtest.AssertEQ(found2, true)
387+
})
388+
}
389+
390+
// https://github.com/gogf/gf/issues/4495
391+
// Test cache isolation - ensure cache keys include database name.
392+
func Test_Issue4495_CacheIsolation(t *testing.T) {
393+
table := createInitTable("issue4495_cache")
394+
defer dropTable(table)
395+
396+
gtest.C(t, func(t *gtest.T) {
397+
// First call should cache the result
398+
tables1, err := db.Tables(ctx)
399+
gtest.AssertNil(err)
400+
401+
// Second call should use cache (same database)
402+
tables2, err := db.Tables(ctx)
403+
gtest.AssertNil(err)
404+
405+
// Results should be consistent
406+
gtest.AssertEQ(len(tables1), len(tables2))
407+
408+
// Both should contain our table
409+
found := false
410+
for _, tbl := range tables1 {
411+
if tbl == "issue4495_cache" {
412+
found = true
413+
break
414+
}
415+
}
416+
gtest.AssertEQ(found, true)
417+
})
418+
}

contrib/drivers/dm/dm_table_fields.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ func (d *Driver) TableFields(
4343
if link, err = d.SlaveLink(usedSchema); err != nil {
4444
return nil, err
4545
}
46-
// The link has been distinguished and no longer needs to judge the owner
46+
// Use usedSchema for OWNER filter to be consistent with Tables()
4747
result, err = d.DoSelect(
4848
ctx, link,
4949
fmt.Sprintf(
5050
tableFieldsSqlTmp,
5151
escapeSingleQuote(strings.ToUpper(table)),
52-
escapeSingleQuote(strings.ToUpper(d.GetSchema())),
52+
escapeSingleQuote(strings.ToUpper(usedSchema)),
5353
),
5454
)
5555
if err != nil {
@@ -66,7 +66,7 @@ func (d *Driver) TableFields(
6666
if pkResult.IsEmpty() {
6767
pkResult, err = d.DoSelect(
6868
ctx, link,
69-
fmt.Sprintf(tableFieldsPkSqlDBATmp, escapeSingleQuote(strings.ToUpper(table)), escapeSingleQuote(strings.ToUpper(d.GetSchema()))),
69+
fmt.Sprintf(tableFieldsPkSqlDBATmp, escapeSingleQuote(strings.ToUpper(table)), escapeSingleQuote(strings.ToUpper(usedSchema))),
7070
)
7171
if err != nil {
7272
return nil, err

0 commit comments

Comments
 (0)