]> source.dussan.org Git - gitea.git/commitdiff
Ensure that schema search path is set with every connection on postgres (#14131)...
authorzeripath <art27@cantab.net>
Sat, 2 Jan 2021 17:58:53 +0000 (17:58 +0000)
committerGitHub <noreply@github.com>
Sat, 2 Jan 2021 17:58:53 +0000 (18:58 +0100)
Backport #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 #14088

Signed-off-by: Andrew Thornton <art27@cantab.net>
models/models.go
models/sql_postgres_with_schema.go [new file with mode: 0644]

index 6aaa26d6279dd6fa4b14f84dd80ba46c22bcde8e..f12a4e8b304caaf9c2ab2786428df36ddfa6da41 100644 (file)
@@ -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
        }
@@ -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
 }
 
diff --git a/models/sql_postgres_with_schema.go b/models/sql_postgres_with_schema.go
new file mode 100644 (file)
index 0000000..0c8893e
--- /dev/null
@@ -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
+}