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>
tokarchuk/v1.17
guillep2k 5 years ago committed by Antoine GIRARD
parent 6d6f1d568e
commit ad1b6d439f
  1. 2
      Makefile
  2. 4
      custom/conf/app.ini.sample
  3. 3
      docs/content/doc/advanced/config-cheat-sheet.en-us.md
  4. 4
      go.mod
  5. 6
      go.sum
  6. 45
      integrations/integration_test.go
  7. BIN
      integrations/migration-test/gitea-v1.5.3.postgres.sql.gz
  8. BIN
      integrations/migration-test/gitea-v1.6.4.postgres.sql.gz
  9. BIN
      integrations/migration-test/gitea-v1.7.0.postgres.sql.gz
  10. 26
      integrations/migration-test/migration_test.go
  11. 1
      integrations/pgsql.ini.tmpl
  12. 7
      models/models.go
  13. 1
      modules/auth/user_form.go
  14. 2
      modules/setting/database.go
  15. 3
      options/locale/locale_en-US.ini
  16. 3
      routers/install.go
  17. 2
      templates/admin/config.tmpl
  18. 5
      templates/install.tmpl
  19. 4
      vendor/modules.txt
  20. 120
      vendor/xorm.io/core/.drone.yml
  21. 2
      vendor/xorm.io/core/README.md
  22. 4
      vendor/xorm.io/core/column.go
  23. 4
      vendor/xorm.io/core/index.go
  24. 216
      vendor/xorm.io/xorm/.drone.yml
  25. 6
      vendor/xorm.io/xorm/dialect_postgres.go
  26. 94
      vendor/xorm.io/xorm/session_insert.go
  27. 10
      vendor/xorm.io/xorm/session_update.go
  28. 10
      vendor/xorm.io/xorm/statement_exprparam.go

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

@ -336,6 +336,10 @@ NAME = gitea
USER = root
; Use PASSWD = `your password` for quoting if you use special characters in the password.
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 MySQL, either "false" (default), "true", or "skip-verify"
SSL_MODE = disable

@ -209,6 +209,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `NAME`: **gitea**: Database name.
- `USER`: **root**: Database username.
- `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.
- `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.

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

@ -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/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
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/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a h1:hzGd080rlkZ5a7v6Tr3x8PJJnWPfKxGMMl92c8DNcww=
xorm.io/xorm v0.8.2-0.20200120024500-c37aff9b3a4a/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=

@ -153,18 +153,53 @@ func initIntegrationTest() {
if err != nil {
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 {
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
}
if _, err = db.Exec(fmt.Sprintf("CREATE DATABASE %s", setting.Database.Name)); err != nil {
log.Fatalf("db.Exec: %v", err)
db.Close()
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:
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;",

@ -168,6 +168,32 @@ func restoreOldDB(t *testing.T, version string) bool {
assert.NoError(t, err)
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",
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
assert.NoError(t, err)

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

@ -128,7 +128,12 @@ func getEngine() (*xorm.Engine, error) {
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

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

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

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

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

@ -128,6 +128,8 @@
<dd>{{if .DbCfg.User}}{{.DbCfg.User}}{{else}}-{{end}}</dd>
{{end}}
{{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>
<dd>{{if .DbCfg.SSLMode}}{{.DbCfg.SSLMode}}{{else}}-{{end}}</dd>
{{end}}

@ -62,6 +62,11 @@
</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 id="mysql_settings" class="{{if not (eq .CurDbOption "MySQL")}}hide{{end}}">

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

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

@ -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
name: go1.12
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

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

@ -1,7 +1,7 @@
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)
[![](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)
# Open

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

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

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

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

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

@ -1,204 +1,14 @@
---
kind: pipeline
name: go1.10-test
workspace:
base: /go
path: src/gitea.com/xorm/xorm
steps:
- name: build
pull: default
image: golang:1.10
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
name: testing
steps:
- name: build
- name: test-vet
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
commands:
- go build -v
- go vet
when:
event:
@ -207,7 +17,7 @@ steps:
- name: test-sqlite
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
@ -221,7 +31,7 @@ steps:
- name: test-mysql
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
@ -235,7 +45,7 @@ steps:
- name: test-mysql-utf8mb4
pull: default
image: golang:1.13
image: golang:1.12
depends_on:
- test-mysql
environment:
@ -251,7 +61,7 @@ steps:
- name: test-mymysql
pull: default
image: golang:1.13
image: golang:1.12
depends_on:
- test-mysql-utf8mb4
environment:
@ -267,7 +77,7 @@ steps:
- name: test-postgres
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
@ -281,7 +91,7 @@ steps:
- name: test-postgres-schema
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
@ -295,7 +105,7 @@ steps:
- name: test-mssql
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
@ -309,7 +119,7 @@ steps:
- name: test-tidb
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
@ -323,12 +133,12 @@ steps:
- name: merge_coverage
pull: default
image: golang:1.13
image: golang:1.12
environment:
GO111MODULE: "on"
GOPROXY: "https://goproxy.cn"
depends_on:
- build
- test-vet
- test-sqlite
- test-mysql
- test-mysql-utf8mb4

@ -901,7 +901,7 @@ func (db *postgres) TableCheckSql(tableName string) (string, []interface{}) {
}
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",
tableName, col.Name, db.SqlType(col))
}
@ -913,8 +913,8 @@ func (db *postgres) DropIndexSql(tableName string, index *core.Index) string {
quote := db.Quote
idxName := index.Name
tableName = strings.Replace(tableName, `"`, "", -1)
tableName = strings.Replace(tableName, `.`, "_", -1)
tableParts := strings.Split(strings.Replace(tableName, `"`, "", -1), ".")
tableName = tableParts[len(tableParts)-1]
if !strings.HasPrefix(idxName, "UQE_") &&
!strings.HasPrefix(idxName, "IDX_") {

@ -729,66 +729,7 @@ func (session *Session) insertMapInterface(m map[string]interface{}) (int64, err
args = append(args, m[colName])
}
w := builder.NewWriter()
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
return session.insertMap(columns, args)
}
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)
}
}
sort.Strings(columns)
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])
}
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()
// if insert where
if session.statement.cond.IsValid() {
if _, err := w.WriteString(fmt.Sprintf("INSERT INTO %s (", session.engine.Quote(tableName))); err != nil {
return 0, err
@ -853,10 +806,29 @@ func (session *Session) insertMapString(m map[string]string) (int64, error) {
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 {
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(fmt.Sprintf(") VALUES (%s", qm)); err != nil {
return 0, err
}
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()

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

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

Loading…
Cancel
Save