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 <sapk@users.noreply.github.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
guillep2k 2020-01-20 12:45:14 -03:00 committed by Antoine GIRARD
parent 6d6f1d568e
commit ad1b6d439f
28 changed files with 177 additions and 407 deletions

View file

@ -79,6 +79,7 @@ TEST_PGSQL_HOST ?= pgsql:5432
TEST_PGSQL_DBNAME ?= testgitea TEST_PGSQL_DBNAME ?= testgitea
TEST_PGSQL_USERNAME ?= postgres TEST_PGSQL_USERNAME ?= postgres
TEST_PGSQL_PASSWORD ?= postgres TEST_PGSQL_PASSWORD ?= postgres
TEST_PGSQL_SCHEMA ?= gtestschema
TEST_MSSQL_HOST ?= mssql:1433 TEST_MSSQL_HOST ?= mssql:1433
TEST_MSSQL_DBNAME ?= gitea TEST_MSSQL_DBNAME ?= gitea
TEST_MSSQL_USERNAME ?= sa TEST_MSSQL_USERNAME ?= sa
@ -306,6 +307,7 @@ generate-ini-pgsql:
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \ -e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \ -e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \ -e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
integrations/pgsql.ini.tmpl > integrations/pgsql.ini integrations/pgsql.ini.tmpl > integrations/pgsql.ini
.PHONY: test-pgsql .PHONY: test-pgsql

View file

@ -336,6 +336,10 @@ NAME = gitea
USER = root USER = root
; Use PASSWD = `your password` for quoting if you use special characters in the password. ; Use PASSWD = `your password` for quoting if you use special characters in the password.
PASSWD = PASSWD =
; For Postgres, schema to use if different from "public". The schema must exist beforehand,
; the user must have creation privileges on it, and the user search path must be set
; to the look into the schema first. e.g.:ALTER USER user SET SEARCH_PATH = schema_name,"$user",public;
SCHEMA =
; For Postgres, either "disable" (default), "require", or "verify-full" ; For Postgres, either "disable" (default), "require", or "verify-full"
; For MySQL, either "false" (default), "true", or "skip-verify" ; For MySQL, either "false" (default), "true", or "skip-verify"
SSL_MODE = disable SSL_MODE = disable

View file

@ -209,6 +209,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `NAME`: **gitea**: Database name. - `NAME`: **gitea**: Database name.
- `USER`: **root**: Database username. - `USER`: **root**: Database username.
- `PASSWD`: **\<empty\>**: Database user password. Use \`your password\` for quoting if you use special characters in the password. - `PASSWD`: **\<empty\>**: Database user password. Use \`your password\` for quoting if you use special characters in the password.
- `SCHEMA`: **\<empty\>**: For PostgreSQL only, schema to use if different from "public". The schema must exist beforehand,
the user must have creation privileges on it, and the user search path must be set to the look into the schema first
(e.g. `ALTER USER user SET SEARCH_PATH = schema_name,"$user",public;`).
- `SSL_MODE`: **disable**: For PostgreSQL and MySQL only. - `SSL_MODE`: **disable**: For PostgreSQL and MySQL only.
- `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. - `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.
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path. - `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.

4
go.mod
View file

@ -112,6 +112,6 @@ require (
mvdan.cc/xurls/v2 v2.1.0 mvdan.cc/xurls/v2 v2.1.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.6 xorm.io/builder v0.3.6
xorm.io/core v0.7.2 xorm.io/core v0.7.3
xorm.io/xorm v0.8.1 xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a
) )

6
go.sum
View file

@ -760,7 +760,9 @@ xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU= xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw= xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM= xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY= xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ= xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a h1:hzGd080rlkZ5a7v6Tr3x8PJJnWPfKxGMMl92c8DNcww=
xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY= xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=

View file

@ -153,18 +153,53 @@ func initIntegrationTest() {
if err != nil { if err != nil {
log.Fatalf("sql.Open: %v", err) log.Fatalf("sql.Open: %v", err)
} }
rows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name)) dbrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name))
if err != nil { if err != nil {
log.Fatalf("db.Query: %v", err) log.Fatalf("db.Query: %v", err)
} }
defer rows.Close() defer dbrows.Close()
if rows.Next() { if !dbrows.Next() {
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
log.Fatalf("db.Exec: CREATE DATABASE: %v", err)
}
}
// Check if we need to setup a specific schema
if len(setting.Database.Schema) == 0 {
break break
} }
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil { db.Close()
log.Fatalf("db.Exec: %v", err)
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
// This is a different db object; requires a different Close()
defer db.Close()
if err != nil {
log.Fatalf("sql.Open: %v", err)
} }
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
if err != nil {
log.Fatalf("db.Query: %v", err)
}
defer schrows.Close()
if !schrows.Next() {
// Create and setup a DB schema
if _, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema)); err != nil {
log.Fatalf("db.Exec: CREATE SCHEMA: %v", err)
}
}
// Make the user's default search path the created schema; this will affect new connections
if _, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema)); err != nil {
log.Fatalf("db.Exec: ALTER USER SET search_path: %v", err)
}
// Make the current connection's search the created schema
if _, err = db.Exec(fmt.Sprintf(`SET search_path = %s`, setting.Database.Schema)); err != nil {
log.Fatalf("db.Exec: ALTER USER SET search_path: %v", err)
}
case setting.Database.UseMSSQL: case setting.Database.UseMSSQL:
host, port := setting.ParseMSSQLHostPort(setting.Database.Host) host, port := setting.ParseMSSQLHostPort(setting.Database.Host)
db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;",

View file

@ -168,6 +168,32 @@ func restoreOldDB(t *testing.T, version string) bool {
assert.NoError(t, err) assert.NoError(t, err)
db.Close() db.Close()
// Check if we need to setup a specific schema
if len(setting.Database.Schema) != 0 {
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
if !assert.NoError(t, err) {
return false
}
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
if !assert.NoError(t, err) || !assert.NotEmpty(t, schrows) {
return false
}
if !schrows.Next() {
// Create and setup a DB schema
_, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s", setting.Database.Schema))
assert.NoError(t, err)
}
schrows.Close()
// Make the user's default search path the created schema; this will affect new connections
_, err = db.Exec(fmt.Sprintf(`ALTER USER "%s" SET search_path = %s`, setting.Database.User, setting.Database.Schema))
assert.NoError(t, err)
db.Close()
}
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode)) setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
assert.NoError(t, err) assert.NoError(t, err)

View file

@ -7,6 +7,7 @@ HOST = {{TEST_PGSQL_HOST}}
NAME = {{TEST_PGSQL_DBNAME}} NAME = {{TEST_PGSQL_DBNAME}}
USER = {{TEST_PGSQL_USERNAME}} USER = {{TEST_PGSQL_USERNAME}}
PASSWD = {{TEST_PGSQL_PASSWORD}} PASSWD = {{TEST_PGSQL_PASSWORD}}
SCHEMA = {{TEST_PGSQL_SCHEMA}}
SSL_MODE = disable SSL_MODE = disable
[indexer] [indexer]

View file

@ -128,7 +128,12 @@ func getEngine() (*xorm.Engine, error) {
return nil, err return nil, err
} }
return xorm.NewEngine(setting.Database.Type, connStr) engine, err := xorm.NewEngine(setting.Database.Type, connStr)
if err != nil {
return nil, err
}
engine.SetSchema(setting.Database.Schema)
return engine, nil
} }
// NewTestEngine sets a new test xorm.Engine // NewTestEngine sets a new test xorm.Engine

View file

@ -25,6 +25,7 @@ type InstallForm struct {
SSLMode string SSLMode string
Charset string `binding:"Required;In(utf8,utf8mb4)"` Charset string `binding:"Required;In(utf8,utf8mb4)"`
DbPath string DbPath string
DbSchema string
AppName string `binding:"Required" locale:"install.app_name"` AppName string `binding:"Required" locale:"install.app_name"`
RepoRootPath string `binding:"Required"` RepoRootPath string `binding:"Required"`

View file

@ -30,6 +30,7 @@ var (
Name string Name string
User string User string
Passwd string Passwd string
Schema string
SSLMode string SSLMode string
Path string Path string
LogSQL bool LogSQL bool
@ -75,6 +76,7 @@ func InitDBConfig() {
if len(Database.Passwd) == 0 { if len(Database.Passwd) == 0 {
Database.Passwd = sec.Key("PASSWD").String() Database.Passwd = sec.Key("PASSWD").String()
} }
Database.Schema = sec.Key("SCHEMA").String()
Database.SSLMode = sec.Key("SSL_MODE").MustString("disable") Database.SSLMode = sec.Key("SSL_MODE").MustString("disable")
Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"}) Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"})
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db")) Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))

View file

@ -102,6 +102,8 @@ user = Username
password = Password password = Password
db_name = Database Name db_name = Database Name
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 . 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 .
db_schema = Schema
db_schema_helper = Leave blank for database default ("public").
ssl_mode = SSL ssl_mode = SSL
charset = Charset charset = Charset
path = Path path = Path
@ -1953,6 +1955,7 @@ config.db_type = Type
config.db_host = Host config.db_host = Host
config.db_name = Name config.db_name = Name
config.db_user = Username config.db_user = Username
config.db_schema = Schema
config.db_ssl_mode = SSL config.db_ssl_mode = SSL
config.db_path = Path config.db_path = Path

View file

@ -54,6 +54,7 @@ func Install(ctx *context.Context) {
form.DbPasswd = setting.Database.Passwd form.DbPasswd = setting.Database.Passwd
form.DbName = setting.Database.Name form.DbName = setting.Database.Name
form.DbPath = setting.Database.Path form.DbPath = setting.Database.Path
form.DbSchema = setting.Database.Schema
form.Charset = setting.Database.Charset form.Charset = setting.Database.Charset
ctx.Data["CurDbOption"] = "MySQL" ctx.Data["CurDbOption"] = "MySQL"
@ -147,6 +148,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
setting.Database.User = form.DbUser setting.Database.User = form.DbUser
setting.Database.Passwd = form.DbPasswd setting.Database.Passwd = form.DbPasswd
setting.Database.Name = form.DbName setting.Database.Name = form.DbName
setting.Database.Schema = form.DbSchema
setting.Database.SSLMode = form.SSLMode setting.Database.SSLMode = form.SSLMode
setting.Database.Charset = form.Charset setting.Database.Charset = form.Charset
setting.Database.Path = form.DbPath setting.Database.Path = form.DbPath
@ -267,6 +269,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
cfg.Section("database").Key("NAME").SetValue(setting.Database.Name) cfg.Section("database").Key("NAME").SetValue(setting.Database.Name)
cfg.Section("database").Key("USER").SetValue(setting.Database.User) cfg.Section("database").Key("USER").SetValue(setting.Database.User)
cfg.Section("database").Key("PASSWD").SetValue(setting.Database.Passwd) cfg.Section("database").Key("PASSWD").SetValue(setting.Database.Passwd)
cfg.Section("database").Key("SCHEMA").SetValue(setting.Database.Schema)
cfg.Section("database").Key("SSL_MODE").SetValue(setting.Database.SSLMode) cfg.Section("database").Key("SSL_MODE").SetValue(setting.Database.SSLMode)
cfg.Section("database").Key("CHARSET").SetValue(setting.Database.Charset) cfg.Section("database").Key("CHARSET").SetValue(setting.Database.Charset)
cfg.Section("database").Key("PATH").SetValue(setting.Database.Path) cfg.Section("database").Key("PATH").SetValue(setting.Database.Path)

View file

@ -128,6 +128,8 @@
<dd>{{if .DbCfg.User}}{{.DbCfg.User}}{{else}}-{{end}}</dd> <dd>{{if .DbCfg.User}}{{.DbCfg.User}}{{else}}-{{end}}</dd>
{{end}} {{end}}
{{if eq .DbCfg.Type "postgres"}} {{if eq .DbCfg.Type "postgres"}}
<dt>{{.i18n.Tr "admin.config.db_schema"}}</dt>
<dd>{{if .DbCfg.Schema}}{{.DbCfg.Schema}}{{else}}-{{end}}</dd>
<dt>{{.i18n.Tr "admin.config.db_ssl_mode"}}</dt> <dt>{{.i18n.Tr "admin.config.db_ssl_mode"}}</dt>
<dd>{{if .DbCfg.SSLMode}}{{.DbCfg.SSLMode}}{{else}}-{{end}}</dd> <dd>{{if .DbCfg.SSLMode}}{{.DbCfg.SSLMode}}{{else}}-{{end}}</dd>
{{end}} {{end}}

View file

@ -62,6 +62,11 @@
</div> </div>
</div> </div>
</div> </div>
<div class="inline field {{if .Err_DbSetting}}error{{end}}">
<label for="db_schema">{{.i18n.Tr "install.db_schema"}}</label>
<input id="db_schema" name="db_schema" value="{{.db_schema}}">
<span class="help">{{.i18n.Tr "install.db_schema_helper"}}</span>
</div>
</div> </div>
<div id="mysql_settings" class="{{if not (eq .CurDbOption "MySQL")}}hide{{end}}"> <div id="mysql_settings" class="{{if not (eq .CurDbOption "MySQL")}}hide{{end}}">

4
vendor/modules.txt vendored
View file

@ -613,7 +613,7 @@ mvdan.cc/xurls/v2
strk.kbt.io/projects/go/libravatar strk.kbt.io/projects/go/libravatar
# xorm.io/builder v0.3.6 # xorm.io/builder v0.3.6
xorm.io/builder xorm.io/builder
# xorm.io/core v0.7.2 # xorm.io/core v0.7.3
xorm.io/core xorm.io/core
# xorm.io/xorm v0.8.1 # xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a
xorm.io/xorm xorm.io/xorm

120
vendor/xorm.io/core/.drone.yml generated vendored
View file

@ -1,128 +1,8 @@
---
kind: pipeline
name: go1.10
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/xorm.io/core
steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: test
pull: default
image: golang:1.10
commands:
- go get github.com/stretchr/testify/assert
- go get github.com/go-xorm/sqlfiddle
- go get github.com/go-sql-driver/mysql
- go get github.com/mattn/go-sqlite3
- go vet
- "go test -v -race -coverprofile=coverage.txt -covermode=atomic -dbConn=\"root:@tcp(mysql:3306)/core_test?charset=utf8mb4\""
when:
event:
- push
- tag
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: core_test
when:
event:
- push
- tag
- pull_request
---
kind: pipeline
name: go1.11
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/xorm.io/core
steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: test
pull: default
image: golang:1.11
commands:
- go vet
- "go test -v -race -coverprofile=coverage.txt -covermode=atomic -dbConn=\"root:@tcp(mysql:3306)/core_test?charset=utf8mb4\""
environment:
GO111MODULE: "on"
GOPROXY: https://goproxy.cn
when:
event:
- push
- tag
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: core_test
when:
event:
- push
- tag
- pull_request
--- ---
kind: pipeline kind: pipeline
name: go1.12 name: go1.12
platform:
os: linux
arch: amd64
clone:
disable: true
workspace:
base: /go
path: src/xorm.io/core
steps: steps:
- name: git
pull: default
image: plugins/git:next
settings:
depth: 50
tags: true
- name: test - name: test
pull: default pull: default

2
vendor/xorm.io/core/README.md generated vendored
View file

@ -1,7 +1,7 @@
Core is a lightweight wrapper of sql.DB. Core is a lightweight wrapper of sql.DB.
[![Build Status](https://drone.gitea.com/api/badges/xorm/core/status.svg)](https://drone.gitea.com/xorm/core) [![Build Status](https://drone.gitea.com/api/badges/xorm/core/status.svg)](https://drone.gitea.com/xorm/core)
[![](http://gocover.io/_badge/xorm.io/core)](http://gocover.io/xorm.io/core) [![Test Coverage](https://gocover.io/_badge/xorm.io/core)](https://gocover.io/xorm.io/core)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/xorm.io/core) [![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/xorm.io/core)
# Open # Open

4
vendor/xorm.io/core/column.go generated vendored
View file

@ -37,7 +37,7 @@ type Column struct {
IsDeleted bool IsDeleted bool
IsCascade bool IsCascade bool
IsVersion bool IsVersion bool
DefaultIsEmpty bool DefaultIsEmpty bool // false means column has no default set, but not default value is empty
EnumOptions map[string]int EnumOptions map[string]int
SetOptions map[string]int SetOptions map[string]int
DisableTimeZone bool DisableTimeZone bool
@ -65,7 +65,7 @@ func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable
IsDeleted: false, IsDeleted: false,
IsCascade: false, IsCascade: false,
IsVersion: false, IsVersion: false,
DefaultIsEmpty: false, DefaultIsEmpty: true, // default should be no default
EnumOptions: make(map[string]int), EnumOptions: make(map[string]int),
Comment: "", Comment: "",
} }

4
vendor/xorm.io/core/index.go generated vendored
View file

@ -26,8 +26,8 @@ type Index struct {
func (index *Index) XName(tableName string) string { func (index *Index) XName(tableName string) string {
if !strings.HasPrefix(index.Name, "UQE_") && if !strings.HasPrefix(index.Name, "UQE_") &&
!strings.HasPrefix(index.Name, "IDX_") { !strings.HasPrefix(index.Name, "IDX_") {
tableName = strings.Replace(tableName, `"`, "", -1) tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".")
tableName = strings.Replace(tableName, `.`, "_", -1) tableName = tableParts[len(tableParts)-1]
if index.Type == UniqueType { if index.Type == UniqueType {
return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) return fmt.Sprintf("UQE_%v_%v", tableName, index.Name)
} }

216
vendor/xorm.io/xorm/.drone.yml generated vendored
View file

@ -1,204 +1,14 @@
--- ---
kind: pipeline kind: pipeline
name: go1.10-test name: testing
workspace:
base: /go
path: src/gitea.com/xorm/xorm
steps: steps:
- name: build - name: test-vet
pull: default pull: default
image: golang:1.10 image: golang:1.12
commands:
- go get -t -d -v
- go build -v
when:
event:
- push
- pull_request
- name: test-sqlite
pull: default
image: golang:1.10
depends_on:
- build
commands:
- "go test -v -race -db=\"sqlite3\" -conn_str=\"./test.db\" -coverprofile=coverage1-1.txt -covermode=atomic"
- "go test -v -race -db=\"sqlite3\" -conn_str=\"./test.db\" -cache=true -coverprofile=coverage1-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mysql
pull: default
image: golang:1.10
depends_on:
- build
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -coverprofile=coverage2-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test\" -cache=true -coverprofile=coverage2-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mysql-utf8mb4
pull: default
image: golang:1.10
depends_on:
- test-mysql
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -coverprofile=coverage2.1-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(mysql)/xorm_test?charset=utf8mb4\" -cache=true -coverprofile=coverage2.1-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mymysql
pull: default
image: golang:1.10
depends_on:
- test-mysql-utf8mb4
commands:
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -coverprofile=coverage3-1.txt -covermode=atomic"
- "go test -v -race -db=\"mymysql\" -conn_str=\"tcp:mysql:3306*xorm_test/root/\" -cache=true -coverprofile=coverage3-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres
pull: default
image: golang:1.10
depends_on:
- build
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -coverprofile=coverage4-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -cache=true -coverprofile=coverage4-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-postgres-schema
pull: default
image: golang:1.10
depends_on:
- build
commands:
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -coverprofile=coverage5-1.txt -covermode=atomic"
- "go test -v -race -db=\"postgres\" -conn_str=\"postgres://postgres:@pgsql/xorm_test?sslmode=disable\" -schema=xorm -cache=true -coverprofile=coverage5-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-mssql
pull: default
image: golang:1.10
depends_on:
- build
commands:
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -coverprofile=coverage6-1.txt -covermode=atomic"
- "go test -v -race -db=\"mssql\" -conn_str=\"server=mssql;user id=sa;password=yourStrong(!)Password;database=xorm_test\" -cache=true -coverprofile=coverage6-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-tidb
pull: default
image: golang:1.10
depends_on:
- build
commands:
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -coverprofile=coverage7-1.txt -covermode=atomic"
- "go test -v -race -db=\"mysql\" -conn_str=\"root:@tcp(tidb:4000)/xorm_test\" -ignore_select_update=true -cache=true -coverprofile=coverage7-2.txt -covermode=atomic"
when:
event:
- push
- pull_request
- name: test-end
pull: default
image: golang:1.10
depends_on:
- test-sqlite
- test-mysql
- test-mysql-utf8mb4
- test-mymysql
- test-postgres
- test-postgres-schema
- test-mssql
- test-tidb
commands:
- echo "go1.10 build end"
when:
event:
- push
- pull_request
services:
- name: mysql
pull: default
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: xorm_test
when:
event:
- push
- tag
- pull_request
- name: pgsql
pull: default
image: postgres:9.5
environment:
POSTGRES_DB: xorm_test
POSTGRES_USER: postgres
when:
event:
- push
- tag
- pull_request
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
SA_PASSWORD: yourStrong(!)Password
MSSQL_PID: Developer
when:
event:
- push
- tag
- pull_request
- name: tidb
pull: default
image: pingcap/tidb:v3.0.3
when:
event:
- push
- tag
- pull_request
---
kind: pipeline
name: go1.13-test
steps:
- name: build
pull: default
image: golang:1.13
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
commands: commands:
- go build -v
- go vet - go vet
when: when:
event: event:
@ -207,7 +17,7 @@ steps:
- name: test-sqlite - name: test-sqlite
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
@ -221,7 +31,7 @@ steps:
- name: test-mysql - name: test-mysql
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
@ -235,7 +45,7 @@ steps:
- name: test-mysql-utf8mb4 - name: test-mysql-utf8mb4
pull: default pull: default
image: golang:1.13 image: golang:1.12
depends_on: depends_on:
- test-mysql - test-mysql
environment: environment:
@ -251,7 +61,7 @@ steps:
- name: test-mymysql - name: test-mymysql
pull: default pull: default
image: golang:1.13 image: golang:1.12
depends_on: depends_on:
- test-mysql-utf8mb4 - test-mysql-utf8mb4
environment: environment:
@ -267,7 +77,7 @@ steps:
- name: test-postgres - name: test-postgres
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
@ -281,7 +91,7 @@ steps:
- name: test-postgres-schema - name: test-postgres-schema
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
@ -295,7 +105,7 @@ steps:
- name: test-mssql - name: test-mssql
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
@ -309,7 +119,7 @@ steps:
- name: test-tidb - name: test-tidb
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
@ -323,12 +133,12 @@ steps:
- name: merge_coverage - name: merge_coverage
pull: default pull: default
image: golang:1.13 image: golang:1.12
environment: environment:
GO111MODULE: "on" GO111MODULE: "on"
GOPROXY: "https://goproxy.cn" GOPROXY: "https://goproxy.cn"
depends_on: depends_on:
- build - test-vet
- test-sqlite - test-sqlite
- test-mysql - test-mysql
- test-mysql-utf8mb4 - test-mysql-utf8mb4

View file

@ -901,7 +901,7 @@ func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) {
} }
func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string { func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string {
if len(db.Schema) == 0 { if len(db.Schema) == 0 || strings.Contains(tableName, ".") {
return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s", return fmt.Sprintf("alter table %s ALTER COLUMN %s TYPE %s",
tableName, col.Name, db.SqlType(col)) tableName, col.Name, db.SqlType(col))
} }
@ -913,8 +913,8 @@ func (db *postgres) DropIndexSql(tableName string, index *core.Index) string {
quote := db.Quote quote := db.Quote
idxName := index.Name idxName := index.Name
tableName = strings.Replace(tableName, `"`, "", -1) tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".")
tableName = strings.Replace(tableName, `.`, "_", -1) tableName = tableParts[len(tableParts)-1]
if !strings.HasPrefix(idxName, "UQE_") && if !strings.HasPrefix(idxName, "UQE_") &&
!strings.HasPrefix(idxName, "IDX_") { !strings.HasPrefix(idxName, "IDX_") {

View file

@ -729,66 +729,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err
args = append(args, m[colName]) args = append(args, m[colName])
} }
w := builder.NewWriter() return session.insertMap(columns, args)
if session.statement.cond.IsValid() {
if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil {
return 0, err
}
if err := writeStrings(w, append(columns, exprs.colNames...), "`", "`"); err != nil {
return 0, err
}
if _, err := w.WriteString(") SELECT "); err != nil {
return 0, err
}
if err := session.statement.writeArgs(w, args); err != nil {
return 0, err
}
if len(exprs.args) > 0 {
if _, err := w.WriteString(","); err != nil {
return 0, err
}
if err := exprs.writeArgs(w); err != nil {
return 0, err
}
}
if _, err := w.WriteString(fmt.Sprintf(" FROM %s WHERE ", session.engine.Quote(tableName))); err != nil {
return 0, err
}
if err := session.statement.cond.WriteTo(w); err != nil {
return 0, err
}
} else {
qm := strings.Repeat("?,", len(columns))
qm = qm[:len(qm)-1]
if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm)); err != nil {
return 0, err
}
w.Append(args...)
}
sql := w.String()
args = w.Args()
if err := session.cacheInsert(tableName); err != nil {
return 0, err
}
res, err := session.exec(sql, args...)
if err != nil {
return 0, err
}
affected, err := res.RowsAffected()
if err != nil {
return 0, err
}
return affected, nil
} }
func (session *Session) insertMapString(m map[string]string) (int64, error) { func (session *Session) insertMapString(m map[string]string) (int64, error) {
@ -808,6 +749,7 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) {
columns = append(columns, k) columns = append(columns, k)
} }
} }
sort.Strings(columns) sort.Strings(columns)
var args = make([]interface{}, 0, len(m)) var args = make([]interface{}, 0, len(m))
@ -815,7 +757,18 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) {
args = append(args, m[colName]) args = append(args, m[colName])
} }
return session.insertMap(columns, args)
}
func (session *Session) insertMap(columns []string, args []interface{}) (int64, error) {
tableName := session.statement.TableName()
if len(tableName) <= 0 {
return 0, ErrTableNotFound
}
exprs := session.statement.exprColumns
w := builder.NewWriter() w := builder.NewWriter()
// if insert where
if session.statement.cond.IsValid() { if session.statement.cond.IsValid() {
if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil { if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil {
return 0, err return 0, err
@ -853,10 +806,29 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) {
qm := strings.Repeat("?,", len(columns)) qm := strings.Repeat("?,", len(columns))
qm = qm[:len(qm)-1] qm = qm[:len(qm)-1]
if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (`%s`) VALUES (%s)", session.engine.Quote(tableName), strings.Join(columns, "`,`"), qm)); err != nil { if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil {
return 0, err return 0, err
} }
if err := writeStrings(w, append(columns, exprs.colNames...), "`", "`"); err != nil {
return 0, err
}
if _, err := w.WriteString(fmt.Sprintf(") VALUES (%s", qm)); err != nil {
return 0, err
}
w.Append(args...) w.Append(args...)
if len(exprs.args) > 0 {
if _, err := w.WriteString(","); err != nil {
return 0, err
}
if err := exprs.writeArgs(w); err != nil {
return 0, err
}
}
if _, err := w.WriteString(")"); err != nil {
return 0, err
}
} }
sql := w.String() sql := w.String()

View file

@ -239,14 +239,20 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6
for i, colName := range exprColumns.colNames { for i, colName := range exprColumns.colNames {
switch tp := exprColumns.args[i].(type) { switch tp := exprColumns.args[i].(type) {
case string: case string:
colNames = append(colNames, session.engine.Quote(colName)+" = "+tp) if len(tp) == 0 {
tp = "''"
}
colNames = append(colNames, session.engine.Quote(colName)+"="+tp)
case *builder.Builder: case *builder.Builder:
subQuery, subArgs, err := builder.ToSQL(tp) subQuery, subArgs, err := builder.ToSQL(tp)
if err != nil { if err != nil {
return 0, err return 0, err
} }
colNames = append(colNames, session.engine.Quote(colName)+" = ("+subQuery+")") colNames = append(colNames, session.engine.Quote(colName)+"=("+subQuery+")")
args = append(args, subArgs...) args = append(args, subArgs...)
default:
colNames = append(colNames, session.engine.Quote(colName)+"=?")
args = append(args, exprColumns.args[i])
} }
} }

View file

@ -69,10 +69,18 @@ func (exprs *exprParams) writeArgs(w *builder.BytesWriter) error {
if _, err := w.WriteString(")"); err != nil { if _, err := w.WriteString(")"); err != nil {
return err return err
} }
default: case string:
if arg == "" {
arg = "''"
}
if _, err := w.WriteString(fmt.Sprintf("%v", arg)); err != nil { if _, err := w.WriteString(fmt.Sprintf("%v", arg)); err != nil {
return err return err
} }
default:
if _, err := w.WriteString("?"); err != nil {
return err
}
w.Append(arg)
} }
if i != len(exprs.args)-1 { if i != len(exprs.args)-1 {
if _, err := w.WriteString(","); err != nil { if _, err := w.WriteString(","); err != nil {