I made a further comment on issue #1280, where an error occurs if you try and specify the Postgres schema to use. i.e. normally to create a table would would specify:
CREATE TABLE schema_name.table_name ( ... )
If you specify the table name in this form, GORM escapes the table name into:
"schema_name"."table_name"
This causes an error.
e.g. I specified the table as:
func (u *User) TableName() string {
// custom table name, this is default
return "schema_1.users"
}
The only way I could get something working was to modify the GORM code as follows. There may be side effects with this approach, and I have not tested all scenarios yet. Also, ideally, all of the db-specific (i.e. "dialect") code should be in the dialect_postgres.go file, which I have not done. In a proper solution, the function to escape table names, column names, etc. should be in the dialect file.
The changes are:
scope.go:
// QuotedTableName return quoted table name
// DEF modified to not add quotes to escape table name if dialect is postgres
func (scope *Scope) QuotedTableName() (name string) {
oldStr := scope.TableName()
aDialect := scope.Dialect().GetName()
if aDialect == "postgres" {
return oldStr
}
if scope.Search != nil && len(scope.Search.tableName) > 0 {
if strings.Index(scope.Search.tableName, " ") != -1 {
return scope.Search.tableName
}
if aDialect == "postgres" {
return scope.Search.tableName
}
newStr := scope.Quote(scope.Search.tableName)
return newStr
}
if aDialect == "postgres" {
return oldStr
}
newStr := scope.Quote( scope.TableName() )
return newStr
//return scope.Quote(scope.TableName())
}
I also made the following changes to dialect_postgres.go:
// DEF: had to modify this to separate table_schema from table_name
func (s postgres) HasIndex(tableName string, indexName string) bool {
var count int
parts := strings.Split( tableName, "." )
s.db.QueryRow("SELECT count(*) FROM pg_indexes WHERE schemaname = $1 AND tablename = $2 AND indexname = $3", parts[0], parts[1], indexName).Scan(&count)
return count > 0
}
// DEF: may need to modify this to separate table_schema from table_name -- tbd
func (s postgres) HasForeignKey(tableName string, foreignKeyName string) bool {
var count int
s.db.QueryRow("SELECT count(con.conname) FROM pg_constraint con WHERE $1::regclass::oid = con.conrelid AND con.conname = $2 AND con.contype='f'", tableName, foreignKeyName).Scan(&count)
return count > 0
}
// DEF: had to modify this to separate table_schema from table_name
func (s postgres) HasTable(tableName string) bool {
parts := strings.Split( tableName, "." )
//fmt.Println("Schema: ", parts[0], " Table: ", parts[1])
var count int
s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.tables WHERE table_schema = $1 AND table_name = $2 AND table_type = 'BASE TABLE'", parts[0], parts[1]).Scan(&count)
return count > 0
}
// DEF: had to modify this to separate table_schema from table_name
func (s postgres) HasColumn(tableName string, columnName string) bool {
var count int
parts := strings.Split( tableName, "." )
s.db.QueryRow("SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_schema = $1 AND table_name = $2 AND column_name = $3", parts[0], parts[1], columnName).Scan(&count)
return count > 0
}
There may be other changes needed as well, which I will post as I discover them. Can we get official code support for Postgres schemas?
Comment From: dfarago
On further thought, its probably better to select the schema in Postgres by using a raw sql execute:
e.g.
db.Exec("SET search_path TO testdb1")
Thereafter, all operations are directed to the selected schema.
Comment From: jinzhu
Not going to support . for postgres, as it will lose some features that GORM currently support.
Comment From: dvaldivia
@dfarago I'm seeing a problem where goroutines are loosing the search_path, have you experience this?
Comment From: kirugan
@dvaldivia this is because SET search_path TO ... affects just one connection to database, but sql.DB has a pool of connections under the hood.
Comment From: kirugan
Or you can solve it with this dirty hack:
db = db.Begin()
db.Exec("set search_path to my_schema")
defer db.Rollback()
// some useful work here
Comment From: generatordog
func (Block) TableName() string {
return "\"schema.tablename\""
}
Just for anyone encounter this issue, the above method works with dot, gorm.io/gorm v1.25.2