From 81273949b9d977c264749feb8bd3529f0c0f9ea8 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Wed, 23 Dec 2020 17:00:29 +0000 Subject: [PATCH 1/3] Ensure that schema search path is set with every connection on postgres 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 --- models/models.go | 21 +++++------ models/sql_postgres_with_schema.go | 60 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 models/sql_postgres_with_schema.go diff --git a/models/models.go b/models/models.go index 6aaa26d6279d..f12a4e8b304c 100644 --- a/models/models.go +++ b/models/models.go @@ -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 index 000000000000..15edcdbad7a8 --- /dev/null +++ b/models/sql_postgres_with_schema.go @@ -0,0 +1,60 @@ +// 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 + } + + 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 + schemaValue, _ := driver.String.ConvertValue(setting.Database.Schema) + + _, err = stmt.Exec([]driver.Value{schemaValue}) + if err != nil { + _ = conn.Close() + return nil, err + } + + return conn, err +} From 208782414e6aaf9a9dcbe3e6e0206c4c7ba85896 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Wed, 23 Dec 2020 17:19:14 +0000 Subject: [PATCH 2/3] no golangci-lint that is not a helpful suggestion Signed-off-by: Andrew Thornton --- models/sql_postgres_with_schema.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/sql_postgres_with_schema.go b/models/sql_postgres_with_schema.go index 15edcdbad7a8..fea125608396 100644 --- a/models/sql_postgres_with_schema.go +++ b/models/sql_postgres_with_schema.go @@ -50,7 +50,8 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) { // driver.String.ConvertValue will never return err for string schemaValue, _ := driver.String.ConvertValue(setting.Database.Schema) - _, err = stmt.Exec([]driver.Value{schemaValue}) + // 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 From c157747be3057bd0e030d5bf86d82e2ef2a644db Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Thu, 24 Dec 2020 10:56:45 +0000 Subject: [PATCH 3/3] Use Execer if available Signed-off-by: Andrew Thornton --- models/sql_postgres_with_schema.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/models/sql_postgres_with_schema.go b/models/sql_postgres_with_schema.go index fea125608396..0c8893ea275c 100644 --- a/models/sql_postgres_with_schema.go +++ b/models/sql_postgres_with_schema.go @@ -36,6 +36,21 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) { 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', @@ -48,7 +63,6 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) { defer stmt.Close() // driver.String.ConvertValue will never return err for string - schemaValue, _ := driver.String.ConvertValue(setting.Database.Schema) // golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint @@ -57,5 +71,5 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) { return nil, err } - return conn, err + return conn, nil }