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)

integrations/pgsql.ini.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ HOST = {{TEST_PGSQL_HOST}}
77
NAME = {{TEST_PGSQL_DBNAME}}
88
USER = {{TEST_PGSQL_USERNAME}}
99
PASSWD = {{TEST_PGSQL_PASSWORD}}
10+
SCHEMA = {{TEST_PGSQL_SCHEMA}}
1011
SSL_MODE = disable
1112

1213
[indexer]

models/models.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,12 @@ func getEngine() (*xorm.Engine, error) {
128128
return nil, err
129129
}
130130

131-
return xorm.NewEngine(setting.Database.Type, connStr)
131+
engine, err := xorm.NewEngine(setting.Database.Type, connStr)
132+
if err != nil {
133+
return nil, err
134+
}
135+
engine.SetSchema(setting.Database.Schema)
136+
return engine, nil
132137
}
133138

134139
// NewTestEngine sets a new test xorm.Engine

modules/auth/user_form.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type InstallForm struct {
2525
SSLMode string
2626
Charset string `binding:"Required;In(utf8,utf8mb4)"`
2727
DbPath string
28+
DbSchema string
2829

2930
AppName string `binding:"Required" locale:"install.app_name"`
3031
RepoRootPath string `binding:"Required"`

modules/setting/database.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var (
3030
Name string
3131
User string
3232
Passwd string
33+
Schema string
3334
SSLMode string
3435
Path string
3536
LogSQL bool
@@ -75,6 +76,7 @@ func InitDBConfig() {
7576
if len(Database.Passwd) == 0 {
7677
Database.Passwd = sec.Key("PASSWD").String()
7778
}
79+
Database.Schema = sec.Key("SCHEMA").String()
7880
Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")
7981
Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"})
8082
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))

options/locale/locale_en-US.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ user = Username
102102
password = Password
103103
db_name = Database Name
104104
db_helper = Note to MySQL users: please use the InnoDB storage engine and if you use "utf8mb4", your InnoDB version must be greater than 5.6 .
105+
db_schema = Schema
106+
db_schema_helper = Leave blank for database default ("public").
105107
ssl_mode = SSL
106108
charset = Charset
107109
path = Path
@@ -1953,6 +1955,7 @@ config.db_type = Type
19531955
config.db_host = Host
19541956
config.db_name = Name
19551957
config.db_user = Username
1958+
config.db_schema = Schema
19561959
config.db_ssl_mode = SSL
19571960
config.db_path = Path
19581961

routers/install.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func Install(ctx *context.Context) {
5454
form.DbPasswd = setting.Database.Passwd
5555
form.DbName = setting.Database.Name
5656
form.DbPath = setting.Database.Path
57+
form.DbSchema = setting.Database.Schema
5758
form.Charset = setting.Database.Charset
5859

5960
ctx.Data["CurDbOption"] = "MySQL"
@@ -147,6 +148,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
147148
setting.Database.User = form.DbUser
148149
setting.Database.Passwd = form.DbPasswd
149150
setting.Database.Name = form.DbName
151+
setting.Database.Schema = form.DbSchema
150152
setting.Database.SSLMode = form.SSLMode
151153
setting.Database.Charset = form.Charset
152154
setting.Database.Path = form.DbPath
@@ -267,6 +269,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
267269
cfg.Section("database").Key("NAME").SetValue(setting.Database.Name)
268270
cfg.Section("database").Key("USER").SetValue(setting.Database.User)
269271
cfg.Section("database").Key("PASSWD").SetValue(setting.Database.Passwd)
272+
cfg.Section("database").Key("SCHEMA").SetValue(setting.Database.Schema)
270273
cfg.Section("database").Key("SSL_MODE").SetValue(setting.Database.SSLMode)
271274
cfg.Section("database").Key("CHARSET").SetValue(setting.Database.Charset)
272275
cfg.Section("database").Key("PATH").SetValue(setting.Database.Path)

templates/admin/config.tmpl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@
128128
<dd>{{if .DbCfg.User}}{{.DbCfg.User}}{{else}}-{{end}}</dd>
129129
{{end}}
130130
{{if eq .DbCfg.Type "postgres"}}
131+
<dt>{{.i18n.Tr "admin.config.db_schema"}}</dt>
132+
<dd>{{if .DbCfg.Schema}}{{.DbCfg.Schema}}{{else}}-{{end}}</dd>
131133
<dt>{{.i18n.Tr "admin.config.db_ssl_mode"}}</dt>
132134
<dd>{{if .DbCfg.SSLMode}}{{.DbCfg.SSLMode}}{{else}}-{{end}}</dd>
133135
{{end}}

templates/install.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@
6262
</div>
6363
</div>
6464
</div>
65+
<div class="inline field {{if .Err_DbSetting}}error{{end}}">
66+
<label for="db_schema">{{.i18n.Tr "install.db_schema"}}</label>
67+
<input id="db_schema" name="db_schema" value="{{.db_schema}}">
68+
<span class="help">{{.i18n.Tr "install.db_schema_helper"}}</span>
69+
</div>
6570
</div>
6671

6772
<div id="mysql_settings" class="{{if not (eq .CurDbOption "MySQL")}}hide{{end}}">

vendor/modules.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ mvdan.cc/xurls/v2
613613
strk.kbt.io/projects/go/libravatar
614614
# xorm.io/builder v0.3.6
615615
xorm.io/builder
616-
# xorm.io/core v0.7.2
616+
# xorm.io/core v0.7.3
617617
xorm.io/core
618-
# xorm.io/xorm v0.8.1
618+
# xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a
619619
xorm.io/xorm

vendor/xorm.io/core/.drone.yml

Lines changed: 0 additions & 120 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)