Skip to content

Commit

Permalink
Ensure that schema search path is set with every connection on postgr…
Browse files Browse the repository at this point in the history
…es (go-gitea#14131)

Backport go-gitea#14131

Unfortunately every connection to postgres requires that the search path is
set appropriately.

This PR shadows the postgres driver to ensure that as soon as a connection
is open, the search_path is set appropriately.

Fix go-gitea#14088

Signed-off-by: Andrew Thornton <art27@cantab.net>
  • Loading branch information
zeripath committed Jan 2, 2021
1 parent 5568dd6 commit b8b72f1
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 11 deletions.
21 changes: 10 additions & 11 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,16 @@ func getEngine() (*xorm.Engine, error) {
return nil, err
}

engine, err := xorm.NewEngine(setting.Database.Type, connStr)
var engine *xorm.Engine

if setting.Database.UsePostgreSQL && len(setting.Database.Schema) > 0 {
// OK whilst we sort out our schema issues - create a schema aware postgres
registerPostgresSchemaDriver()
engine, err = xorm.NewEngine("postgresschema", connStr)
} else {
engine, err = xorm.NewEngine(setting.Database.Type, connStr)
}

if err != nil {
return nil, err
}
Expand All @@ -155,16 +164,6 @@ func getEngine() (*xorm.Engine, error) {
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
}
engine.SetSchema(setting.Database.Schema)
if setting.Database.UsePostgreSQL && len(setting.Database.Schema) > 0 {
// Add the schema to the search path
if _, err := engine.Exec(`SELECT set_config(
'search_path',
? || ',' || current_setting('search_path'),
false)`,
setting.Database.Schema); err != nil {
return nil, err
}
}
return engine, nil
}

Expand Down
75 changes: 75 additions & 0 deletions models/sql_postgres_with_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
"database/sql"
"database/sql/driver"
"sync"

"code.gitea.io/gitea/modules/setting"

"github.com/lib/pq"
"xorm.io/xorm/dialects"
)

var registerOnce sync.Once

func registerPostgresSchemaDriver() {
registerOnce.Do(func() {
sql.Register("postgresschema", &postgresSchemaDriver{})
dialects.RegisterDriver("postgresschema", dialects.QueryDriver("postgres"))
})
}

type postgresSchemaDriver struct {
pq.Driver
}

// Open opens a new connection to the database. name is a connection string.
// This function opens the postgres connection in the default manner but immediately
// runs set_config to set the search_path appropriately
func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) {
conn, err := d.Driver.Open(name)
if err != nil {
return conn, err
}
schemaValue, _ := driver.String.ConvertValue(setting.Database.Schema)

// golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here
// and in any case pq does not implement it
if execer, ok := conn.(driver.Execer); ok { //nolint
_, err := execer.Exec(`SELECT set_config(
'search_path',
$1 || ',' || current_setting('search_path'),
false)`, []driver.Value{schemaValue}) //nolint
if err != nil {
_ = conn.Close()
return nil, err
}
return conn, nil
}

stmt, err := conn.Prepare(`SELECT set_config(
'search_path',
$1 || ',' || current_setting('search_path'),
false)`)
if err != nil {
_ = conn.Close()
return nil, err
}
defer stmt.Close()

// driver.String.ConvertValue will never return err for string

// golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here
_, err = stmt.Exec([]driver.Value{schemaValue}) //nolint
if err != nil {
_ = conn.Close()
return nil, err
}

return conn, nil
}

0 comments on commit b8b72f1

Please sign in to comment.