Skip to content

Commit ad1b6d4

Browse files
guillep2ksapklunny
committed
Add support for database schema in PostgreSQL (#8819)
* Add support for database schema * Require setting search_path for the db user * Add schema setting to admin/config.tmpl * Use a schema different from default for psql tests * Update postgres scripts to use custom schema * Update to xorm/core 0.7.3 and xorm/xorm c37aff9b3a * Fix migration test Co-authored-by: Antoine GIRARD <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent 6d6f1d5 commit ad1b6d4

28 files changed

+177
-407
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ TEST_PGSQL_HOST ?= pgsql:5432
7979
TEST_PGSQL_DBNAME ?= testgitea
8080
TEST_PGSQL_USERNAME ?= postgres
8181
TEST_PGSQL_PASSWORD ?= postgres
82+
TEST_PGSQL_SCHEMA ?= gtestschema
8283
TEST_MSSQL_HOST ?= mssql:1433
8384
TEST_MSSQL_DBNAME ?= gitea
8485
TEST_MSSQL_USERNAME ?= sa
@@ -306,6 +307,7 @@ generate-ini-pgsql:
306307
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
307308
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
308309
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
310+
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
309311
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
310312

311313
.PHONY: test-pgsql

custom/conf/app.ini.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ NAME = gitea
336336
USER = root
337337
; Use PASSWD = `your password` for quoting if you use special characters in the password.
338338
PASSWD =
339+
; For Postgres, schema to use if different from "public". The schema must exist beforehand,
340+
; the user must have creation privileges on it, and the user search path must be set
341+
; to the look into the schema first. e.g.:ALTER USER user SET SEARCH_PATH = schema_name,"$user",public;
342+
SCHEMA =
339343
; For Postgres, either "disable" (default), "require", or "verify-full"
340344
; For MySQL, either "false" (default), "true", or "skip-verify"
341345
SSL_MODE = disable

docs/content/doc/advanced/config-cheat-sheet.en-us.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
209209
- `NAME`: **gitea**: Database name.
210210
- `USER`: **root**: Database username.
211211
- `PASSWD`: **\<empty\>**: Database user password. Use \`your password\` for quoting if you use special characters in the password.
212+
- `SCHEMA`: **\<empty\>**: For PostgreSQL only, schema to use if different from "public". The schema must exist beforehand,
213+
the user must have creation privileges on it, and the user search path must be set to the look into the schema first
214+
(e.g. `ALTER USER user SET SEARCH_PATH = schema_name,"$user",public;`).
212215
- `SSL_MODE`: **disable**: For PostgreSQL and MySQL only.
213216
- `CHARSET`: **utf8**: For MySQL only, either "utf8" or "utf8mb4", default is "utf8". NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
214217
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,6 @@ require (
112112
mvdan.cc/xurls/v2 v2.1.0
113113
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
114114
xorm.io/builder v0.3.6
115-
xorm.io/core v0.7.2
116-
xorm.io/xorm v0.8.1
115+
xorm.io/core v0.7.3
116+
xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a
117117
)

go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,9 @@ xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
760760
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
761761
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
762762
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
763+
xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
764+
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
763765
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
764766
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
765-
xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
766-
xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
767+
xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a h1:hzGd080rlkZ5a7v6Tr3x8PJJnWPfKxGMMl92c8DNcww=
768+
xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=

integrations/integration_test.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,53 @@ func initIntegrationTest() {
153153
if err != nil {
154154
log.Fatalf("sql.Open: %v", err)
155155
}
156-
rows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name))
156+
dbrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name))
157157
if err != nil {
158158
log.Fatalf("db.Query: %v", err)
159159
}
160-
defer rows.Close()
160+
defer dbrows.Close()
161161

162-
if rows.Next() {
162+
if !dbrows.Next() {
163+
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
164+
log.Fatalf("db.Exec: CREATE DATABASE: %v", err)
165+
}
166+
}
167+
// Check if we need to setup a specific schema
168+
if len(setting.Database.Schema) == 0 {
163169
break
164170
}
165-
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
166-
log.Fatalf("db.Exec: %v", err)
171+
db.Close()
172+
173+
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
174+
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
175+
// This is a different db object; requires a different Close()
176+
defer db.Close()
177+
if err != nil {
178+
log.Fatalf("sql.Open: %v", err)
179+
}
180+
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
181+
if err != nil {
182+
log.Fatalf("db.Query: %v", err)
183+
}
184+
defer schrows.Close()
185+
186+
if !schrows.Next() {
187+
// Create and setup a DB schema
188+
if _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema)); err != nil {
189+
log.Fatalf("db.Exec: CREATE SCHEMA: %v", err)
190+
}
191+
}
192+
193+
// Make the user's default search path the created schema; this will affect new connections
194+
if _, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema)); err != nil {
195+
log.Fatalf("db.Exec: ALTER USER SET search_path: %v", err)
196+
}
197+
198+
// Make the current connection's search the created schema
199+
if _, err = db.Exec(fmt.Sprintf(`SET search_path = %s`, setting.Database.Schema)); err != nil {
200+
log.Fatalf("db.Exec: ALTER USER SET search_path: %v", err)
167201
}
202+
168203
case setting.Database.UseMSSQL:
169204
host, port := setting.ParseMSSQLHostPort(setting.Database.Host)
170205
db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",
Binary file not shown.
Binary file not shown.
Binary file not shown.

integrations/migration-test/migration_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,32 @@ func restoreOldDB(t *testing.T, version string) bool {
168168
assert.NoError(t, err)
169169
db.Close()
170170

171+
// Check if we need to setup a specific schema
172+
if len(setting.Database.Schema) != 0 {
173+
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
174+
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
175+
if !assert.NoError(t, err) {
176+
return false
177+
}
178+
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
179+
if !assert.NoError(t, err) || !assert.NotEmpty(t, schrows) {
180+
return false
181+
}
182+
183+
if !schrows.Next() {
184+
// Create and setup a DB schema
185+
_, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
186+
assert.NoError(t, err)
187+
}
188+
schrows.Close()
189+
190+
// Make the user's default search path the created schema; this will affect new connections
191+
_, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema))
192+
assert.NoError(t, err)
193+
194+
db.Close()
195+
}
196+
171197
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
172198
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
173199
assert.NoError(t, err)

0 commit comments

Comments
 (0)